@vidtreo/recorder 1.8.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +252 -13
- package/dist/index.js +2987 -354
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -102,6 +102,10 @@ export {};
|
|
|
102
102
|
|
|
103
103
|
export {};
|
|
104
104
|
|
|
105
|
+
export {};
|
|
106
|
+
|
|
107
|
+
export {};
|
|
108
|
+
|
|
105
109
|
export type PacketFingerprint = {
|
|
106
110
|
codec: string | null;
|
|
107
111
|
duration: number;
|
|
@@ -263,6 +267,14 @@ export {};
|
|
|
263
267
|
|
|
264
268
|
export {};
|
|
265
269
|
|
|
270
|
+
export {};
|
|
271
|
+
|
|
272
|
+
export {};
|
|
273
|
+
|
|
274
|
+
export {};
|
|
275
|
+
|
|
276
|
+
export {};
|
|
277
|
+
|
|
266
278
|
export type VidtreoRecorderConfig = {
|
|
267
279
|
apiKey?: string;
|
|
268
280
|
getAccessToken?: AccessTokenProvider;
|
|
@@ -298,6 +310,8 @@ export type RecordingStopResult = {
|
|
|
298
310
|
recordingId: string;
|
|
299
311
|
uploadUrl: string;
|
|
300
312
|
blob: Blob;
|
|
313
|
+
mediaType: MediaType;
|
|
314
|
+
audioId?: string;
|
|
301
315
|
};
|
|
302
316
|
export declare class VidtreoRecorder {
|
|
303
317
|
private readonly controller;
|
|
@@ -313,6 +327,9 @@ export declare class VidtreoRecorder {
|
|
|
313
327
|
setPictureInPicture(update: PictureInPictureConfigInput): void;
|
|
314
328
|
getPictureInPictureConfig(): ResolvedPictureInPictureConfig;
|
|
315
329
|
stopRecording(): Promise<RecordingStopResult>;
|
|
330
|
+
getAudioRecordingConfig(): Promise<AudioRecordingConfig>;
|
|
331
|
+
uploadAudio(audio: TranscodedAudio): Promise<RecordingStopResult>;
|
|
332
|
+
private uploadMedia;
|
|
316
333
|
getAvailableDevices(): Promise<import(".").AvailableDevices>;
|
|
317
334
|
muteAudio(): void;
|
|
318
335
|
unmuteAudio(): void;
|
|
@@ -329,6 +346,132 @@ export declare class VidtreoRecorder {
|
|
|
329
346
|
private ensureInitialized;
|
|
330
347
|
}
|
|
331
348
|
|
|
349
|
+
export declare const ERROR_AUDIO_RECORDING_DISABLED = "recording.audio-disabled";
|
|
350
|
+
export declare const ERROR_AUDIO_SOURCE_SWITCH_UNSUPPORTED = "recording.audio-source-switch-unsupported";
|
|
351
|
+
export declare const ERROR_AUDIO_UNSUPPORTED_INPUT = "audio.unsupported-input";
|
|
352
|
+
export declare const ERROR_AUDIO_NO_AUDIO_TRACK = "audio.no-audio-track";
|
|
353
|
+
export declare const ERROR_AUDIO_CODEC_UNSUPPORTED = "audio.codec-unsupported";
|
|
354
|
+
export type AudioRecordingErrorCode = typeof ERROR_AUDIO_RECORDING_DISABLED | typeof ERROR_AUDIO_SOURCE_SWITCH_UNSUPPORTED | typeof ERROR_AUDIO_UNSUPPORTED_INPUT | typeof ERROR_AUDIO_NO_AUDIO_TRACK | typeof ERROR_AUDIO_CODEC_UNSUPPORTED;
|
|
355
|
+
export type AudioRecordingError = Error & {
|
|
356
|
+
code: AudioRecordingErrorCode;
|
|
357
|
+
};
|
|
358
|
+
export declare function createAudioRecordingError(code: AudioRecordingErrorCode, message: string): AudioRecordingError;
|
|
359
|
+
|
|
360
|
+
export declare const AUDIO_OUTPUT_FORMATS: Readonly<Record<AudioFormat, AudioOutputFormat>>;
|
|
361
|
+
export type ResolveAudioOutputFormatOptions = {
|
|
362
|
+
isLinuxPlatform: boolean;
|
|
363
|
+
canEncode: (format: AudioFormat) => Promise<boolean>;
|
|
364
|
+
};
|
|
365
|
+
export declare function getAudioFormatCandidates(requestedFormat: AudioFormat, isLinuxPlatform: boolean): AudioFormat[];
|
|
366
|
+
export declare function resolveAudioOutputFormat(requestedFormat: AudioFormat, options: ResolveAudioOutputFormatOptions): Promise<AudioOutputFormat>;
|
|
367
|
+
|
|
368
|
+
export type AudioNormalizerDependencies = {
|
|
369
|
+
createAudioContext: () => AudioContext | null;
|
|
370
|
+
};
|
|
371
|
+
export declare class AudioNormalizer {
|
|
372
|
+
private audioContext;
|
|
373
|
+
private source;
|
|
374
|
+
private destination;
|
|
375
|
+
private readonly dependencies;
|
|
376
|
+
constructor(dependencies?: Partial<AudioNormalizerDependencies>);
|
|
377
|
+
start(track: MediaStreamTrack): MediaStreamTrack | null;
|
|
378
|
+
close(): void;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export declare const MAX_AUDIO_PEAK_POINTS = 1000;
|
|
382
|
+
export declare class AudioPeaksAccumulator {
|
|
383
|
+
private readonly windowPeaks;
|
|
384
|
+
private windowFrames;
|
|
385
|
+
private currentWindowPeak;
|
|
386
|
+
private currentWindowFrameCount;
|
|
387
|
+
addPlanarSamples(data: Float32Array, frames: number, numberOfChannels: number, sampleRate: number): void;
|
|
388
|
+
toPeaks(): AudioPeaks;
|
|
389
|
+
private flushWindow;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export type MediaType = "video" | "audio";
|
|
393
|
+
export type AudioFormat = "aac" | "opus";
|
|
394
|
+
export type AudioBitrate = 64000 | 96000 | 128000 | 192000;
|
|
395
|
+
export type AudioChannels = 1 | 2;
|
|
396
|
+
export type AudioRecordingConfig = {
|
|
397
|
+
enabled: boolean;
|
|
398
|
+
format: AudioFormat;
|
|
399
|
+
bitrate: AudioBitrate;
|
|
400
|
+
channels: AudioChannels;
|
|
401
|
+
echoCancellation: boolean;
|
|
402
|
+
noiseSuppression: boolean;
|
|
403
|
+
autoGain: boolean;
|
|
404
|
+
normalize: boolean;
|
|
405
|
+
};
|
|
406
|
+
export type AudioEncodingConfig = Pick<AudioRecordingConfig, "format" | "bitrate" | "channels">;
|
|
407
|
+
export type AudioContainer = "mp4" | "webm";
|
|
408
|
+
export type AudioOutputFormat = {
|
|
409
|
+
format: AudioFormat;
|
|
410
|
+
container: AudioContainer;
|
|
411
|
+
mimeType: "audio/mp4" | "audio/webm";
|
|
412
|
+
extension: "m4a" | "webm";
|
|
413
|
+
};
|
|
414
|
+
export type AudioPeaks = {
|
|
415
|
+
version: 1;
|
|
416
|
+
length: number;
|
|
417
|
+
data: number[];
|
|
418
|
+
};
|
|
419
|
+
export type AudioUploadDetails = {
|
|
420
|
+
format: AudioFormat;
|
|
421
|
+
bitrate: number;
|
|
422
|
+
channels: number;
|
|
423
|
+
sampleRate: number;
|
|
424
|
+
peaks: AudioPeaks | null;
|
|
425
|
+
};
|
|
426
|
+
export type EncodedAudio = {
|
|
427
|
+
blob: Blob;
|
|
428
|
+
outputFormat: AudioOutputFormat;
|
|
429
|
+
bitrate: number;
|
|
430
|
+
peaks: AudioPeaks | null;
|
|
431
|
+
};
|
|
432
|
+
export type AudioRecording = {
|
|
433
|
+
blob: Blob;
|
|
434
|
+
mimeType: string;
|
|
435
|
+
extension: string;
|
|
436
|
+
duration: number;
|
|
437
|
+
peaks: AudioPeaks | null;
|
|
438
|
+
format: AudioFormat;
|
|
439
|
+
bitrate: number;
|
|
440
|
+
channels: number;
|
|
441
|
+
sampleRate: number;
|
|
442
|
+
};
|
|
443
|
+
export type TranscodedAudio = {
|
|
444
|
+
blob: Blob;
|
|
445
|
+
mimeType: string;
|
|
446
|
+
filename: string;
|
|
447
|
+
duration: number;
|
|
448
|
+
peaks: AudioPeaks;
|
|
449
|
+
format: AudioFormat;
|
|
450
|
+
bitrate: number;
|
|
451
|
+
channels: number;
|
|
452
|
+
sampleRate: number;
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
export declare const DISABLED_AUDIO_RECORDING_CONFIG: Readonly<AudioRecordingConfig>;
|
|
456
|
+
export declare function parseAudioRecordingConfig(value: unknown): AudioRecordingConfig;
|
|
457
|
+
export declare function requireEnabledAudioRecordingConfig(config: AudioRecordingConfig | undefined): AudioRecordingConfig;
|
|
458
|
+
|
|
459
|
+
export type AudioTranscodeOptions = {
|
|
460
|
+
onProgress?: (progress: number) => void;
|
|
461
|
+
signal?: AbortSignal;
|
|
462
|
+
};
|
|
463
|
+
type AudioMetadata = {
|
|
464
|
+
duration: number;
|
|
465
|
+
channels: number;
|
|
466
|
+
sampleRate: number;
|
|
467
|
+
};
|
|
468
|
+
export declare function readAudioMetadata(blob: Blob): Promise<AudioMetadata>;
|
|
469
|
+
export declare function computeAudioPeaksFromBlob(blob: Blob): Promise<AudioPeaks | null>;
|
|
470
|
+
export declare function createAudioRecording(encoded: EncodedAudio): Promise<AudioRecording>;
|
|
471
|
+
export declare function convertRecordedAudio(blob: Blob, config: AudioEncodingConfig, options?: AudioTranscodeOptions): Promise<AudioRecording>;
|
|
472
|
+
export declare function transcodeAudioFile(file: File | Blob, config: AudioEncodingConfig, options?: AudioTranscodeOptions): Promise<TranscodedAudio>;
|
|
473
|
+
export {};
|
|
474
|
+
|
|
332
475
|
import type { ConfigService } from "../config/config-service";
|
|
333
476
|
import { VideoStorageService } from "../storage/video-storage";
|
|
334
477
|
import { type RecordingUploadResult } from "../upload/recording-upload-result";
|
|
@@ -742,6 +885,7 @@ export type UploadCallbacks = {
|
|
|
742
885
|
onUploadError: (id: string, error: Error) => void;
|
|
743
886
|
};
|
|
744
887
|
|
|
888
|
+
import type { AudioUploadDetails, MediaType } from "../audio-recording/types";
|
|
745
889
|
export type PendingUpload = {
|
|
746
890
|
id: string;
|
|
747
891
|
blob: Blob;
|
|
@@ -752,12 +896,17 @@ export type PendingUpload = {
|
|
|
752
896
|
duration?: number;
|
|
753
897
|
metadata?: Record<string, unknown>;
|
|
754
898
|
userMetadata?: Record<string, unknown>;
|
|
899
|
+
mediaType: MediaType;
|
|
900
|
+
audio?: AudioUploadDetails;
|
|
755
901
|
status: "pending" | "uploading" | "failed" | "completed";
|
|
756
902
|
retryCount: number;
|
|
757
903
|
lastError?: string;
|
|
758
904
|
createdAt: number;
|
|
759
905
|
updatedAt: number;
|
|
760
906
|
};
|
|
907
|
+
export type NewPendingUpload = Omit<PendingUpload, "id" | "createdAt" | "updatedAt" | "status" | "retryCount" | "mediaType"> & {
|
|
908
|
+
mediaType?: MediaType;
|
|
909
|
+
};
|
|
761
910
|
export type StorageWriteProbeResult = {
|
|
762
911
|
ok: true;
|
|
763
912
|
} | {
|
|
@@ -775,7 +924,7 @@ export declare class VideoStorageService {
|
|
|
775
924
|
private validateRequiredSchema;
|
|
776
925
|
isInitialized(): boolean;
|
|
777
926
|
probeWriteCapability(): Promise<StorageWriteProbeResult>;
|
|
778
|
-
savePendingUpload(upload:
|
|
927
|
+
savePendingUpload(upload: NewPendingUpload): Promise<string>;
|
|
779
928
|
getPendingUploads(status?: PendingUpload["status"]): Promise<PendingUpload[]>;
|
|
780
929
|
updateUploadStatus(id: string, updates: Partial<PendingUpload>): Promise<void>;
|
|
781
930
|
deleteUpload(id: string): Promise<void>;
|
|
@@ -907,11 +1056,13 @@ export type PermissionFlowOrchestratorDependencies = {
|
|
|
907
1056
|
createPermissionRecoveryData: (errorCode: DeviceIssueCode, browserInfo: PermissionFlowBrowserInfo, language?: PermissionFlowLanguage) => PermissionRecoveryData;
|
|
908
1057
|
getBrowserInfo: () => PermissionFlowBrowserInfo;
|
|
909
1058
|
getIsSecureContext: () => boolean;
|
|
910
|
-
requestPermissionProbe: () => Promise<void>;
|
|
1059
|
+
requestPermissionProbe: (mediaType?: PermissionFlowMediaType) => Promise<void>;
|
|
911
1060
|
};
|
|
1061
|
+
export type PermissionFlowMediaType = "video" | "audio";
|
|
912
1062
|
export type PermissionFlowOrchestratorOptions = {
|
|
913
1063
|
callbacks?: PermissionFlowCallbacks;
|
|
914
1064
|
language?: PermissionFlowLanguage;
|
|
1065
|
+
mediaType?: PermissionFlowMediaType;
|
|
915
1066
|
};
|
|
916
1067
|
|
|
917
1068
|
export declare function createPermissionFlowOrchestratorDependencies(): PermissionFlowOrchestratorDependencies;
|
|
@@ -919,6 +1070,8 @@ export declare class PermissionFlowOrchestrator {
|
|
|
919
1070
|
private readonly dependencies;
|
|
920
1071
|
private readonly callbacks;
|
|
921
1072
|
private readonly language;
|
|
1073
|
+
private readonly mediaType;
|
|
1074
|
+
private readonly permissionNames;
|
|
922
1075
|
private currentState;
|
|
923
1076
|
private cleanupCameraListener;
|
|
924
1077
|
private cleanupMicrophoneListener;
|
|
@@ -988,6 +1141,7 @@ export type BackendConfigResponse = {
|
|
|
988
1141
|
opacity?: number;
|
|
989
1142
|
position: string;
|
|
990
1143
|
};
|
|
1144
|
+
audio?: unknown;
|
|
991
1145
|
};
|
|
992
1146
|
export type FormatCompatibilityPolicy = {
|
|
993
1147
|
preferredVideoCodec: NonNullable<TranscodeConfig["codec"]>;
|
|
@@ -1103,12 +1257,14 @@ export declare class PictureInPictureCompositor implements PictureInPictureCompo
|
|
|
1103
1257
|
private cloneCamera;
|
|
1104
1258
|
}
|
|
1105
1259
|
|
|
1260
|
+
import type { AudioRecordingConfig } from "../audio-recording/types";
|
|
1106
1261
|
export type ConstraintsBuilderDependencies = {
|
|
1107
1262
|
originalCameraConstraints: MediaTrackConstraints | null;
|
|
1108
1263
|
getSelectedCameraDeviceId: () => string | null;
|
|
1109
1264
|
};
|
|
1110
1265
|
export declare function buildVideoConstraints(cameraDeviceId: string | null, dependencies: ConstraintsBuilderDependencies): MediaTrackConstraints;
|
|
1111
1266
|
export declare function buildAudioConstraints(micDeviceId: string | null): MediaTrackConstraints;
|
|
1267
|
+
export declare function buildAudioRecordingConstraints(config: AudioRecordingConfig, micDeviceId: string | null): MediaTrackConstraints;
|
|
1112
1268
|
|
|
1113
1269
|
import type { SourceType } from "../recording/types";
|
|
1114
1270
|
export declare const ERROR_SCREEN_SELECTION_CANCELLED = "screen.selection-cancelled";
|
|
@@ -1189,6 +1345,7 @@ export declare class SourceSwitchManager {
|
|
|
1189
1345
|
getLiveCameraStream(): MediaStream | null;
|
|
1190
1346
|
switchTo(sourceType: SourceType): Promise<void>;
|
|
1191
1347
|
private resolveSwitch;
|
|
1348
|
+
startAudioOnly(audioConstraints: MediaTrackConstraints): Promise<MediaStream>;
|
|
1192
1349
|
startScreenOnly(): Promise<MediaStream>;
|
|
1193
1350
|
switchCompositeCamera(): Promise<MediaStream>;
|
|
1194
1351
|
handleStreamStop(): void;
|
|
@@ -1233,6 +1390,8 @@ export declare function handleScreenSelectionError(callbacks: TransitionCallback
|
|
|
1233
1390
|
export declare const pictureInPictureWorkerCode = "(() => {\n // src/core/utils/error-handler.ts\n function extractErrorMessage(error) {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n }\n\n // src/core/stream/picture-in-picture-layout.ts\n var PICTURE_IN_PICTURE_TRANSITION_MILLISECONDS = 120;\n var MARGIN_RATIO = 0.03;\n var BORDER_RATIO = 0.02;\n var SHADOW_BLUR_RATIO = 0.06;\n var SHADOW_OFFSET_RATIO = 0.015;\n var ROUNDED_CORNER_RATIO = 0.2;\n var DEFAULT_MAX_LONG_EDGE = 1920;\n var DEFAULT_MAX_SHORT_EDGE = 1080;\n function clamp(value, min, max) {\n return Math.min(Math.max(value, min), max);\n }\n function calculateBubbleRect(frameWidth, frameHeight, config) {\n const position = config.position;\n if (typeof position !== \"string\") {\n const diameter = Math.min(Math.round(frameHeight * config.size), Math.max(0, Math.min(frameWidth, frameHeight)));\n return {\n x: clamp(Math.round(position.x * frameWidth), 0, frameWidth - diameter),\n y: clamp(Math.round(position.y * frameHeight), 0, frameHeight - diameter),\n width: diameter,\n height: diameter\n };\n }\n const margin = Math.round(frameHeight * MARGIN_RATIO);\n const maxDiameter = Math.max(0, Math.min(frameWidth, frameHeight) - margin * 2);\n const diameter = Math.min(Math.round(frameHeight * config.size), maxDiameter);\n let x = margin;\n if (position.endsWith(\"right\")) {\n x = frameWidth - margin - diameter;\n }\n let y = margin;\n if (position.startsWith(\"bottom\")) {\n y = frameHeight - margin - diameter;\n }\n return { x, y, width: diameter, height: diameter };\n }\n function calculateBubbleStyle(bubble, shape) {\n let cornerRadius = bubble.width / 2;\n if (shape === \"rounded\") {\n cornerRadius = bubble.width * ROUNDED_CORNER_RATIO;\n }\n return {\n borderWidth: Math.max(1, Math.round(bubble.width * BORDER_RATIO)),\n shadowBlur: Math.round(bubble.width * SHADOW_BLUR_RATIO),\n shadowOffsetY: Math.round(bubble.width * SHADOW_OFFSET_RATIO),\n cornerRadius\n };\n }\n function calculateCoverCrop(sourceWidth, sourceHeight, targetWidth, targetHeight) {\n if (sourceWidth <= 0 || sourceHeight <= 0 || targetWidth <= 0 || targetHeight <= 0) {\n return { x: 0, y: 0, width: sourceWidth, height: sourceHeight };\n }\n const sourceAspect = sourceWidth / sourceHeight;\n const targetAspect = targetWidth / targetHeight;\n if (sourceAspect > targetAspect) {\n const width = sourceHeight * targetAspect;\n return {\n x: (sourceWidth - width) / 2,\n y: 0,\n width,\n height: sourceHeight\n };\n }\n const height = sourceWidth / targetAspect;\n return {\n x: 0,\n y: (sourceHeight - height) / 2,\n width: sourceWidth,\n height\n };\n }\n function toEven(value) {\n return Math.max(2, Math.floor(value / 2) * 2);\n }\n function calculateCompositeSize(sourceWidth, sourceHeight, maxWidth, maxHeight) {\n let maxLongEdge = DEFAULT_MAX_LONG_EDGE;\n let maxShortEdge = DEFAULT_MAX_SHORT_EDGE;\n if (typeof maxWidth === \"number\" && typeof maxHeight === \"number\" && maxWidth > 0 && maxHeight > 0) {\n maxLongEdge = Math.max(maxWidth, maxHeight);\n maxShortEdge = Math.min(maxWidth, maxHeight);\n }\n const longEdge = Math.max(sourceWidth, sourceHeight);\n const shortEdge = Math.min(sourceWidth, sourceHeight);\n const scale = Math.min(1, maxLongEdge / longEdge, maxShortEdge / shortEdge);\n return {\n width: toEven(sourceWidth * scale),\n height: toEven(sourceHeight * scale)\n };\n }\n function easeOutCubic(progress) {\n return 1 - (1 - progress) ** 3;\n }\n function calculateTransitionRect(from, to, elapsedMilliseconds, durationMilliseconds) {\n if (durationMilliseconds <= 0 || elapsedMilliseconds >= durationMilliseconds) {\n return { ...to };\n }\n const eased = easeOutCubic(clamp(elapsedMilliseconds / durationMilliseconds, 0, 1));\n return {\n x: from.x + (to.x - from.x) * eased,\n y: from.y + (to.y - from.y) * eased,\n width: from.width + (to.width - from.width) * eased,\n height: from.height + (to.height - from.height) * eased\n };\n }\n\n // src/core/stream/picture-in-picture-worker.ts\n var MILLISECONDS_PER_SECOND = 1000;\n var MICROSECONDS_PER_MILLISECOND = 1000;\n var FRAME_INTERVAL_TOLERANCE_MILLISECONDS = 2;\n var BORDER_COLOR = \"rgba(255, 255, 255, 0.92)\";\n var SHADOW_COLOR = \"rgba(0, 0, 0, 0.35)\";\n var state = null;\n function respond(response) {\n self.postMessage(response);\n }\n function readFrames(reader, onFrame) {\n return reader.read().then((result) => {\n if (result.done) {\n return;\n }\n onFrame(result.value);\n return readFrames(reader, onFrame);\n });\n }\n function ensureContext(current, width, height) {\n const canvas = current.canvas;\n if (canvas && current.context && canvas.width === width && canvas.height === height) {\n return current.context;\n }\n const nextCanvas = new OffscreenCanvas(width, height);\n const context = nextCanvas.getContext(\"2d\", { alpha: false });\n if (!context) {\n throw new Error(\"Failed to get picture-in-picture canvas context\");\n }\n current.canvas = nextCanvas;\n current.context = context;\n return context;\n }\n function traceShape(context, rect, shape, cornerRadius) {\n context.beginPath();\n if (shape === \"circle\") {\n context.arc(rect.x + rect.width / 2, rect.y + rect.height / 2, rect.width / 2, 0, Math.PI * 2);\n return;\n }\n context.roundRect(rect.x, rect.y, rect.width, rect.height, cornerRadius);\n }\n function drawBubble(context, current, cameraFrame, width, height, now) {\n const bubble = resolveBubbleRect(current, width, height, now);\n if (bubble.width <= 0) {\n return;\n }\n const style = calculateBubbleStyle(bubble, current.layout.shape);\n context.save();\n context.shadowColor = SHADOW_COLOR;\n context.shadowBlur = style.shadowBlur;\n context.shadowOffsetY = style.shadowOffsetY;\n context.fillStyle = BORDER_COLOR;\n traceShape(context, bubble, current.layout.shape, style.cornerRadius);\n context.fill();\n context.restore();\n const inner = {\n x: bubble.x + style.borderWidth,\n y: bubble.y + style.borderWidth,\n width: bubble.width - style.borderWidth * 2,\n height: bubble.height - style.borderWidth * 2\n };\n const innerRadius = Math.max(0, style.cornerRadius - style.borderWidth);\n const crop = calculateCoverCrop(cameraFrame.displayWidth, cameraFrame.displayHeight, inner.width, inner.height);\n context.save();\n traceShape(context, inner, current.layout.shape, innerRadius);\n context.clip();\n context.drawImage(cameraFrame, crop.x, crop.y, crop.width, crop.height, inner.x, inner.y, inner.width, inner.height);\n context.restore();\n }\n function resolveBubbleRect(current, width, height, now) {\n const target = calculateBubbleRect(width, height, current.layout);\n const transition = current.transition;\n let bubble = target;\n if (transition) {\n const elapsed = now - transition.startedAtMilliseconds;\n bubble = calculateTransitionRect(transition.from, target, elapsed, PICTURE_IN_PICTURE_TRANSITION_MILLISECONDS);\n if (elapsed >= PICTURE_IN_PICTURE_TRANSITION_MILLISECONDS) {\n current.transition = null;\n }\n }\n current.bubbleRect = bubble;\n return bubble;\n }\n function applyLayout(current, message) {\n current.layout = message.layout;\n if (!(message.shouldAnimate && current.bubbleRect)) {\n current.transition = null;\n return;\n }\n current.transition = {\n from: current.bubbleRect,\n startedAtMilliseconds: performance.now()\n };\n }\n function nextTimestamp(current, now) {\n const timestamp = Math.max(Math.round(now * MICROSECONDS_PER_MILLISECOND), current.lastTimestampMicroseconds + 1);\n current.lastTimestampMicroseconds = timestamp;\n return timestamp;\n }\n function compose(current) {\n const screenFrame = current.screenFrame;\n if (!(current.isRunning && screenFrame)) {\n return;\n }\n const now = performance.now();\n const elapsed = now - current.lastOutputMilliseconds;\n if (elapsed + FRAME_INTERVAL_TOLERANCE_MILLISECONDS < current.minFrameIntervalMilliseconds) {\n return;\n }\n const desiredSize = current.writer.desiredSize;\n if (desiredSize !== null && desiredSize <= 0) {\n return;\n }\n const { width, height } = calculateCompositeSize(screenFrame.displayWidth, screenFrame.displayHeight, current.maxWidth, current.maxHeight);\n const context = ensureContext(current, width, height);\n context.drawImage(screenFrame, 0, 0, width, height);\n if (current.cameraFrame) {\n drawBubble(context, current, current.cameraFrame, width, height, now);\n }\n const canvas = current.canvas;\n if (!canvas) {\n return;\n }\n current.lastOutputMilliseconds = now;\n const outputFrame = new VideoFrame(canvas, {\n timestamp: nextTimestamp(current, now)\n });\n current.writer.write(outputFrame).catch((error) => {\n outputFrame.close();\n reportError(error);\n });\n }\n function reportError(error) {\n if (!state?.isRunning) {\n return;\n }\n stop();\n respond({ type: \"error\", message: extractErrorMessage(error) });\n }\n function attachCamera(current, camera) {\n current.cameraGeneration += 1;\n const generation = current.cameraGeneration;\n if (current.cameraReader) {\n current.cameraReader.cancel().catch(() => {\n return;\n });\n }\n current.cameraReader = null;\n if (current.cameraFrame) {\n current.cameraFrame.close();\n current.cameraFrame = null;\n }\n if (!camera) {\n return;\n }\n const reader = camera.getReader();\n current.cameraReader = reader;\n readFrames(reader, (frame) => {\n if (!current.isRunning || current.cameraGeneration !== generation) {\n frame.close();\n return;\n }\n if (current.cameraFrame) {\n current.cameraFrame.close();\n }\n current.cameraFrame = frame;\n compose(current);\n }).then(() => {\n if (current.cameraGeneration !== generation || !current.cameraFrame) {\n return;\n }\n current.cameraFrame.close();\n current.cameraFrame = null;\n }).catch(reportError);\n }\n function start(message) {\n stop();\n const current = {\n layout: message.layout,\n bubbleRect: null,\n transition: null,\n maxWidth: message.maxWidth,\n maxHeight: message.maxHeight,\n minFrameIntervalMilliseconds: MILLISECONDS_PER_SECOND / message.maxFrameRate,\n writer: message.output.getWriter(),\n canvas: null,\n context: null,\n screenFrame: null,\n cameraFrame: null,\n screenReader: message.screen.getReader(),\n cameraReader: null,\n cameraGeneration: 0,\n lastOutputMilliseconds: Number.NEGATIVE_INFINITY,\n lastTimestampMicroseconds: 0,\n isRunning: true\n };\n state = current;\n attachCamera(current, message.camera);\n readFrames(current.screenReader, (frame) => {\n if (!current.isRunning) {\n frame.close();\n return;\n }\n if (current.screenFrame) {\n current.screenFrame.close();\n }\n current.screenFrame = frame;\n compose(current);\n }).catch(reportError);\n }\n function stop() {\n const current = state;\n if (!current) {\n return;\n }\n state = null;\n current.isRunning = false;\n current.screenReader.cancel().catch(() => {\n return;\n });\n if (current.cameraReader) {\n current.cameraReader.cancel().catch(() => {\n return;\n });\n }\n if (current.screenFrame) {\n current.screenFrame.close();\n }\n if (current.cameraFrame) {\n current.cameraFrame.close();\n }\n current.writer.close().catch(() => {\n return;\n });\n }\n function handleMessage(message) {\n if (message.type === \"start\") {\n start(message);\n return;\n }\n if (message.type === \"stop\") {\n stop();\n return;\n }\n if (!state) {\n return;\n }\n if (message.type === \"camera\") {\n attachCamera(state, message.camera);\n return;\n }\n applyLayout(state, message);\n }\n self.addEventListener(\"message\", (event) => {\n handleMessage(event.data);\n });\n})();\n";
|
|
1234
1391
|
|
|
1235
1392
|
import { SystemAudioMixer } from "../audio/system-audio-mixer";
|
|
1393
|
+
import { AudioNormalizer } from "../audio-recording/audio-normalizer";
|
|
1394
|
+
import type { EncodedAudio } from "../audio-recording/types";
|
|
1236
1395
|
import { type SupportCheckOptions, type SupportReport } from "../processor/support-check";
|
|
1237
1396
|
import type { RecordingRouteDecision } from "../routing/recording-route-decision";
|
|
1238
1397
|
import type { TranscodeConfig } from "../transcode/transcode-types";
|
|
@@ -1245,6 +1404,7 @@ export type StopRecordingResult = {
|
|
|
1245
1404
|
recordingStats?: StreamRecordingStats;
|
|
1246
1405
|
encoderAcceleration?: VideoHardwareAcceleration;
|
|
1247
1406
|
mediaRecorderDiagnostics?: Record<string, unknown>;
|
|
1407
|
+
audio?: EncodedAudio;
|
|
1248
1408
|
};
|
|
1249
1409
|
type CheckRecorderSupportDependency = (options: SupportCheckOptions) => Promise<SupportReport>;
|
|
1250
1410
|
type GetCurrentTimestampDependency = () => number;
|
|
@@ -1252,6 +1412,7 @@ type StreamRecordingStateDependencies = {
|
|
|
1252
1412
|
checkRecorderSupport: CheckRecorderSupportDependency;
|
|
1253
1413
|
getCurrentTimestamp: GetCurrentTimestampDependency;
|
|
1254
1414
|
createAudioMixer: () => SystemAudioMixer;
|
|
1415
|
+
createAudioNormalizer: () => AudioNormalizer;
|
|
1255
1416
|
};
|
|
1256
1417
|
export declare class StreamRecordingState {
|
|
1257
1418
|
private recordingStartTime;
|
|
@@ -1282,6 +1443,8 @@ export declare class StreamRecordingState {
|
|
|
1282
1443
|
private isSystemAudioCaptureEnabled;
|
|
1283
1444
|
private systemAudioTrack;
|
|
1284
1445
|
private audioMixer;
|
|
1446
|
+
private audioNormalizer;
|
|
1447
|
+
private isAudioOnlyRecording;
|
|
1285
1448
|
private readonly streamManager;
|
|
1286
1449
|
private readonly dependencies;
|
|
1287
1450
|
constructor(streamManager: StreamManager, dependencies?: Partial<StreamRecordingStateDependencies>);
|
|
@@ -1303,6 +1466,8 @@ export declare class StreamRecordingState {
|
|
|
1303
1466
|
isMuted(): boolean;
|
|
1304
1467
|
switchVideoSource(newStream: MediaStream): Promise<void>;
|
|
1305
1468
|
private buildRecordingStream;
|
|
1469
|
+
private buildNormalizedStream;
|
|
1470
|
+
private releaseAudioNormalizer;
|
|
1306
1471
|
private releaseAudioMixer;
|
|
1307
1472
|
getCurrentVideoSource(): MediaStream;
|
|
1308
1473
|
private formatTimeElapsed;
|
|
@@ -1407,6 +1572,7 @@ export declare class StreamManager {
|
|
|
1407
1572
|
private buildAudioConstraints;
|
|
1408
1573
|
startStream(): Promise<MediaStream>;
|
|
1409
1574
|
startStreamWithVideoTrack(videoTrack: MediaStreamTrack): Promise<MediaStream>;
|
|
1575
|
+
startAudioOnlyStream(audioConstraints: MediaTrackConstraints): Promise<MediaStream>;
|
|
1410
1576
|
private acquireVideoAndAudioStream;
|
|
1411
1577
|
private acquireAudioInBackground;
|
|
1412
1578
|
private resolveAudioConstraintsForAttempt;
|
|
@@ -1418,7 +1584,7 @@ export declare class StreamManager {
|
|
|
1418
1584
|
private recreateStreamWithNewTrack;
|
|
1419
1585
|
private switchDeviceTrack;
|
|
1420
1586
|
switchVideoDevice(deviceId: string | null): Promise<MediaStream>;
|
|
1421
|
-
switchAudioDevice(deviceId: string | null): Promise<MediaStream>;
|
|
1587
|
+
switchAudioDevice(deviceId: string | null, audioConstraints?: MediaTrackConstraints | boolean): Promise<MediaStream>;
|
|
1422
1588
|
setMediaStream(stream: MediaStream): void;
|
|
1423
1589
|
setAudioTracksEnabled(enabled: boolean): void;
|
|
1424
1590
|
destroy(): void;
|
|
@@ -1434,6 +1600,7 @@ export declare function isTrackLive(track: MediaStreamTrack | undefined): boolea
|
|
|
1434
1600
|
export declare function areTracksLive(videoTrack: MediaStreamTrack | undefined, audioTrack: MediaStreamTrack | undefined): boolean;
|
|
1435
1601
|
export declare function validateTrack(track: MediaStreamTrack | undefined, trackType: string, stream: MediaStream): void;
|
|
1436
1602
|
|
|
1603
|
+
import type { EncodedAudio } from "../audio-recording/types";
|
|
1437
1604
|
export type CameraConstraints = {
|
|
1438
1605
|
width?: number | {
|
|
1439
1606
|
ideal?: number;
|
|
@@ -1517,6 +1684,7 @@ export type StreamProcessorResult = {
|
|
|
1517
1684
|
totalSize: number;
|
|
1518
1685
|
recordingStats?: StreamRecordingStats;
|
|
1519
1686
|
encoderAcceleration?: VideoHardwareAcceleration;
|
|
1687
|
+
audio?: EncodedAudio;
|
|
1520
1688
|
};
|
|
1521
1689
|
export type StreamRecordingStats = {
|
|
1522
1690
|
videoFrameCount: number;
|
|
@@ -1581,9 +1749,10 @@ export declare class CameraStreamManager {
|
|
|
1581
1749
|
}>;
|
|
1582
1750
|
startStream(): Promise<MediaStream>;
|
|
1583
1751
|
startStreamWithVideoTrack(videoTrack: MediaStreamTrack): Promise<MediaStream>;
|
|
1752
|
+
startAudioOnlyStream(audioConstraints: MediaTrackConstraints): Promise<MediaStream>;
|
|
1584
1753
|
stopStream(): void;
|
|
1585
1754
|
switchVideoDevice(deviceId: string | null): Promise<MediaStream>;
|
|
1586
|
-
switchAudioDevice(deviceId: string | null): Promise<MediaStream>;
|
|
1755
|
+
switchAudioDevice(deviceId: string | null, audioConstraints?: MediaTrackConstraints): Promise<MediaStream>;
|
|
1587
1756
|
setSystemAudioCaptureEnabled(isEnabled: boolean): void;
|
|
1588
1757
|
setSystemAudioTrack(track: MediaStreamTrack | null): void;
|
|
1589
1758
|
isMixingSystemAudio(): boolean;
|
|
@@ -1747,6 +1916,14 @@ export type DecideRecordingRouteOptions = {
|
|
|
1747
1916
|
};
|
|
1748
1917
|
};
|
|
1749
1918
|
export declare function decideRecordingRoute(options: DecideRecordingRouteOptions): RecordingRouteDecision;
|
|
1919
|
+
export type AudioRecordingCapabilities = {
|
|
1920
|
+
isSecureContext: boolean;
|
|
1921
|
+
hasAudioEncoder: boolean;
|
|
1922
|
+
hasAudioWorklet: boolean;
|
|
1923
|
+
hasMediaRecorder: boolean;
|
|
1924
|
+
};
|
|
1925
|
+
export declare function detectAudioRecordingCapabilities(): AudioRecordingCapabilities;
|
|
1926
|
+
export declare function decideAudioRecordingRoute(capabilities: AudioRecordingCapabilities): RecordingRouteDecision;
|
|
1750
1927
|
|
|
1751
1928
|
export type TranscodeRunOptions = {
|
|
1752
1929
|
optimizeForSpeed?: boolean;
|
|
@@ -1809,6 +1986,7 @@ export declare function transcodeVideoForUpload(file: Blob, config?: Partial<Tra
|
|
|
1809
1986
|
export {};
|
|
1810
1987
|
|
|
1811
1988
|
import type { Quality, VideoCodec } from "mediabunny";
|
|
1989
|
+
import type { AudioRecordingConfig, MediaType } from "../audio-recording/types";
|
|
1812
1990
|
export type { AudioCodec, OutputFormat, WatermarkConfig, WatermarkPosition, } from "../processor/types";
|
|
1813
1991
|
import type { AudioCodec, OutputFormat, WatermarkConfig } from "../processor/types";
|
|
1814
1992
|
export type VideoLatencyMode = "quality" | "realtime";
|
|
@@ -1828,6 +2006,8 @@ export type TranscodeConfig = {
|
|
|
1828
2006
|
}>;
|
|
1829
2007
|
tabVisibilityOverlayText?: string;
|
|
1830
2008
|
watermark?: WatermarkConfig;
|
|
2009
|
+
mediaType?: MediaType;
|
|
2010
|
+
audioRecording?: AudioRecordingConfig;
|
|
1831
2011
|
};
|
|
1832
2012
|
export type TranscodeInput = Blob | File | string;
|
|
1833
2013
|
export type TranscodeResult = {
|
|
@@ -1890,7 +2070,7 @@ export declare const AUDIO_WORKLET_MESSAGE_TYPE_AUDIO_CHUNK = "audioChunk";
|
|
|
1890
2070
|
export declare const AUDIO_WORKLET_MESSAGE_TYPE_SET_MUTED = "setMuted";
|
|
1891
2071
|
export declare const AUDIO_WORKLET_MESSAGE_TYPE_SET_PAUSED = "setPaused";
|
|
1892
2072
|
export declare const AUDIO_WORKLET_MESSAGE_TYPE_SHUTDOWN = "shutdown";
|
|
1893
|
-
export declare const audioWorkletProcessorCode = "\nconst AUDIO_WORKLET_MESSAGE_TYPE_AUDIO_CHUNK = \"audioChunk\";\nconst AUDIO_WORKLET_MESSAGE_TYPE_SET_MUTED = \"setMuted\";\nconst AUDIO_WORKLET_MESSAGE_TYPE_SET_PAUSED = \"setPaused\";\nconst AUDIO_WORKLET_MESSAGE_TYPE_SHUTDOWN = \"shutdown\";\n\nclass VidtreoAudioWorkletProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.isMuted = false;\n this.isPaused = false;\n this.processedFrames = 0;\n this.port.onmessage = this.handleMessage.bind(this);\n }\n\n handleMessage(event) {\n const data = event.data;\n if (!data || typeof data !== \"object\") {\n return;\n }\n\n if (data.type === AUDIO_WORKLET_MESSAGE_TYPE_SET_MUTED) {\n this.isMuted = data.isMuted === true;\n return;\n }\n\n if (data.type === AUDIO_WORKLET_MESSAGE_TYPE_SET_PAUSED) {\n this.isPaused = data.isPaused === true;\n return;\n }\n\n if (data.type === AUDIO_WORKLET_MESSAGE_TYPE_SHUTDOWN) {\n this.isPaused = true;\n }\n }\n\n calculateStats(samples) {\n if (!samples || samples.length === 0) {\n return { peak: 0, rms: 0 };\n }\n\n let peak = 0;\n let sumSquares = 0;\n\n for (const sample of samples) {\n const absoluteSample = Math.abs(sample);\n peak = Math.max(peak, absoluteSample);\n sumSquares += sample * sample;\n }\n\n return {\n peak,\n rms: Math.sqrt(sumSquares / samples.length),\n };\n }\n\n process(inputs) {\n if (this.isPaused) {\n return true;\n }\n\n const inputGroup = inputs[0];\n if (!inputGroup || inputGroup.length === 0) {\n return true;\n }\n\n const firstChannel = inputGroup[0];\n if (!firstChannel || firstChannel.length === 0) {\n return true;\n }\n\n const frames = firstChannel.length;\n const data = new Float32Array(frames);\n\n data.set(
|
|
2073
|
+
export declare const audioWorkletProcessorCode = "\nconst AUDIO_WORKLET_MESSAGE_TYPE_AUDIO_CHUNK = \"audioChunk\";\nconst AUDIO_WORKLET_MESSAGE_TYPE_SET_MUTED = \"setMuted\";\nconst AUDIO_WORKLET_MESSAGE_TYPE_SET_PAUSED = \"setPaused\";\nconst AUDIO_WORKLET_MESSAGE_TYPE_SHUTDOWN = \"shutdown\";\n\nclass VidtreoAudioWorkletProcessor extends AudioWorkletProcessor {\n constructor(options) {\n super();\n const processorOptions = options && options.processorOptions;\n this.channelCount =\n processorOptions && processorOptions.channelCount === 2 ? 2 : 1;\n this.isMuted = false;\n this.isPaused = false;\n this.processedFrames = 0;\n this.port.onmessage = this.handleMessage.bind(this);\n }\n\n handleMessage(event) {\n const data = event.data;\n if (!data || typeof data !== \"object\") {\n return;\n }\n\n if (data.type === AUDIO_WORKLET_MESSAGE_TYPE_SET_MUTED) {\n this.isMuted = data.isMuted === true;\n return;\n }\n\n if (data.type === AUDIO_WORKLET_MESSAGE_TYPE_SET_PAUSED) {\n this.isPaused = data.isPaused === true;\n return;\n }\n\n if (data.type === AUDIO_WORKLET_MESSAGE_TYPE_SHUTDOWN) {\n this.isPaused = true;\n }\n }\n\n calculateStats(samples) {\n if (!samples || samples.length === 0) {\n return { peak: 0, rms: 0 };\n }\n\n let peak = 0;\n let sumSquares = 0;\n\n for (const sample of samples) {\n const absoluteSample = Math.abs(sample);\n peak = Math.max(peak, absoluteSample);\n sumSquares += sample * sample;\n }\n\n return {\n peak,\n rms: Math.sqrt(sumSquares / samples.length),\n };\n }\n\n process(inputs) {\n if (this.isPaused) {\n return true;\n }\n\n const inputGroup = inputs[0];\n if (!inputGroup || inputGroup.length === 0) {\n return true;\n }\n\n const firstChannel = inputGroup[0];\n if (!firstChannel || firstChannel.length === 0) {\n return true;\n }\n\n const frames = firstChannel.length;\n const data = new Float32Array(frames * this.channelCount);\n\n for (let channel = 0; channel < this.channelCount; channel += 1) {\n const channelData = inputGroup[channel] || firstChannel;\n data.set(channelData, channel * frames);\n }\n const inputStats = this.calculateStats(firstChannel);\n\n if (this.isMuted) {\n data.fill(0);\n }\n\n const timestamp = this.processedFrames / sampleRate;\n this.processedFrames += frames;\n\n this.port.postMessage(\n {\n type: AUDIO_WORKLET_MESSAGE_TYPE_AUDIO_CHUNK,\n data,\n frames,\n numberOfChannels: this.channelCount,\n sampleRate,\n timestamp,\n inputPeak: inputStats.peak,\n inputRms: inputStats.rms,\n },\n [data.buffer]\n );\n\n return true;\n }\n}\n\nregisterProcessor(\"vidtreo-audio-worklet\", VidtreoAudioWorkletProcessor);\n";
|
|
1894
2074
|
|
|
1895
2075
|
export type AudioHealthClassification = "healthy" | "no-chunks" | "silent-while-unmuted" | "muted-silence-expected" | "speaking-while-muted" | "low-signal";
|
|
1896
2076
|
export type AudioHealthInput = {
|
|
@@ -1956,9 +2136,12 @@ export declare class AudioHealthMonitor {
|
|
|
1956
2136
|
private mutedDurationMs;
|
|
1957
2137
|
private currentMuted;
|
|
1958
2138
|
private hasHealthySignal;
|
|
2139
|
+
private pausedAtMs;
|
|
1959
2140
|
constructor(options?: AudioHealthMonitorOptions);
|
|
1960
2141
|
recordChunk(input: AudioHealthInput): AudioHealthSnapshot;
|
|
1961
2142
|
inspect(timestampMs: number, isMuted: boolean): AudioHealthSnapshot;
|
|
2143
|
+
pause(timestampMs: number): void;
|
|
2144
|
+
resume(timestampMs: number): void;
|
|
1962
2145
|
reset(): void;
|
|
1963
2146
|
private snapshot;
|
|
1964
2147
|
getFinalSnapshot(timestampMs: number, isMuted: boolean): AudioHealthFinalSnapshot;
|
|
@@ -2008,7 +2191,7 @@ export declare class AudioWorkletController {
|
|
|
2008
2191
|
private isPaused;
|
|
2009
2192
|
private isProcessing;
|
|
2010
2193
|
private hasAudioWorkletUrlLease;
|
|
2011
|
-
initialize(audioStream: MediaStream, onAudioChunk: (chunk: AudioWorkletChunk) => void): Promise<AudioWorkletConfig>;
|
|
2194
|
+
initialize(audioStream: MediaStream, onAudioChunk: (chunk: AudioWorkletChunk) => void, channelCount?: number): Promise<AudioWorkletConfig>;
|
|
2012
2195
|
startProcessing(): Promise<void>;
|
|
2013
2196
|
setMuted(isMuted: boolean): void;
|
|
2014
2197
|
setPaused(isPaused: boolean): void;
|
|
@@ -2161,6 +2344,7 @@ export declare function fitOutputToSource(output: OutputSize, source: OutputSize
|
|
|
2161
2344
|
export {};
|
|
2162
2345
|
|
|
2163
2346
|
import type { Quality, VideoCodec } from "mediabunny";
|
|
2347
|
+
import type { AudioRecordingConfig, MediaType } from "../audio-recording/types";
|
|
2164
2348
|
export type OutputFormat = "mp4" | "mkv" | "mov" | "webm";
|
|
2165
2349
|
export type AudioCodec = "aac" | "opus";
|
|
2166
2350
|
export type WatermarkPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center";
|
|
@@ -2203,6 +2387,8 @@ export type TranscodeConfig = {
|
|
|
2203
2387
|
audioBitrate?: number;
|
|
2204
2388
|
latencyMode?: "quality" | "realtime";
|
|
2205
2389
|
watermark?: WatermarkConfig;
|
|
2390
|
+
mediaType?: MediaType;
|
|
2391
|
+
audioRecording?: AudioRecordingConfig;
|
|
2206
2392
|
};
|
|
2207
2393
|
export type TranscodeInput = Blob | File | string;
|
|
2208
2394
|
export type TranscodeResult = {
|
|
@@ -2261,10 +2447,13 @@ export declare class WorkerProcessor {
|
|
|
2261
2447
|
private readonly videoFramePreflightDeps;
|
|
2262
2448
|
private noFrameWatchdogId;
|
|
2263
2449
|
private readonly watchdogDelayMs;
|
|
2450
|
+
private audioOnlyRecording;
|
|
2264
2451
|
constructor(dependencies?: WorkerProcessorDependencies);
|
|
2265
2452
|
prewarm(): void;
|
|
2266
2453
|
private getWorkerProbeResult;
|
|
2267
2454
|
startProcessing(stream: MediaStream, config: TranscodeConfig, overlayConfig?: OverlayConfig): Promise<void>;
|
|
2455
|
+
private startAudioOnlyProcessing;
|
|
2456
|
+
private prepareAudioOnlyStart;
|
|
2268
2457
|
private getWorkerOrThrow;
|
|
2269
2458
|
private ensureProcessingInactive;
|
|
2270
2459
|
private handleAudioWorkletChunk;
|
|
@@ -2305,6 +2494,7 @@ export declare class WorkerProcessor {
|
|
|
2305
2494
|
private buildRecordingStatsSummary;
|
|
2306
2495
|
private resetFinalizeRuntimeState;
|
|
2307
2496
|
private createBlobFromChunks;
|
|
2497
|
+
private createAudioOnlyResult;
|
|
2308
2498
|
private rejectFinalizeBlobCreationError;
|
|
2309
2499
|
cancel(): Promise<void>;
|
|
2310
2500
|
destroy(): void;
|
|
@@ -2392,7 +2582,7 @@ export declare function getWatermarkPosition(options: WatermarkPositionOptions):
|
|
|
2392
2582
|
y: number;
|
|
2393
2583
|
};
|
|
2394
2584
|
|
|
2395
|
-
export declare const workerCode = "(() => {\n var __create = Object.create;\n var __getProtoOf = Object.getPrototypeOf;\n var __defProp = Object.defineProperty;\n var __getOwnPropNames = Object.getOwnPropertyNames;\n var __getOwnPropDesc = Object.getOwnPropertyDescriptor;\n var __hasOwnProp = Object.prototype.hasOwnProperty;\n function __accessProp(key) {\n return this[key];\n }\n var __toESMCache_node;\n var __toESMCache_esm;\n var __toESM = (mod, isNodeMode, target) => {\n var canCache = mod != null && typeof mod === \"object\";\n if (canCache) {\n var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;\n var cached = cache.get(mod);\n if (cached)\n return cached;\n }\n target = mod != null ? __create(__getProtoOf(mod)) : {};\n const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, \"default\") ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target;\n if (mod && typeof mod === \"object\" || typeof mod === \"function\") {\n for (let key of __getOwnPropNames(mod))\n if (!__hasOwnProp.call(to, key))\n __defProp(to, key, {\n get: __accessProp.bind(mod, key),\n enumerable: true\n });\n }\n if (canCache)\n cache.set(mod, to);\n return to;\n };\n var __toCommonJS = (from) => {\n var entry = (__moduleCache ??= new WeakMap).get(from), desc;\n if (entry)\n return entry;\n entry = __defProp({}, \"__esModule\", { value: true });\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (var key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(entry, key))\n __defProp(entry, key, {\n get: __accessProp.bind(from, key),\n enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable\n });\n }\n __moduleCache.set(from, entry);\n return entry;\n };\n var __moduleCache;\n var __returnValue = (v) => v;\n function __exportSetter(name, newValue) {\n this[name] = __returnValue.bind(null, newValue);\n }\n var __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, {\n get: all[name],\n enumerable: true,\n configurable: true,\n set: __exportSetter.bind(all, name)\n });\n };\n var __require = /* @__PURE__ */ ((x) => typeof require !== \"undefined\" ? require : typeof Proxy !== \"undefined\" ? new Proxy(x, {\n get: (a, b) => (typeof require !== \"undefined\" ? require : a)[b]\n }) : x)(function(x) {\n if (typeof require !== \"undefined\")\n return require.apply(this, arguments);\n throw Error('Dynamic require of \"' + x + '\" is not supported');\n });\n\n // src/core/processor/worker/recorder-worker.ts\n var exports_recorder_worker = {};\n __export(exports_recorder_worker, {\n RecorderWorker: () => RecorderWorker,\n initializeRecorderWorker: () => initializeRecorderWorker\n });\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/misc.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n function assert(x) {\n if (!x) {\n throw new Error(\"Assertion failed.\");\n }\n }\n var DEG_TO_RAD = Math.PI / 180;\n var RAD_TO_DEG = 180 / Math.PI;\n var normalizeRotation = (rotation) => {\n const mappedRotation = (rotation % 360 + 360) % 360;\n if (mappedRotation === 0 || mappedRotation === 90 || mappedRotation === 180 || mappedRotation === 270) {\n return mappedRotation;\n } else {\n throw new Error(`Invalid rotation ${rotation}.`);\n }\n };\n var IDENTITY_MATRIX = [1, 0, 0, 0, 1, 0, 0, 0, 1];\n var centeredTransformationMatrix = (linear, width, height) => {\n const [a, b, , c, d] = linear;\n const transformedWidth = Math.abs(a) * width + Math.abs(c) * height;\n const transformedHeight = Math.abs(b) * width + Math.abs(d) * height;\n return multiplyMatrices(multiplyMatrices(translationMatrix(-width / 2, -height / 2), linear), translationMatrix(transformedWidth / 2, transformedHeight / 2));\n };\n var rotationMatrix = (rotationInDegrees) => {\n const theta = rotationInDegrees * DEG_TO_RAD;\n const cosTheta = Math.round(Math.cos(theta));\n const sinTheta = Math.round(Math.sin(theta));\n return [\n cosTheta,\n sinTheta,\n 0,\n -sinTheta,\n cosTheta,\n 0,\n 0,\n 0,\n 1\n ];\n };\n var translationMatrix = (x, y) => {\n return [\n 1,\n 0,\n 0,\n 0,\n 1,\n 0,\n x,\n y,\n 1\n ];\n };\n var scaleMatrix = (x, y) => {\n return [\n x,\n 0,\n 0,\n 0,\n y,\n 0,\n 0,\n 0,\n 1\n ];\n };\n var multiplyMatrices = (a, b) => {\n const result = new Array(9);\n for (let i = 0;i < 3; i++) {\n for (let j = 0;j < 3; j++) {\n result[3 * i + j] = a[3 * i] * b[j] + a[3 * i + 1] * b[3 + j] + a[3 * i + 2] * b[6 + j];\n }\n }\n return result;\n };\n var composeRotationAndFlip = (rotation1, flip1, rotation2, flip2) => {\n return {\n rotation: normalizeRotation(rotation1 + (flip1 ? -rotation2 : rotation2)),\n flip: flip1 !== flip2\n };\n };\n var last = (arr) => {\n return arr && arr[arr.length - 1];\n };\n var isU32 = (value) => {\n return value >= 0 && value < 2 ** 32;\n };\n var isI32 = (value) => {\n return value >= -(2 ** 31) && value < 2 ** 31;\n };\n var readExpGolomb = (bitstream) => {\n let leadingZeroBits = 0;\n while (bitstream.readBits(1) === 0 && leadingZeroBits < 32) {\n leadingZeroBits++;\n }\n if (leadingZeroBits >= 32) {\n throw new Error(\"Invalid exponential-Golomb code.\");\n }\n const result = (1 << leadingZeroBits) - 1 + bitstream.readBits(leadingZeroBits);\n return result;\n };\n var readSignedExpGolomb = (bitstream) => {\n const codeNum = readExpGolomb(bitstream);\n return (codeNum & 1) === 0 ? -(codeNum >> 1) : codeNum + 1 >> 1;\n };\n var toUint8Array = (source) => {\n if (source.constructor === Uint8Array) {\n return source;\n } else if (ArrayBuffer.isView(source)) {\n return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);\n } else {\n return new Uint8Array(source);\n }\n };\n var toDataView = (source) => {\n if (source.constructor === DataView) {\n return source;\n } else if (ArrayBuffer.isView(source)) {\n return new DataView(source.buffer, source.byteOffset, source.byteLength);\n } else {\n return new DataView(source);\n }\n };\n var TextEncoder = typeof globalThis.TextEncoder !== \"undefined\" ? globalThis.TextEncoder : class TextEncoder {\n constructor() {\n this.encoding = \"utf-8\";\n }\n encode(input = \"\") {\n const bytes = new Uint8Array(3 * input.length);\n let n = 0;\n for (let i = 0;i < input.length; i++) {\n let c = input.charCodeAt(i);\n if (c < 128) {\n bytes[n++] = c;\n } else if (c < 2048) {\n bytes[n++] = 192 | c >> 6;\n bytes[n++] = 128 | c & 63;\n } else if (c < 55296 || c > 57343) {\n bytes[n++] = 224 | c >> 12;\n bytes[n++] = 128 | c >> 6 & 63;\n bytes[n++] = 128 | c & 63;\n } else {\n const next = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;\n if (c < 56320 && next >= 56320 && next <= 57343) {\n c = 65536 + (c - 55296 << 10) + (next - 56320);\n i++;\n bytes[n++] = 240 | c >> 18;\n bytes[n++] = 128 | c >> 12 & 63;\n bytes[n++] = 128 | c >> 6 & 63;\n bytes[n++] = 128 | c & 63;\n } else {\n bytes[n++] = 239;\n bytes[n++] = 191;\n bytes[n++] = 189;\n }\n }\n }\n return bytes.slice(0, n);\n }\n };\n var TextDecoder = typeof globalThis.TextDecoder !== \"undefined\" ? globalThis.TextDecoder : class TextDecoder {\n constructor(label = \"utf-8\") {\n const normalized = label.trim().toLowerCase();\n if (normalized === \"utf-8\" || normalized === \"utf8\" || normalized === \"unicode-1-1-utf-8\") {\n this.encoding = \"utf-8\";\n } else if (normalized === \"utf-16le\" || normalized === \"utf-16\") {\n this.encoding = \"utf-16le\";\n } else if (normalized === \"utf-16be\") {\n this.encoding = \"utf-16be\";\n } else {\n throw new RangeError(`The encoding label provided ('${label}') is invalid.`);\n }\n }\n decode(input) {\n const bytes = input ? toUint8Array(input) : new Uint8Array(0);\n const units = [];\n if (this.encoding === \"utf-8\") {\n let i = bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191 ? 3 : 0;\n while (i < bytes.length) {\n const lead = bytes[i];\n if (lead < 128) {\n units.push(lead);\n i++;\n continue;\n }\n let continuationCount;\n let codePoint;\n if (lead >= 194 && lead < 224) {\n continuationCount = 1;\n codePoint = lead & 31;\n } else if (lead >= 224 && lead < 240) {\n continuationCount = 2;\n codePoint = lead & 15;\n } else if (lead >= 240 && lead < 245) {\n continuationCount = 3;\n codePoint = lead & 7;\n } else {\n units.push(65533);\n i++;\n continue;\n }\n let lowerBound = lead === 224 ? 160 : lead === 240 ? 144 : 128;\n let upperBound = lead === 237 ? 159 : lead === 244 ? 143 : 191;\n let j = 1;\n for (;j <= continuationCount; j++) {\n const byte = i + j < bytes.length ? bytes[i + j] : 0;\n if (byte < lowerBound || byte > upperBound) {\n break;\n }\n codePoint = codePoint << 6 | byte & 63;\n lowerBound = 128;\n upperBound = 191;\n }\n i += j;\n if (j <= continuationCount) {\n units.push(65533);\n } else if (codePoint >= 65536) {\n codePoint -= 65536;\n units.push(55296 | codePoint >> 10, 56320 | codePoint & 1023);\n } else {\n units.push(codePoint);\n }\n }\n } else {\n const littleEndian = this.encoding === \"utf-16le\";\n const bomLow = littleEndian ? 255 : 254;\n const bomHigh = littleEndian ? 254 : 255;\n let i = bytes.length >= 2 && bytes[0] === bomLow && bytes[1] === bomHigh ? 2 : 0;\n while (i + 1 < bytes.length) {\n const unit = littleEndian ? bytes[i] | bytes[i + 1] << 8 : bytes[i] << 8 | bytes[i + 1];\n i += 2;\n if (unit >= 55296 && unit <= 56319) {\n const next = i + 1 < bytes.length ? littleEndian ? bytes[i] | bytes[i + 1] << 8 : bytes[i] << 8 | bytes[i + 1] : -1;\n if (next >= 56320 && next <= 57343) {\n units.push(unit, next);\n i += 2;\n } else {\n units.push(65533);\n }\n } else if (unit >= 56320 && unit <= 57343) {\n units.push(65533);\n } else {\n units.push(unit);\n }\n }\n if (i < bytes.length) {\n units.push(65533);\n }\n }\n let result = \"\";\n for (let i = 0;i < units.length; i += 8192) {\n result += String.fromCharCode(...units.slice(i, i + 8192));\n }\n return result;\n }\n };\n var textEncoder = /* @__PURE__ */ new TextEncoder;\n var COLOR_PRIMARIES_MAP = {\n bt709: 1,\n bt470bg: 5,\n smpte170m: 6,\n bt2020: 9,\n smpte432: 12\n };\n var TRANSFER_CHARACTERISTICS_MAP = {\n bt709: 1,\n smpte170m: 6,\n linear: 8,\n \"iec61966-2-1\": 13,\n pq: 16,\n hlg: 18\n };\n var MATRIX_COEFFICIENTS_MAP = {\n rgb: 0,\n bt709: 1,\n bt470bg: 5,\n smpte170m: 6,\n \"bt2020-ncl\": 9\n };\n var colorSpaceIsEmpty = (colorSpace) => {\n return !colorSpace || colorSpace.primaries == null && colorSpace.transfer == null && colorSpace.matrix == null && colorSpace.fullRange == null;\n };\n var isAllowSharedBufferSource = (x) => {\n return x instanceof ArrayBuffer || typeof SharedArrayBuffer !== \"undefined\" && x instanceof SharedArrayBuffer || ArrayBuffer.isView(x);\n };\n\n class AsyncMutex {\n constructor() {\n this.currentPromise = Promise.resolve();\n this.pending = 0;\n }\n async acquire() {\n let resolver;\n const nextPromise = new Promise((resolve) => {\n let resolved = false;\n resolver = () => {\n if (resolved) {\n return;\n }\n resolve();\n this.pending--;\n resolved = true;\n };\n });\n const currentPromiseAlias = this.currentPromise;\n this.currentPromise = nextPromise;\n this.pending++;\n await currentPromiseAlias;\n return resolver;\n }\n }\n var bytesToHexString = (bytes) => {\n return [...bytes].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n };\n var reverseBitsU32 = (x) => {\n x = x >> 1 & 1431655765 | (x & 1431655765) << 1;\n x = x >> 2 & 858993459 | (x & 858993459) << 2;\n x = x >> 4 & 252645135 | (x & 252645135) << 4;\n x = x >> 8 & 16711935 | (x & 16711935) << 8;\n x = x >> 16 & 65535 | (x & 65535) << 16;\n return x >>> 0;\n };\n var binarySearchLessOrEqual = (arr, key, valueGetter) => {\n let low = 0;\n let high = arr.length - 1;\n let ans = -1;\n while (low <= high) {\n const mid = low + (high - low + 1) / 2 | 0;\n const midVal = valueGetter(arr[mid]);\n if (midVal <= key) {\n ans = mid;\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n return ans;\n };\n var promiseWithResolvers = () => {\n let resolve;\n let reject;\n const promise = new Promise((res, rej) => {\n resolve = res;\n reject = rej;\n });\n return { promise, resolve, reject };\n };\n var assertNever = (x) => {\n throw new Error(`Unexpected value: ${x}`);\n };\n var getUint24 = (view, byteOffset, littleEndian) => {\n const byte1 = view.getUint8(byteOffset);\n const byte2 = view.getUint8(byteOffset + 1);\n const byte3 = view.getUint8(byteOffset + 2);\n if (littleEndian) {\n return byte1 | byte2 << 8 | byte3 << 16;\n } else {\n return byte1 << 16 | byte2 << 8 | byte3;\n }\n };\n var setUint24 = (view, byteOffset, value, littleEndian) => {\n value = value >>> 0;\n value = value & 16777215;\n if (littleEndian) {\n view.setUint8(byteOffset, value & 255);\n view.setUint8(byteOffset + 1, value >>> 8 & 255);\n view.setUint8(byteOffset + 2, value >>> 16 & 255);\n } else {\n view.setUint8(byteOffset, value >>> 16 & 255);\n view.setUint8(byteOffset + 1, value >>> 8 & 255);\n view.setUint8(byteOffset + 2, value & 255);\n }\n };\n var setInt24 = (view, byteOffset, value, littleEndian) => {\n value = clamp(value, -8388608, 8388607);\n if (value < 0) {\n value = value + 16777216 & 16777215;\n }\n setUint24(view, byteOffset, value, littleEndian);\n };\n var clamp = (value, min, max) => {\n return Math.max(min, Math.min(max, value));\n };\n var lerp = (from, to, t) => {\n return from + (to - from) * t;\n };\n var UNDETERMINED_LANGUAGE = \"und\";\n var roundToMultiple = (value, multiple) => {\n return Math.round(value / multiple) * multiple;\n };\n var roundToDivisor = (value, multiple) => {\n return Math.round(value * multiple) / multiple;\n };\n var floorToDivisor = (value, multiple) => {\n return Math.floor(value * multiple) / multiple;\n };\n var popcount = (value) => {\n let count = 0;\n while (value !== 0) {\n value &= value - 1;\n count++;\n }\n return count;\n };\n var ISO_639_2_REGEX = /^[a-z]{3}$/;\n var isIso639Dash2LanguageCode = (x) => {\n return ISO_639_2_REGEX.test(x);\n };\n var SECOND_TO_MICROSECOND_FACTOR = 1e6 * (1 + Number.EPSILON);\n var computeRationalApproximation = (x, maxDenominator) => {\n const sign = x < 0 ? -1 : 1;\n x = Math.abs(x);\n let prevNumerator = 0, prevDenominator = 1;\n let currNumerator = 1, currDenominator = 0;\n let remainder = x;\n while (true) {\n const integer = Math.floor(remainder);\n const nextNumerator = integer * currNumerator + prevNumerator;\n const nextDenominator = integer * currDenominator + prevDenominator;\n if (nextDenominator > maxDenominator) {\n return {\n num: sign * currNumerator,\n den: currDenominator\n };\n }\n prevNumerator = currNumerator;\n prevDenominator = currDenominator;\n currNumerator = nextNumerator;\n currDenominator = nextDenominator;\n remainder = 1 / (remainder - integer);\n if (!isFinite(remainder)) {\n break;\n }\n }\n return {\n num: sign * currNumerator,\n den: currDenominator\n };\n };\n\n class CallSerializer {\n constructor() {\n this.currentPromise = Promise.resolve();\n }\n call(fn) {\n return this.currentPromise = this.currentPromise.then(fn);\n }\n }\n var isWebKitCache = null;\n var isWebKit = () => {\n if (isWebKitCache !== null) {\n return isWebKitCache;\n }\n return isWebKitCache = !!(typeof navigator !== \"undefined\" && (navigator.vendor?.match(/apple/i) || /AppleWebKit/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) || /\\b(iPad|iPhone|iPod)\\b/.test(navigator.userAgent)));\n };\n var isFirefoxCache = null;\n var isFirefox = () => {\n if (isFirefoxCache !== null) {\n return isFirefoxCache;\n }\n return isFirefoxCache = typeof navigator !== \"undefined\" && navigator.userAgent?.includes(\"Firefox\");\n };\n var isChromiumCache = null;\n var isChromium = () => {\n if (isChromiumCache !== null) {\n return isChromiumCache;\n }\n return isChromiumCache = !!(typeof navigator !== \"undefined\" && (navigator.vendor?.includes(\"Google Inc\") || /Chrome/.test(navigator.userAgent)));\n };\n var missingWebCodecsClassMessage = (className) => {\n if (typeof globalThis.isSecureContext !== \"undefined\" && !globalThis.isSecureContext) {\n return `${className} is not available in this environment; this may be because this page is running in an` + ` insecure context. Try serving your page over HTTPS or use localhost.`;\n }\n return `${className} is not available in this environment.`;\n };\n var isCodecReclaimedError = (error) => {\n return error instanceof DOMException && error.name === \"QuotaExceededError\" && /reclaimed/i.test(error.message);\n };\n var NativePromiseConstructor = (async () => {})().constructor;\n var isThenable = (value) => {\n if (value instanceof NativePromiseConstructor || value instanceof Promise) {\n return true;\n }\n return typeof value?.then === \"function\";\n };\n var keyValueIterator = function* (object) {\n for (const key in object) {\n const value = object[key];\n if (value === undefined) {\n continue;\n }\n yield { key, value };\n }\n };\n var polyfillSymbolDispose = () => {\n Symbol.dispose ??= Symbol(\"Symbol.dispose\");\n };\n var isNumber = (x) => {\n return typeof x === \"number\" && !Number.isNaN(x);\n };\n var arrayArgmin = (array, getValue) => {\n let minIndex = -1;\n let minValue = Infinity;\n for (let i = 0;i < array.length; i++) {\n const value = getValue(array[i]);\n if (value < minValue) {\n minValue = value;\n minIndex = i;\n }\n }\n return minIndex;\n };\n var simplifyRational = (rational) => {\n assert(Number.isInteger(rational.num));\n assert(Number.isInteger(rational.den));\n assert(rational.den !== 0);\n let a = Math.abs(rational.num);\n let b = Math.abs(rational.den);\n while (b !== 0) {\n const t = a % b;\n a = b;\n b = t;\n }\n const gcd = a || 1;\n return {\n num: rational.num / gcd,\n den: rational.den / gcd\n };\n };\n var validateRectangle = (rect, propertyPath) => {\n if (typeof rect !== \"object\" || !rect) {\n throw new TypeError(`${propertyPath} must be an object.`);\n }\n if (!Number.isInteger(rect.left) || rect.left < 0) {\n throw new TypeError(`${propertyPath}.left must be a non-negative integer.`);\n }\n if (!Number.isInteger(rect.top) || rect.top < 0) {\n throw new TypeError(`${propertyPath}.top must be a non-negative integer.`);\n }\n if (!Number.isInteger(rect.width) || rect.width < 0) {\n throw new TypeError(`${propertyPath}.width must be a non-negative integer.`);\n }\n if (!Number.isInteger(rect.height) || rect.height < 0) {\n throw new TypeError(`${propertyPath}.height must be a non-negative integer.`);\n }\n };\n var unthrottledTimeoutCallbacks = new Map;\n var unthrottledIntervalCallbacks = new Map;\n var wait = (ms) => {\n return new Promise((resolve) => setTimeout(resolve, ms));\n };\n var toArray = (x) => {\n if (Array.isArray(x)) {\n return x;\n } else {\n return [x];\n }\n };\n\n class EventEmitter {\n constructor() {\n this._listeners = new Map;\n }\n on(event, listener, options) {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set);\n }\n const entry = { fn: listener, once: options?.once ?? false };\n this._listeners.get(event).add(entry);\n return () => {\n this._listeners.get(event)?.delete(entry);\n };\n }\n _emit(...args) {\n const [event, data] = args;\n const listeners = this._listeners.get(event);\n if (!listeners) {\n return;\n }\n for (const entry of listeners) {\n try {\n entry.fn(data);\n } catch (error) {\n console.error(error);\n }\n if (entry.once) {\n listeners.delete(entry);\n }\n }\n }\n }\n var isRecordStringString = (value) => {\n return value !== null && typeof value === \"object\" && Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((x) => typeof x === \"string\");\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/logging.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var LogLevel;\n (function(LogLevel) {\n LogLevel[LogLevel[\"Silent\"] = 0] = \"Silent\";\n LogLevel[LogLevel[\"Errors\"] = 1] = \"Errors\";\n LogLevel[LogLevel[\"Warnings\"] = 2] = \"Warnings\";\n LogLevel[LogLevel[\"Info\"] = 3] = \"Info\";\n })(LogLevel || (LogLevel = {}));\n\n class Logging {\n constructor() {}\n static get level() {\n return Logging._level;\n }\n static set level(value) {\n if (value !== LogLevel.Silent && value !== LogLevel.Errors && value !== LogLevel.Warnings && value !== LogLevel.Info) {\n throw new TypeError(\"Invalid log level. Use one of the values of the LogLevel enum.\");\n }\n Logging._level = value;\n }\n static get _emitter() {\n return Logging._emitterInstance ??= new EventEmitter;\n }\n static on(event, listener, options) {\n return Logging._emitter.on(event, listener, options);\n }\n static _error(...args) {\n Logging._emitter._emit(\"error\", args);\n if (Logging._level >= LogLevel.Errors) {\n console.error(...args);\n }\n }\n static _warn(...args) {\n Logging._emitter._emit(\"warn\", args);\n if (Logging._level >= LogLevel.Warnings) {\n console.warn(...args);\n }\n }\n static _info(...args) {\n Logging._emitter._emit(\"info\", args);\n if (Logging._level >= LogLevel.Info) {\n console.info(...args);\n }\n }\n }\n Logging._level = LogLevel.Info;\n Logging._emitterInstance = null;\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/metadata.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class RichImageData {\n constructor(data, mimeType) {\n this.data = data;\n this.mimeType = mimeType;\n if (!(data instanceof Uint8Array)) {\n throw new TypeError(\"data must be a Uint8Array.\");\n }\n if (typeof mimeType !== \"string\") {\n throw new TypeError(\"mimeType must be a string.\");\n }\n }\n }\n\n class AttachedFile {\n constructor(data, mimeType, name, description) {\n this.data = data;\n this.mimeType = mimeType;\n this.name = name;\n this.description = description;\n if (!(data instanceof Uint8Array)) {\n throw new TypeError(\"data must be a Uint8Array.\");\n }\n if (mimeType !== undefined && typeof mimeType !== \"string\") {\n throw new TypeError(\"mimeType, when provided, must be a string.\");\n }\n if (name !== undefined && typeof name !== \"string\") {\n throw new TypeError(\"name, when provided, must be a string.\");\n }\n if (description !== undefined && typeof description !== \"string\") {\n throw new TypeError(\"description, when provided, must be a string.\");\n }\n }\n }\n var validateMetadataTags = (tags) => {\n if (!tags || typeof tags !== \"object\") {\n throw new TypeError(\"tags must be an object.\");\n }\n if (tags.title !== undefined && typeof tags.title !== \"string\") {\n throw new TypeError(\"tags.title, when provided, must be a string.\");\n }\n if (tags.description !== undefined && typeof tags.description !== \"string\") {\n throw new TypeError(\"tags.description, when provided, must be a string.\");\n }\n if (tags.artist !== undefined && typeof tags.artist !== \"string\") {\n throw new TypeError(\"tags.artist, when provided, must be a string.\");\n }\n if (tags.album !== undefined && typeof tags.album !== \"string\") {\n throw new TypeError(\"tags.album, when provided, must be a string.\");\n }\n if (tags.albumArtist !== undefined && typeof tags.albumArtist !== \"string\") {\n throw new TypeError(\"tags.albumArtist, when provided, must be a string.\");\n }\n if (tags.trackNumber !== undefined && (!Number.isInteger(tags.trackNumber) || tags.trackNumber <= 0)) {\n throw new TypeError(\"tags.trackNumber, when provided, must be a positive integer.\");\n }\n if (tags.tracksTotal !== undefined && (!Number.isInteger(tags.tracksTotal) || tags.tracksTotal <= 0)) {\n throw new TypeError(\"tags.tracksTotal, when provided, must be a positive integer.\");\n }\n if (tags.discNumber !== undefined && (!Number.isInteger(tags.discNumber) || tags.discNumber <= 0)) {\n throw new TypeError(\"tags.discNumber, when provided, must be a positive integer.\");\n }\n if (tags.discsTotal !== undefined && (!Number.isInteger(tags.discsTotal) || tags.discsTotal <= 0)) {\n throw new TypeError(\"tags.discsTotal, when provided, must be a positive integer.\");\n }\n if (tags.genre !== undefined && typeof tags.genre !== \"string\") {\n throw new TypeError(\"tags.genre, when provided, must be a string.\");\n }\n if (tags.date !== undefined && (!(tags.date instanceof Date) || Number.isNaN(tags.date.getTime()))) {\n throw new TypeError(\"tags.date, when provided, must be a valid Date.\");\n }\n if (tags.beatsPerMinute !== undefined && (!Number.isInteger(tags.beatsPerMinute) || tags.beatsPerMinute <= 0)) {\n throw new TypeError(\"tags.beatsPerMinute, when provided, must be a positive integer.\");\n }\n if (tags.lyrics !== undefined && typeof tags.lyrics !== \"string\") {\n throw new TypeError(\"tags.lyrics, when provided, must be a string.\");\n }\n if (tags.images !== undefined) {\n if (!Array.isArray(tags.images)) {\n throw new TypeError(\"tags.images, when provided, must be an array.\");\n }\n for (const image of tags.images) {\n if (!image || typeof image !== \"object\") {\n throw new TypeError(\"Each image in tags.images must be an object.\");\n }\n if (!(image.data instanceof Uint8Array)) {\n throw new TypeError(\"Each image.data must be a Uint8Array.\");\n }\n if (typeof image.mimeType !== \"string\") {\n throw new TypeError(\"Each image.mimeType must be a string.\");\n }\n if (![\"coverFront\", \"coverBack\", \"unknown\"].includes(image.kind)) {\n throw new TypeError(\"Each image.kind must be 'coverFront', 'coverBack', or 'unknown'.\");\n }\n }\n }\n if (tags.comment !== undefined && typeof tags.comment !== \"string\") {\n throw new TypeError(\"tags.comment, when provided, must be a string.\");\n }\n if (tags.raw !== undefined) {\n if (!tags.raw || typeof tags.raw !== \"object\") {\n throw new TypeError(\"tags.raw, when provided, must be an object.\");\n }\n for (const value of Object.values(tags.raw)) {\n if (value !== null && typeof value !== \"string\" && !(Array.isArray(value) && value.every((x) => typeof x === \"string\")) && !(value instanceof Uint8Array) && !(value instanceof RichImageData) && !(value instanceof AttachedFile) && !isRecordStringString(value)) {\n throw new TypeError(\"Each value in tags.raw must be a string, string array, Uint8Array, RichImageData, AttachedFile, \" + \"Record<string, string>, or null.\");\n }\n }\n }\n };\n var validateTrackDisposition = (disposition) => {\n if (!disposition || typeof disposition !== \"object\") {\n throw new TypeError(\"disposition must be an object.\");\n }\n if (disposition.default !== undefined && typeof disposition.default !== \"boolean\") {\n throw new TypeError(\"disposition.default must be a boolean.\");\n }\n if (disposition.primary !== undefined && typeof disposition.primary !== \"boolean\") {\n throw new TypeError(\"disposition.primary must be a boolean.\");\n }\n if (disposition.forced !== undefined && typeof disposition.forced !== \"boolean\") {\n throw new TypeError(\"disposition.forced must be a boolean.\");\n }\n if (disposition.original !== undefined && typeof disposition.original !== \"boolean\") {\n throw new TypeError(\"disposition.original must be a boolean.\");\n }\n if (disposition.commentary !== undefined && typeof disposition.commentary !== \"boolean\") {\n throw new TypeError(\"disposition.commentary must be a boolean.\");\n }\n if (disposition.hearingImpaired !== undefined && typeof disposition.hearingImpaired !== \"boolean\") {\n throw new TypeError(\"disposition.hearingImpaired must be a boolean.\");\n }\n if (disposition.visuallyImpaired !== undefined && typeof disposition.visuallyImpaired !== \"boolean\") {\n throw new TypeError(\"disposition.visuallyImpaired must be a boolean.\");\n }\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/shared/bitstream.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class Bitstream {\n constructor(bytes) {\n this.bytes = bytes;\n this.pos = 0;\n }\n seekToByte(byteOffset) {\n this.pos = 8 * byteOffset;\n }\n readBit() {\n const byteIndex = Math.floor(this.pos / 8);\n const byte = this.bytes[byteIndex] ?? 0;\n const bitIndex = 7 - (this.pos & 7);\n const bit = (byte & 1 << bitIndex) >> bitIndex;\n this.pos++;\n return bit;\n }\n readBits(n) {\n if (n === 1) {\n return this.readBit();\n }\n let result = 0;\n for (let i = 0;i < n; i++) {\n result <<= 1;\n result |= this.readBit();\n }\n return result;\n }\n writeBits(n, value) {\n const end = this.pos + n;\n for (let i = this.pos;i < end; i++) {\n const byteIndex = Math.floor(i / 8);\n let byte = this.bytes[byteIndex];\n const bitIndex = 7 - (i & 7);\n byte &= ~(1 << bitIndex);\n byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex;\n this.bytes[byteIndex] = byte;\n }\n this.pos = end;\n }\n copyBits(n, other) {\n let i = 0;\n for (i;i < n - 7; i += 8) {\n this.writeBits(8, other.readBits(8));\n }\n const leftover = n - i;\n if (leftover > 0) {\n this.writeBits(leftover, other.readBits(leftover));\n }\n }\n readAlignedByte() {\n if (this.pos % 8 !== 0) {\n throw new Error(\"Bitstream is not byte-aligned.\");\n }\n const byteIndex = this.pos / 8;\n const byte = this.bytes[byteIndex] ?? 0;\n this.pos += 8;\n return byte;\n }\n skipBits(n) {\n this.pos += n;\n }\n getBitsLeft() {\n return this.bytes.length * 8 - this.pos;\n }\n clone() {\n const clone = new Bitstream(this.bytes);\n clone.pos = this.pos;\n return clone;\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/shared/aac-misc.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var aacFrequencyTable = [\n 96000,\n 88200,\n 64000,\n 48000,\n 44100,\n 32000,\n 24000,\n 22050,\n 16000,\n 12000,\n 11025,\n 8000,\n 7350\n ];\n var aacChannelMap = [-1, 1, 2, 3, 4, 5, 6, 8];\n var parseAacAudioSpecificConfig = (bytes) => {\n if (!bytes || bytes.byteLength < 2) {\n throw new TypeError(\"AAC description must be at least 2 bytes long.\");\n }\n const bitstream = new Bitstream(bytes);\n const objectType = readAacObjectType(bitstream);\n const { frequencyIndex, sampleRate } = readAacSamplingFrequency(bitstream);\n const channelConfiguration = bitstream.readBits(4);\n let numberOfChannels = null;\n if (channelConfiguration >= 1 && channelConfiguration <= 7) {\n numberOfChannels = aacChannelMap[channelConfiguration];\n }\n let coreObjectType = objectType;\n let psPresent = false;\n let outputSampleRate = sampleRate;\n if (objectType === 5 || objectType === 29) {\n psPresent = objectType === 29;\n outputSampleRate = readAacSamplingFrequency(bitstream).sampleRate;\n coreObjectType = readAacObjectType(bitstream);\n if (coreObjectType === 22) {\n bitstream.skipBits(4);\n }\n } else {\n while (bitstream.getBitsLeft() > 15) {\n const searchStart = bitstream.pos;\n if (bitstream.readBits(11) !== 695) {\n bitstream.pos = searchStart + 1;\n continue;\n }\n if (readAacObjectType(bitstream) === 5 && bitstream.readBits(1)) {\n outputSampleRate = readAacSamplingFrequency(bitstream).sampleRate;\n if (bitstream.getBitsLeft() > 11 && bitstream.readBits(11) === 1352) {\n psPresent = !!bitstream.readBits(1);\n }\n }\n break;\n }\n }\n if (numberOfChannels !== null && numberOfChannels > 1) {\n psPresent = false;\n }\n return {\n objectType,\n coreObjectType,\n frequencyIndex,\n channelConfiguration,\n outputSampleRate,\n outputNumberOfChannels: psPresent && numberOfChannels === 1 ? 2 : numberOfChannels\n };\n };\n var readAacObjectType = (bitstream) => {\n const objectType = bitstream.readBits(5);\n return objectType === 31 ? 32 + bitstream.readBits(6) : objectType;\n };\n var readAacSamplingFrequency = (bitstream) => {\n const frequencyIndex = bitstream.readBits(4);\n if (frequencyIndex === 15) {\n return {\n frequencyIndex,\n sampleRate: bitstream.readBits(24)\n };\n }\n return {\n frequencyIndex,\n sampleRate: frequencyIndex < aacFrequencyTable.length ? aacFrequencyTable[frequencyIndex] : null\n };\n };\n var buildAacAudioSpecificConfig = (config) => {\n const usesSbr = config.objectType === 5 || config.objectType === 29;\n const usesPs = config.objectType === 29;\n const coreSampleRate = usesSbr ? config.outputSampleRate / 2 : config.outputSampleRate;\n const coreNumberOfChannels = usesPs ? 1 : config.outputNumberOfChannels;\n const channelConfiguration = aacChannelMap.indexOf(coreNumberOfChannels);\n if (channelConfiguration === -1) {\n throw new TypeError(`Unsupported number of channels: ${config.outputNumberOfChannels}`);\n }\n let bitCount = 5 + 4 + 4 + 3;\n if (config.objectType >= 32) {\n bitCount += 6;\n }\n if (findAacFrequencyIndex(coreSampleRate) === 15) {\n bitCount += 24;\n }\n if (usesSbr) {\n bitCount += 4 + 5;\n if (findAacFrequencyIndex(config.outputSampleRate) === 15) {\n bitCount += 24;\n }\n }\n const byteCount = Math.ceil(bitCount / 8);\n const bytes = new Uint8Array(byteCount);\n const bitstream = new Bitstream(bytes);\n writeAacObjectType(bitstream, config.objectType);\n writeAacSamplingFrequency(bitstream, coreSampleRate);\n bitstream.writeBits(4, channelConfiguration);\n if (usesSbr) {\n writeAacSamplingFrequency(bitstream, config.outputSampleRate);\n writeAacObjectType(bitstream, 2);\n }\n bitstream.writeBits(3, 0);\n return bytes;\n };\n var writeAacObjectType = (bitstream, objectType) => {\n if (objectType < 32) {\n bitstream.writeBits(5, objectType);\n } else {\n bitstream.writeBits(5, 31);\n bitstream.writeBits(6, objectType - 32);\n }\n };\n var writeAacSamplingFrequency = (bitstream, sampleRate) => {\n const frequencyIndex = findAacFrequencyIndex(sampleRate);\n bitstream.writeBits(4, frequencyIndex);\n if (frequencyIndex === 15) {\n bitstream.writeBits(24, sampleRate);\n }\n };\n var findAacFrequencyIndex = (sampleRate) => {\n const index = aacFrequencyTable.indexOf(sampleRate);\n return index === -1 ? 15 : index;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/shared/ac3-misc.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var AC3_SAMPLE_RATES = [48000, 44100, 32000];\n var EAC3_REDUCED_SAMPLE_RATES = [24000, 22050, 16000];\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/version.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var VERSION = \"1.61.3\";\n var VERSIONED_NAME = `Mediabunny v${VERSION}`;\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/codec-data.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var AvcNalUnitType;\n (function(AvcNalUnitType) {\n AvcNalUnitType[AvcNalUnitType[\"NON_IDR_SLICE\"] = 1] = \"NON_IDR_SLICE\";\n AvcNalUnitType[AvcNalUnitType[\"SLICE_DPA\"] = 2] = \"SLICE_DPA\";\n AvcNalUnitType[AvcNalUnitType[\"SLICE_DPB\"] = 3] = \"SLICE_DPB\";\n AvcNalUnitType[AvcNalUnitType[\"SLICE_DPC\"] = 4] = \"SLICE_DPC\";\n AvcNalUnitType[AvcNalUnitType[\"IDR\"] = 5] = \"IDR\";\n AvcNalUnitType[AvcNalUnitType[\"SEI\"] = 6] = \"SEI\";\n AvcNalUnitType[AvcNalUnitType[\"SPS\"] = 7] = \"SPS\";\n AvcNalUnitType[AvcNalUnitType[\"PPS\"] = 8] = \"PPS\";\n AvcNalUnitType[AvcNalUnitType[\"AUD\"] = 9] = \"AUD\";\n AvcNalUnitType[AvcNalUnitType[\"SPS_EXT\"] = 13] = \"SPS_EXT\";\n })(AvcNalUnitType || (AvcNalUnitType = {}));\n var HevcNalUnitType;\n (function(HevcNalUnitType) {\n HevcNalUnitType[HevcNalUnitType[\"RASL_N\"] = 8] = \"RASL_N\";\n HevcNalUnitType[HevcNalUnitType[\"RASL_R\"] = 9] = \"RASL_R\";\n HevcNalUnitType[HevcNalUnitType[\"BLA_W_LP\"] = 16] = \"BLA_W_LP\";\n HevcNalUnitType[HevcNalUnitType[\"RSV_IRAP_VCL23\"] = 23] = \"RSV_IRAP_VCL23\";\n HevcNalUnitType[HevcNalUnitType[\"VPS_NUT\"] = 32] = \"VPS_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"SPS_NUT\"] = 33] = \"SPS_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"PPS_NUT\"] = 34] = \"PPS_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"AUD_NUT\"] = 35] = \"AUD_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"PREFIX_SEI_NUT\"] = 39] = \"PREFIX_SEI_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"SUFFIX_SEI_NUT\"] = 40] = \"SUFFIX_SEI_NUT\";\n })(HevcNalUnitType || (HevcNalUnitType = {}));\n var iterateNalUnitsInAnnexB = function* (packetData) {\n let i = 0;\n let nalStart = -1;\n while (i < packetData.length - 2) {\n const zeroIndex = packetData.indexOf(0, i);\n if (zeroIndex === -1 || zeroIndex >= packetData.length - 2) {\n break;\n }\n i = zeroIndex;\n let startCodeLength = 0;\n if (i + 3 < packetData.length && packetData[i + 1] === 0 && packetData[i + 2] === 0 && packetData[i + 3] === 1) {\n startCodeLength = 4;\n } else if (packetData[i + 1] === 0 && packetData[i + 2] === 1) {\n startCodeLength = 3;\n }\n if (startCodeLength === 0) {\n i++;\n continue;\n }\n if (nalStart !== -1 && i > nalStart) {\n yield {\n offset: nalStart,\n length: i - nalStart\n };\n }\n nalStart = i + startCodeLength;\n i = nalStart;\n }\n if (nalStart !== -1 && nalStart < packetData.length) {\n yield {\n offset: nalStart,\n length: packetData.length - nalStart\n };\n }\n };\n var iterateNalUnitsInLengthPrefixed = function* (packetData, lengthSize) {\n let offset = 0;\n const dataView = new DataView(packetData.buffer, packetData.byteOffset, packetData.byteLength);\n while (offset + lengthSize <= packetData.length) {\n let nalUnitLength;\n if (lengthSize === 1) {\n nalUnitLength = dataView.getUint8(offset);\n } else if (lengthSize === 2) {\n nalUnitLength = dataView.getUint16(offset, false);\n } else if (lengthSize === 3) {\n nalUnitLength = getUint24(dataView, offset, false);\n } else {\n assert(lengthSize === 4);\n nalUnitLength = dataView.getUint32(offset, false);\n }\n offset += lengthSize;\n yield {\n offset,\n length: nalUnitLength\n };\n offset += nalUnitLength;\n }\n };\n var iterateAvcNalUnits = (packetData, decoderConfig) => {\n if (decoderConfig.description) {\n const bytes = toUint8Array(decoderConfig.description);\n const lengthSizeMinusOne = bytes[4] & 3;\n const lengthSize = lengthSizeMinusOne + 1;\n return iterateNalUnitsInLengthPrefixed(packetData, lengthSize);\n } else {\n return iterateNalUnitsInAnnexB(packetData);\n }\n };\n var extractNalUnitTypeForAvc = (byte) => {\n return byte & 31;\n };\n var removeEmulationPreventionBytes = (data) => {\n const result = [];\n const len = data.length;\n for (let i = 0;i < len; i++) {\n if (i + 2 < len && data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 3) {\n result.push(0, 0);\n i += 2;\n } else {\n result.push(data[i]);\n }\n }\n return new Uint8Array(result);\n };\n var ANNEX_B_START_CODE = new Uint8Array([0, 0, 0, 1]);\n var concatNalUnitsInLengthPrefixed = (nalUnits, lengthSize) => {\n const totalLength = nalUnits.reduce((a, b) => a + lengthSize + b.byteLength, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const nalUnit of nalUnits) {\n const dataView = new DataView(result.buffer, result.byteOffset, result.byteLength);\n switch (lengthSize) {\n case 1:\n dataView.setUint8(offset, nalUnit.byteLength);\n break;\n case 2:\n dataView.setUint16(offset, nalUnit.byteLength, false);\n break;\n case 3:\n setUint24(dataView, offset, nalUnit.byteLength, false);\n break;\n case 4:\n dataView.setUint32(offset, nalUnit.byteLength, false);\n break;\n }\n offset += lengthSize;\n result.set(nalUnit, offset);\n offset += nalUnit.byteLength;\n }\n return result;\n };\n var extractAvcDecoderConfigurationRecord = (packetData) => {\n try {\n const spsUnits = [];\n const ppsUnits = [];\n const spsExtUnits = [];\n for (const loc of iterateNalUnitsInAnnexB(packetData)) {\n const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length);\n const type = extractNalUnitTypeForAvc(nalUnit[0]);\n if (type === AvcNalUnitType.SPS) {\n spsUnits.push(nalUnit);\n } else if (type === AvcNalUnitType.PPS) {\n ppsUnits.push(nalUnit);\n } else if (type === AvcNalUnitType.SPS_EXT) {\n spsExtUnits.push(nalUnit);\n }\n }\n if (spsUnits.length === 0) {\n return null;\n }\n if (ppsUnits.length === 0) {\n return null;\n }\n const spsData = spsUnits[0];\n const spsInfo = parseAvcSps(spsData);\n assert(spsInfo !== null);\n const hasExtendedData = spsInfo.profileIdc === 100 || spsInfo.profileIdc === 110 || spsInfo.profileIdc === 122 || spsInfo.profileIdc === 144;\n return {\n configurationVersion: 1,\n avcProfileIndication: spsInfo.profileIdc,\n profileCompatibility: spsInfo.constraintFlags,\n avcLevelIndication: spsInfo.levelIdc,\n lengthSizeMinusOne: 3,\n sequenceParameterSets: spsUnits,\n pictureParameterSets: ppsUnits,\n chromaFormat: hasExtendedData ? spsInfo.chromaFormatIdc : null,\n bitDepthLumaMinus8: hasExtendedData ? spsInfo.bitDepthLumaMinus8 : null,\n bitDepthChromaMinus8: hasExtendedData ? spsInfo.bitDepthChromaMinus8 : null,\n sequenceParameterSetExt: hasExtendedData ? spsExtUnits : null\n };\n } catch (error) {\n Logging._error(\"Error building AVC Decoder Configuration Record:\", error);\n return null;\n }\n };\n var serializeAvcDecoderConfigurationRecord = (record) => {\n const bytes = [];\n bytes.push(record.configurationVersion);\n bytes.push(record.avcProfileIndication);\n bytes.push(record.profileCompatibility);\n bytes.push(record.avcLevelIndication);\n bytes.push(252 | record.lengthSizeMinusOne & 3);\n bytes.push(224 | record.sequenceParameterSets.length & 31);\n for (const sps of record.sequenceParameterSets) {\n const length = sps.byteLength;\n bytes.push(length >> 8);\n bytes.push(length & 255);\n for (let i = 0;i < length; i++) {\n bytes.push(sps[i]);\n }\n }\n bytes.push(record.pictureParameterSets.length);\n for (const pps of record.pictureParameterSets) {\n const length = pps.byteLength;\n bytes.push(length >> 8);\n bytes.push(length & 255);\n for (let i = 0;i < length; i++) {\n bytes.push(pps[i]);\n }\n }\n if ((record.avcProfileIndication === 100 || record.avcProfileIndication === 110 || record.avcProfileIndication === 122 || record.avcProfileIndication === 144) && record.chromaFormat !== null) {\n assert(record.bitDepthLumaMinus8 !== null);\n assert(record.bitDepthChromaMinus8 !== null);\n assert(record.sequenceParameterSetExt !== null);\n bytes.push(252 | record.chromaFormat & 3);\n bytes.push(248 | record.bitDepthLumaMinus8 & 7);\n bytes.push(248 | record.bitDepthChromaMinus8 & 7);\n bytes.push(record.sequenceParameterSetExt.length);\n for (const spsExt of record.sequenceParameterSetExt) {\n const length = spsExt.byteLength;\n bytes.push(length >> 8);\n bytes.push(length & 255);\n for (let i = 0;i < length; i++) {\n bytes.push(spsExt[i]);\n }\n }\n }\n return new Uint8Array(bytes);\n };\n var AVC_HEVC_ASPECT_RATIO_IDC_TABLE = {\n 1: { num: 1, den: 1 },\n 2: { num: 12, den: 11 },\n 3: { num: 10, den: 11 },\n 4: { num: 16, den: 11 },\n 5: { num: 40, den: 33 },\n 6: { num: 24, den: 11 },\n 7: { num: 20, den: 11 },\n 8: { num: 32, den: 11 },\n 9: { num: 80, den: 33 },\n 10: { num: 18, den: 11 },\n 11: { num: 15, den: 11 },\n 12: { num: 64, den: 33 },\n 13: { num: 160, den: 99 },\n 14: { num: 4, den: 3 },\n 15: { num: 3, den: 2 },\n 16: { num: 2, den: 1 }\n };\n var parseAvcSps = (sps) => {\n try {\n const emulationUnpreventedBytes = removeEmulationPreventionBytes(sps);\n const bitstream = new Bitstream(emulationUnpreventedBytes);\n bitstream.skipBits(1);\n bitstream.skipBits(2);\n const nalUnitType = bitstream.readBits(5);\n if (nalUnitType !== 7) {\n return null;\n }\n const profileIdc = bitstream.readAlignedByte();\n const constraintFlags = bitstream.readAlignedByte();\n const levelIdc = bitstream.readAlignedByte();\n readExpGolomb(bitstream);\n let chromaFormatIdc = 1;\n let bitDepthLumaMinus8 = 0;\n let bitDepthChromaMinus8 = 0;\n let separateColourPlaneFlag = 0;\n if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {\n chromaFormatIdc = readExpGolomb(bitstream);\n if (chromaFormatIdc === 3) {\n separateColourPlaneFlag = bitstream.readBits(1);\n }\n bitDepthLumaMinus8 = readExpGolomb(bitstream);\n bitDepthChromaMinus8 = readExpGolomb(bitstream);\n bitstream.skipBits(1);\n const seqScalingMatrixPresentFlag = bitstream.readBits(1);\n if (seqScalingMatrixPresentFlag) {\n for (let i = 0;i < (chromaFormatIdc !== 3 ? 8 : 12); i++) {\n const seqScalingListPresentFlag = bitstream.readBits(1);\n if (seqScalingListPresentFlag) {\n const sizeOfScalingList = i < 6 ? 16 : 64;\n let lastScale = 8;\n let nextScale = 8;\n for (let j = 0;j < sizeOfScalingList; j++) {\n if (nextScale !== 0) {\n const deltaScale = readSignedExpGolomb(bitstream);\n nextScale = (lastScale + deltaScale + 256) % 256;\n }\n lastScale = nextScale === 0 ? lastScale : nextScale;\n }\n }\n }\n }\n }\n readExpGolomb(bitstream);\n const picOrderCntType = readExpGolomb(bitstream);\n if (picOrderCntType === 0) {\n readExpGolomb(bitstream);\n } else if (picOrderCntType === 1) {\n bitstream.skipBits(1);\n readSignedExpGolomb(bitstream);\n readSignedExpGolomb(bitstream);\n const numRefFramesInPicOrderCntCycle = readExpGolomb(bitstream);\n for (let i = 0;i < numRefFramesInPicOrderCntCycle; i++) {\n readSignedExpGolomb(bitstream);\n }\n }\n readExpGolomb(bitstream);\n bitstream.skipBits(1);\n const picWidthInMbsMinus1 = readExpGolomb(bitstream);\n const picHeightInMapUnitsMinus1 = readExpGolomb(bitstream);\n const codedWidth = 16 * (picWidthInMbsMinus1 + 1);\n const codedHeight = 16 * (picHeightInMapUnitsMinus1 + 1);\n let displayWidth = codedWidth;\n let displayHeight = codedHeight;\n const frameMbsOnlyFlag = bitstream.readBits(1);\n if (!frameMbsOnlyFlag) {\n bitstream.skipBits(1);\n }\n bitstream.skipBits(1);\n const frameCroppingFlag = bitstream.readBits(1);\n if (frameCroppingFlag) {\n const frameCropLeftOffset = readExpGolomb(bitstream);\n const frameCropRightOffset = readExpGolomb(bitstream);\n const frameCropTopOffset = readExpGolomb(bitstream);\n const frameCropBottomOffset = readExpGolomb(bitstream);\n let cropUnitX;\n let cropUnitY;\n const chromaArrayType = separateColourPlaneFlag === 0 ? chromaFormatIdc : 0;\n if (chromaArrayType === 0) {\n cropUnitX = 1;\n cropUnitY = 2 - frameMbsOnlyFlag;\n } else {\n const subWidthC = chromaFormatIdc === 3 ? 1 : 2;\n const subHeightC = chromaFormatIdc === 1 ? 2 : 1;\n cropUnitX = subWidthC;\n cropUnitY = subHeightC * (2 - frameMbsOnlyFlag);\n }\n displayWidth -= cropUnitX * (frameCropLeftOffset + frameCropRightOffset);\n displayHeight -= cropUnitY * (frameCropTopOffset + frameCropBottomOffset);\n }\n let colourPrimaries = 2;\n let transferCharacteristics = 2;\n let matrixCoefficients = 2;\n let fullRangeFlag = 0;\n let pixelAspectRatio = { num: 1, den: 1 };\n let numReorderFrames = null;\n let maxDecFrameBuffering = null;\n let bitstreamRestrictionFlagBitOffset = null;\n let bitstreamRestrictionFlag = null;\n const vuiParametersFlagBitOffset = bitstream.pos;\n const vuiParametersPresentFlag = bitstream.readBits(1);\n if (vuiParametersPresentFlag) {\n const aspectRatioInfoPresentFlag = bitstream.readBits(1);\n if (aspectRatioInfoPresentFlag) {\n const aspectRatioIdc = bitstream.readBits(8);\n if (aspectRatioIdc === 255) {\n pixelAspectRatio = {\n num: bitstream.readBits(16),\n den: bitstream.readBits(16)\n };\n } else {\n const aspectRatio = AVC_HEVC_ASPECT_RATIO_IDC_TABLE[aspectRatioIdc];\n if (aspectRatio) {\n pixelAspectRatio = aspectRatio;\n }\n }\n }\n const overscanInfoPresentFlag = bitstream.readBits(1);\n if (overscanInfoPresentFlag) {\n bitstream.skipBits(1);\n }\n const videoSignalTypePresentFlag = bitstream.readBits(1);\n if (videoSignalTypePresentFlag) {\n bitstream.skipBits(3);\n fullRangeFlag = bitstream.readBits(1);\n const colourDescriptionPresentFlag = bitstream.readBits(1);\n if (colourDescriptionPresentFlag) {\n colourPrimaries = bitstream.readBits(8);\n transferCharacteristics = bitstream.readBits(8);\n matrixCoefficients = bitstream.readBits(8);\n }\n }\n const chromaLocInfoPresentFlag = bitstream.readBits(1);\n if (chromaLocInfoPresentFlag) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n const timingInfoPresentFlag = bitstream.readBits(1);\n if (timingInfoPresentFlag) {\n bitstream.skipBits(32);\n bitstream.skipBits(32);\n bitstream.skipBits(1);\n }\n const nalHrdParametersPresentFlag = bitstream.readBits(1);\n if (nalHrdParametersPresentFlag) {\n skipAvcHrdParameters(bitstream);\n }\n const vclHrdParametersPresentFlag = bitstream.readBits(1);\n if (vclHrdParametersPresentFlag) {\n skipAvcHrdParameters(bitstream);\n }\n if (nalHrdParametersPresentFlag || vclHrdParametersPresentFlag) {\n bitstream.skipBits(1);\n }\n bitstream.skipBits(1);\n bitstreamRestrictionFlagBitOffset = bitstream.pos;\n bitstreamRestrictionFlag = bitstream.readBits(1);\n if (bitstreamRestrictionFlag) {\n bitstream.skipBits(1);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n numReorderFrames = readExpGolomb(bitstream);\n maxDecFrameBuffering = readExpGolomb(bitstream);\n }\n }\n if (numReorderFrames === null) {\n assert(maxDecFrameBuffering === null);\n const constraintSet3Flag = constraintFlags & 16;\n if ((profileIdc === 44 || profileIdc === 86 || profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244) && constraintSet3Flag) {\n numReorderFrames = 0;\n maxDecFrameBuffering = 0;\n } else {\n const picWidthInMbs = picWidthInMbsMinus1 + 1;\n const picHeightInMapUnits = picHeightInMapUnitsMinus1 + 1;\n const frameHeightInMbs = (2 - frameMbsOnlyFlag) * picHeightInMapUnits;\n const levelInfo = AVC_LEVEL_TABLE.find((x) => x.level >= levelIdc) ?? last(AVC_LEVEL_TABLE);\n const maxDpbFrames = Math.min(Math.floor(levelInfo.maxDpbMbs / (picWidthInMbs * frameHeightInMbs)), 16);\n numReorderFrames = maxDpbFrames;\n maxDecFrameBuffering = maxDpbFrames;\n }\n }\n assert(maxDecFrameBuffering !== null);\n return {\n emulationUnpreventedBytes,\n profileIdc,\n constraintFlags,\n levelIdc,\n frameMbsOnlyFlag,\n chromaFormatIdc,\n bitDepthLumaMinus8,\n bitDepthChromaMinus8,\n codedWidth,\n codedHeight,\n displayWidth,\n displayHeight,\n pixelAspectRatio,\n colourPrimaries,\n matrixCoefficients,\n transferCharacteristics,\n fullRangeFlag,\n numReorderFrames,\n maxDecFrameBuffering,\n vuiParametersFlagBitOffset,\n bitstreamRestrictionFlagBitOffset,\n bitstreamRestrictionFlag\n };\n } catch (error) {\n Logging._error(\"Error parsing AVC SPS:\", error);\n return null;\n }\n };\n var skipAvcHrdParameters = (bitstream) => {\n const cpb_cnt_minus1 = readExpGolomb(bitstream);\n bitstream.skipBits(4);\n bitstream.skipBits(4);\n for (let i = 0;i <= cpb_cnt_minus1; i++) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n bitstream.skipBits(1);\n }\n bitstream.skipBits(5);\n bitstream.skipBits(5);\n bitstream.skipBits(5);\n bitstream.skipBits(5);\n };\n var iterateHevcNalUnits = (packetData, decoderConfig) => {\n if (decoderConfig.description) {\n const bytes = toUint8Array(decoderConfig.description);\n const lengthSizeMinusOne = bytes[21] & 3;\n const lengthSize = lengthSizeMinusOne + 1;\n return iterateNalUnitsInLengthPrefixed(packetData, lengthSize);\n } else {\n return iterateNalUnitsInAnnexB(packetData);\n }\n };\n var extractNalUnitTypeForHevc = (byte) => {\n return byte >> 1 & 63;\n };\n var parseHevcSps = (sps) => {\n try {\n const bitstream = new Bitstream(removeEmulationPreventionBytes(sps));\n bitstream.skipBits(16);\n bitstream.readBits(4);\n const spsMaxSubLayersMinus1 = bitstream.readBits(3);\n const spsTemporalIdNestingFlag = bitstream.readBits(1);\n const { general_profile_space, general_tier_flag, general_profile_idc, general_profile_compatibility_flags, general_constraint_indicator_flags, general_level_idc } = parseProfileTierLevel(bitstream, spsMaxSubLayersMinus1);\n readExpGolomb(bitstream);\n const chromaFormatIdc = readExpGolomb(bitstream);\n let separateColourPlaneFlag = 0;\n if (chromaFormatIdc === 3) {\n separateColourPlaneFlag = bitstream.readBits(1);\n }\n const picWidthInLumaSamples = readExpGolomb(bitstream);\n const picHeightInLumaSamples = readExpGolomb(bitstream);\n let displayWidth = picWidthInLumaSamples;\n let displayHeight = picHeightInLumaSamples;\n if (bitstream.readBits(1)) {\n const confWinLeftOffset = readExpGolomb(bitstream);\n const confWinRightOffset = readExpGolomb(bitstream);\n const confWinTopOffset = readExpGolomb(bitstream);\n const confWinBottomOffset = readExpGolomb(bitstream);\n let subWidthC = 1;\n let subHeightC = 1;\n const chromaArrayType = separateColourPlaneFlag === 0 ? chromaFormatIdc : 0;\n if (chromaArrayType === 1) {\n subWidthC = 2;\n subHeightC = 2;\n } else if (chromaArrayType === 2) {\n subWidthC = 2;\n subHeightC = 1;\n }\n displayWidth -= (confWinLeftOffset + confWinRightOffset) * subWidthC;\n displayHeight -= (confWinTopOffset + confWinBottomOffset) * subHeightC;\n }\n const bitDepthLumaMinus8 = readExpGolomb(bitstream);\n const bitDepthChromaMinus8 = readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n const spsSubLayerOrderingInfoPresentFlag = bitstream.readBits(1);\n const startI = spsSubLayerOrderingInfoPresentFlag ? 0 : spsMaxSubLayersMinus1;\n let spsMaxNumReorderPics = 0;\n for (let i = startI;i <= spsMaxSubLayersMinus1; i++) {\n readExpGolomb(bitstream);\n spsMaxNumReorderPics = readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n if (bitstream.readBits(1)) {\n if (bitstream.readBits(1)) {\n skipScalingListData(bitstream);\n }\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n if (bitstream.readBits(1)) {\n bitstream.skipBits(4);\n bitstream.skipBits(4);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n bitstream.skipBits(1);\n }\n const numShortTermRefPicSets = readExpGolomb(bitstream);\n skipAllStRefPicSets(bitstream, numShortTermRefPicSets);\n if (bitstream.readBits(1)) {\n const numLongTermRefPicsSps = readExpGolomb(bitstream);\n for (let i = 0;i < numLongTermRefPicsSps; i++) {\n readExpGolomb(bitstream);\n bitstream.skipBits(1);\n }\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n let colourPrimaries = 2;\n let transferCharacteristics = 2;\n let matrixCoefficients = 2;\n let fullRangeFlag = 0;\n let minSpatialSegmentationIdc = 0;\n let pixelAspectRatio = { num: 1, den: 1 };\n if (bitstream.readBits(1)) {\n const vui = parseHevcVui(bitstream, spsMaxSubLayersMinus1);\n pixelAspectRatio = vui.pixelAspectRatio;\n colourPrimaries = vui.colourPrimaries;\n transferCharacteristics = vui.transferCharacteristics;\n matrixCoefficients = vui.matrixCoefficients;\n fullRangeFlag = vui.fullRangeFlag;\n minSpatialSegmentationIdc = vui.minSpatialSegmentationIdc;\n }\n return {\n displayWidth,\n displayHeight,\n pixelAspectRatio,\n colourPrimaries,\n transferCharacteristics,\n matrixCoefficients,\n fullRangeFlag,\n maxDecFrameBuffering: spsMaxNumReorderPics + 1,\n spsMaxSubLayersMinus1,\n spsTemporalIdNestingFlag,\n generalProfileSpace: general_profile_space,\n generalTierFlag: general_tier_flag,\n generalProfileIdc: general_profile_idc,\n generalProfileCompatibilityFlags: general_profile_compatibility_flags,\n generalConstraintIndicatorFlags: general_constraint_indicator_flags,\n generalLevelIdc: general_level_idc,\n chromaFormatIdc,\n bitDepthLumaMinus8,\n bitDepthChromaMinus8,\n minSpatialSegmentationIdc\n };\n } catch (error) {\n Logging._error(\"Error parsing HEVC SPS:\", error);\n return null;\n }\n };\n var extractHevcDecoderConfigurationRecord = (packetData) => {\n try {\n const vpsUnits = [];\n const spsUnits = [];\n const ppsUnits = [];\n const seiUnits = [];\n for (const loc of iterateNalUnitsInAnnexB(packetData)) {\n const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length);\n const type = extractNalUnitTypeForHevc(nalUnit[0]);\n if (type === HevcNalUnitType.VPS_NUT) {\n vpsUnits.push(nalUnit);\n } else if (type === HevcNalUnitType.SPS_NUT) {\n spsUnits.push(nalUnit);\n } else if (type === HevcNalUnitType.PPS_NUT) {\n ppsUnits.push(nalUnit);\n } else if (type === HevcNalUnitType.PREFIX_SEI_NUT || type === HevcNalUnitType.SUFFIX_SEI_NUT) {\n seiUnits.push(nalUnit);\n }\n }\n if (spsUnits.length === 0 || ppsUnits.length === 0)\n return null;\n const spsInfo = parseHevcSps(spsUnits[0]);\n if (!spsInfo)\n return null;\n let parallelismType = 0;\n if (ppsUnits.length > 0) {\n const pps = ppsUnits[0];\n const ppsBitstream = new Bitstream(removeEmulationPreventionBytes(pps));\n ppsBitstream.skipBits(16);\n readExpGolomb(ppsBitstream);\n readExpGolomb(ppsBitstream);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(3);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n readExpGolomb(ppsBitstream);\n readExpGolomb(ppsBitstream);\n readSignedExpGolomb(ppsBitstream);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n if (ppsBitstream.readBits(1)) {\n readExpGolomb(ppsBitstream);\n }\n readSignedExpGolomb(ppsBitstream);\n readSignedExpGolomb(ppsBitstream);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n const tiles_enabled_flag = ppsBitstream.readBits(1);\n const entropy_coding_sync_enabled_flag = ppsBitstream.readBits(1);\n if (!tiles_enabled_flag && !entropy_coding_sync_enabled_flag)\n parallelismType = 0;\n else if (tiles_enabled_flag && !entropy_coding_sync_enabled_flag)\n parallelismType = 2;\n else if (!tiles_enabled_flag && entropy_coding_sync_enabled_flag)\n parallelismType = 3;\n else\n parallelismType = 0;\n }\n const arrays = [\n ...vpsUnits.length ? [\n {\n arrayCompleteness: 1,\n nalUnitType: HevcNalUnitType.VPS_NUT,\n nalUnits: vpsUnits\n }\n ] : [],\n ...spsUnits.length ? [\n {\n arrayCompleteness: 1,\n nalUnitType: HevcNalUnitType.SPS_NUT,\n nalUnits: spsUnits\n }\n ] : [],\n ...ppsUnits.length ? [\n {\n arrayCompleteness: 1,\n nalUnitType: HevcNalUnitType.PPS_NUT,\n nalUnits: ppsUnits\n }\n ] : [],\n ...seiUnits.length ? [\n {\n arrayCompleteness: 1,\n nalUnitType: extractNalUnitTypeForHevc(seiUnits[0][0]),\n nalUnits: seiUnits\n }\n ] : []\n ];\n const record = {\n configurationVersion: 1,\n generalProfileSpace: spsInfo.generalProfileSpace,\n generalTierFlag: spsInfo.generalTierFlag,\n generalProfileIdc: spsInfo.generalProfileIdc,\n generalProfileCompatibilityFlags: spsInfo.generalProfileCompatibilityFlags,\n generalConstraintIndicatorFlags: spsInfo.generalConstraintIndicatorFlags,\n generalLevelIdc: spsInfo.generalLevelIdc,\n minSpatialSegmentationIdc: spsInfo.minSpatialSegmentationIdc,\n parallelismType,\n chromaFormatIdc: spsInfo.chromaFormatIdc,\n bitDepthLumaMinus8: spsInfo.bitDepthLumaMinus8,\n bitDepthChromaMinus8: spsInfo.bitDepthChromaMinus8,\n avgFrameRate: 0,\n constantFrameRate: 0,\n numTemporalLayers: spsInfo.spsMaxSubLayersMinus1 + 1,\n temporalIdNested: spsInfo.spsTemporalIdNestingFlag,\n lengthSizeMinusOne: 3,\n arrays\n };\n return record;\n } catch (error) {\n Logging._error(\"Error building HEVC Decoder Configuration Record:\", error);\n return null;\n }\n };\n var parseProfileTierLevel = (bitstream, maxNumSubLayersMinus1) => {\n const general_profile_space = bitstream.readBits(2);\n const general_tier_flag = bitstream.readBits(1);\n const general_profile_idc = bitstream.readBits(5);\n let general_profile_compatibility_flags = 0;\n for (let i = 0;i < 32; i++) {\n general_profile_compatibility_flags = general_profile_compatibility_flags << 1 | bitstream.readBits(1);\n }\n const general_constraint_indicator_flags = new Uint8Array(6);\n for (let i = 0;i < 6; i++) {\n general_constraint_indicator_flags[i] = bitstream.readBits(8);\n }\n const general_level_idc = bitstream.readBits(8);\n const sub_layer_profile_present_flag = [];\n const sub_layer_level_present_flag = [];\n for (let i = 0;i < maxNumSubLayersMinus1; i++) {\n sub_layer_profile_present_flag.push(bitstream.readBits(1));\n sub_layer_level_present_flag.push(bitstream.readBits(1));\n }\n if (maxNumSubLayersMinus1 > 0) {\n for (let i = maxNumSubLayersMinus1;i < 8; i++) {\n bitstream.skipBits(2);\n }\n }\n for (let i = 0;i < maxNumSubLayersMinus1; i++) {\n if (sub_layer_profile_present_flag[i])\n bitstream.skipBits(88);\n if (sub_layer_level_present_flag[i])\n bitstream.skipBits(8);\n }\n return {\n general_profile_space,\n general_tier_flag,\n general_profile_idc,\n general_profile_compatibility_flags,\n general_constraint_indicator_flags,\n general_level_idc\n };\n };\n var skipScalingListData = (bitstream) => {\n for (let sizeId = 0;sizeId < 4; sizeId++) {\n for (let matrixId = 0;matrixId < (sizeId === 3 ? 2 : 6); matrixId++) {\n const scaling_list_pred_mode_flag = bitstream.readBits(1);\n if (!scaling_list_pred_mode_flag) {\n readExpGolomb(bitstream);\n } else {\n const coefNum = Math.min(64, 1 << 4 + (sizeId << 1));\n if (sizeId > 1) {\n readSignedExpGolomb(bitstream);\n }\n for (let i = 0;i < coefNum; i++) {\n readSignedExpGolomb(bitstream);\n }\n }\n }\n }\n };\n var skipAllStRefPicSets = (bitstream, num_short_term_ref_pic_sets) => {\n const NumDeltaPocs = [];\n for (let stRpsIdx = 0;stRpsIdx < num_short_term_ref_pic_sets; stRpsIdx++) {\n NumDeltaPocs[stRpsIdx] = skipStRefPicSet(bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs);\n }\n };\n var skipStRefPicSet = (bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs) => {\n let NumDeltaPocsThis = 0;\n let inter_ref_pic_set_prediction_flag = 0;\n let RefRpsIdx = 0;\n if (stRpsIdx !== 0) {\n inter_ref_pic_set_prediction_flag = bitstream.readBits(1);\n }\n if (inter_ref_pic_set_prediction_flag) {\n if (stRpsIdx === num_short_term_ref_pic_sets) {\n const delta_idx_minus1 = readExpGolomb(bitstream);\n RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1);\n } else {\n RefRpsIdx = stRpsIdx - 1;\n }\n bitstream.readBits(1);\n readExpGolomb(bitstream);\n const numDelta = NumDeltaPocs[RefRpsIdx] ?? 0;\n for (let j = 0;j <= numDelta; j++) {\n const used_by_curr_pic_flag = bitstream.readBits(1);\n if (!used_by_curr_pic_flag) {\n bitstream.readBits(1);\n }\n }\n NumDeltaPocsThis = NumDeltaPocs[RefRpsIdx];\n } else {\n const num_negative_pics = readExpGolomb(bitstream);\n const num_positive_pics = readExpGolomb(bitstream);\n for (let i = 0;i < num_negative_pics; i++) {\n readExpGolomb(bitstream);\n bitstream.readBits(1);\n }\n for (let i = 0;i < num_positive_pics; i++) {\n readExpGolomb(bitstream);\n bitstream.readBits(1);\n }\n NumDeltaPocsThis = num_negative_pics + num_positive_pics;\n }\n return NumDeltaPocsThis;\n };\n var parseHevcVui = (bitstream, sps_max_sub_layers_minus1) => {\n let colourPrimaries = 2;\n let transferCharacteristics = 2;\n let matrixCoefficients = 2;\n let fullRangeFlag = 0;\n let minSpatialSegmentationIdc = 0;\n let pixelAspectRatio = { num: 1, den: 1 };\n if (bitstream.readBits(1)) {\n const aspect_ratio_idc = bitstream.readBits(8);\n if (aspect_ratio_idc === 255) {\n pixelAspectRatio = {\n num: bitstream.readBits(16),\n den: bitstream.readBits(16)\n };\n } else {\n const aspectRatio = AVC_HEVC_ASPECT_RATIO_IDC_TABLE[aspect_ratio_idc];\n if (aspectRatio) {\n pixelAspectRatio = aspectRatio;\n }\n }\n }\n if (bitstream.readBits(1)) {\n bitstream.readBits(1);\n }\n if (bitstream.readBits(1)) {\n bitstream.readBits(3);\n fullRangeFlag = bitstream.readBits(1);\n if (bitstream.readBits(1)) {\n colourPrimaries = bitstream.readBits(8);\n transferCharacteristics = bitstream.readBits(8);\n matrixCoefficients = bitstream.readBits(8);\n }\n }\n if (bitstream.readBits(1)) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n bitstream.readBits(1);\n bitstream.readBits(1);\n bitstream.readBits(1);\n if (bitstream.readBits(1)) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n if (bitstream.readBits(1)) {\n bitstream.readBits(32);\n bitstream.readBits(32);\n if (bitstream.readBits(1)) {\n readExpGolomb(bitstream);\n }\n if (bitstream.readBits(1)) {\n skipHevcHrdParameters(bitstream, true, sps_max_sub_layers_minus1);\n }\n }\n if (bitstream.readBits(1)) {\n bitstream.readBits(1);\n bitstream.readBits(1);\n bitstream.readBits(1);\n minSpatialSegmentationIdc = readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n return {\n pixelAspectRatio,\n colourPrimaries,\n transferCharacteristics,\n matrixCoefficients,\n fullRangeFlag,\n minSpatialSegmentationIdc\n };\n };\n var skipHevcHrdParameters = (bitstream, commonInfPresentFlag, maxNumSubLayersMinus1) => {\n let nal_hrd_parameters_present_flag = false;\n let vcl_hrd_parameters_present_flag = false;\n let sub_pic_hrd_params_present_flag = false;\n if (commonInfPresentFlag) {\n nal_hrd_parameters_present_flag = bitstream.readBits(1) === 1;\n vcl_hrd_parameters_present_flag = bitstream.readBits(1) === 1;\n if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {\n sub_pic_hrd_params_present_flag = bitstream.readBits(1) === 1;\n if (sub_pic_hrd_params_present_flag) {\n bitstream.readBits(8);\n bitstream.readBits(5);\n bitstream.readBits(1);\n bitstream.readBits(5);\n }\n bitstream.readBits(4);\n bitstream.readBits(4);\n if (sub_pic_hrd_params_present_flag) {\n bitstream.readBits(4);\n }\n bitstream.readBits(5);\n bitstream.readBits(5);\n bitstream.readBits(5);\n }\n }\n for (let i = 0;i <= maxNumSubLayersMinus1; i++) {\n const fixed_pic_rate_general_flag = bitstream.readBits(1) === 1;\n let fixed_pic_rate_within_cvs_flag = true;\n if (!fixed_pic_rate_general_flag) {\n fixed_pic_rate_within_cvs_flag = bitstream.readBits(1) === 1;\n }\n let low_delay_hrd_flag = false;\n if (fixed_pic_rate_within_cvs_flag) {\n readExpGolomb(bitstream);\n } else {\n low_delay_hrd_flag = bitstream.readBits(1) === 1;\n }\n let CpbCnt = 1;\n if (!low_delay_hrd_flag) {\n const cpb_cnt_minus1 = readExpGolomb(bitstream);\n CpbCnt = cpb_cnt_minus1 + 1;\n }\n if (nal_hrd_parameters_present_flag) {\n skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag);\n }\n if (vcl_hrd_parameters_present_flag) {\n skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag);\n }\n }\n };\n var skipSubLayerHrdParameters = (bitstream, CpbCnt, sub_pic_hrd_params_present_flag) => {\n for (let i = 0;i < CpbCnt; i++) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n if (sub_pic_hrd_params_present_flag) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n bitstream.readBits(1);\n }\n };\n var serializeHevcDecoderConfigurationRecord = (record) => {\n const bytes = [];\n bytes.push(record.configurationVersion);\n bytes.push((record.generalProfileSpace & 3) << 6 | (record.generalTierFlag & 1) << 5 | record.generalProfileIdc & 31);\n bytes.push(record.generalProfileCompatibilityFlags >>> 24 & 255);\n bytes.push(record.generalProfileCompatibilityFlags >>> 16 & 255);\n bytes.push(record.generalProfileCompatibilityFlags >>> 8 & 255);\n bytes.push(record.generalProfileCompatibilityFlags & 255);\n bytes.push(...record.generalConstraintIndicatorFlags);\n bytes.push(record.generalLevelIdc & 255);\n bytes.push(240 | record.minSpatialSegmentationIdc >> 8 & 15);\n bytes.push(record.minSpatialSegmentationIdc & 255);\n bytes.push(252 | record.parallelismType & 3);\n bytes.push(252 | record.chromaFormatIdc & 3);\n bytes.push(248 | record.bitDepthLumaMinus8 & 7);\n bytes.push(248 | record.bitDepthChromaMinus8 & 7);\n bytes.push(record.avgFrameRate >> 8 & 255);\n bytes.push(record.avgFrameRate & 255);\n bytes.push((record.constantFrameRate & 3) << 6 | (record.numTemporalLayers & 7) << 3 | (record.temporalIdNested & 1) << 2 | record.lengthSizeMinusOne & 3);\n bytes.push(record.arrays.length & 255);\n for (const arr of record.arrays) {\n bytes.push((arr.arrayCompleteness & 1) << 7 | 0 << 6 | arr.nalUnitType & 63);\n bytes.push(arr.nalUnits.length >> 8 & 255);\n bytes.push(arr.nalUnits.length & 255);\n for (const nal of arr.nalUnits) {\n bytes.push(nal.length >> 8 & 255);\n bytes.push(nal.length & 255);\n for (let i = 0;i < nal.length; i++) {\n bytes.push(nal[i]);\n }\n }\n }\n return new Uint8Array(bytes);\n };\n var HevcNaluOrderState;\n (function(HevcNaluOrderState) {\n HevcNaluOrderState[HevcNaluOrderState[\"audAllowed\"] = 0] = \"audAllowed\";\n HevcNaluOrderState[HevcNaluOrderState[\"beforeFirstVcl\"] = 1] = \"beforeFirstVcl\";\n HevcNaluOrderState[HevcNaluOrderState[\"afterFirstVcl\"] = 2] = \"afterFirstVcl\";\n HevcNaluOrderState[HevcNaluOrderState[\"eoBitstreamAllowed\"] = 3] = \"eoBitstreamAllowed\";\n HevcNaluOrderState[HevcNaluOrderState[\"noMoreDataAllowed\"] = 4] = \"noMoreDataAllowed\";\n })(HevcNaluOrderState || (HevcNaluOrderState = {}));\n var VP9_COLOR_SPACE_TABLE = {\n 1: { colourPrimaries: 5, transferCharacteristics: 6, matrixCoefficients: 5 },\n 2: { colourPrimaries: 1, transferCharacteristics: 1, matrixCoefficients: 1 },\n 3: { colourPrimaries: 6, transferCharacteristics: 6, matrixCoefficients: 6 },\n 4: { colourPrimaries: 7, transferCharacteristics: 7, matrixCoefficients: 7 },\n 5: { colourPrimaries: 9, transferCharacteristics: 14, matrixCoefficients: 9 },\n 7: { colourPrimaries: 1, transferCharacteristics: 13, matrixCoefficients: 0 }\n };\n var extractVp9CodecInfoFromPacket = (packet) => {\n const bitstream = new Bitstream(packet);\n const frameMarker = bitstream.readBits(2);\n if (frameMarker !== 2) {\n return null;\n }\n const profileLowBit = bitstream.readBits(1);\n const profileHighBit = bitstream.readBits(1);\n const profile = (profileHighBit << 1) + profileLowBit;\n if (profile === 3) {\n bitstream.skipBits(1);\n }\n const showExistingFrame = bitstream.readBits(1);\n if (showExistingFrame === 1) {\n return null;\n }\n const frameType = bitstream.readBits(1);\n if (frameType !== 0) {\n return null;\n }\n bitstream.skipBits(2);\n const syncCode = bitstream.readBits(24);\n if (syncCode !== 4817730) {\n return null;\n }\n let bitDepth = 8;\n if (profile >= 2) {\n const tenOrTwelveBit = bitstream.readBits(1);\n bitDepth = tenOrTwelveBit ? 12 : 10;\n }\n const colorSpace = bitstream.readBits(3);\n let chromaSubsampling = 0;\n let videoFullRangeFlag = 0;\n if (colorSpace !== 7) {\n const colorRange = bitstream.readBits(1);\n videoFullRangeFlag = colorRange;\n if (profile === 1 || profile === 3) {\n const subsamplingX = bitstream.readBits(1);\n const subsamplingY = bitstream.readBits(1);\n chromaSubsampling = !subsamplingX && !subsamplingY ? 3 : subsamplingX && !subsamplingY ? 2 : 1;\n bitstream.skipBits(1);\n } else {\n chromaSubsampling = 1;\n }\n } else {\n chromaSubsampling = 3;\n videoFullRangeFlag = 1;\n }\n const widthMinusOne = bitstream.readBits(16);\n const heightMinusOne = bitstream.readBits(16);\n const width = widthMinusOne + 1;\n const height = heightMinusOne + 1;\n const pictureSize = width * height;\n let level = last(VP9_LEVEL_TABLE).level;\n for (const entry of VP9_LEVEL_TABLE) {\n if (pictureSize <= entry.maxPictureSize) {\n level = entry.level;\n break;\n }\n }\n const colorSpaceValues = VP9_COLOR_SPACE_TABLE[colorSpace];\n const colourPrimaries = colorSpaceValues?.colourPrimaries ?? 2;\n const transferCharacteristics = colorSpaceValues?.transferCharacteristics ?? 2;\n const matrixCoefficients = colorSpaceValues?.matrixCoefficients ?? 2;\n return {\n profile,\n level,\n bitDepth,\n chromaSubsampling,\n videoFullRangeFlag,\n colourPrimaries,\n transferCharacteristics,\n matrixCoefficients\n };\n };\n var iterateAv1PacketObus = function* (packet) {\n const bitstream = new Bitstream(packet);\n const readLeb128 = () => {\n let value = 0;\n for (let i = 0;i < 8; i++) {\n const byte = bitstream.readAlignedByte();\n value += (byte & 127) * 2 ** (i * 7);\n if (!(byte & 128)) {\n break;\n }\n if (i === 7 && byte & 128) {\n return null;\n }\n }\n if (value > 2 ** 32 - 1) {\n return null;\n }\n return value;\n };\n while (bitstream.getBitsLeft() >= 8) {\n bitstream.skipBits(1);\n const obuType = bitstream.readBits(4);\n const obuExtension = bitstream.readBits(1);\n const obuHasSizeField = bitstream.readBits(1);\n bitstream.skipBits(1);\n if (obuExtension) {\n bitstream.skipBits(8);\n }\n let obuSize;\n if (obuHasSizeField) {\n const obuSizeValue = readLeb128();\n if (obuSizeValue === null)\n return;\n obuSize = obuSizeValue;\n } else {\n obuSize = Math.floor(bitstream.getBitsLeft() / 8);\n }\n assert(bitstream.pos % 8 === 0);\n yield {\n type: obuType,\n data: packet.subarray(bitstream.pos / 8, bitstream.pos / 8 + obuSize)\n };\n bitstream.skipBits(obuSize * 8);\n }\n };\n var extractAv1CodecInfoFromPacket = (packet) => {\n for (const { type, data } of iterateAv1PacketObus(packet)) {\n if (type !== 1) {\n continue;\n }\n const bitstream = new Bitstream(data);\n const seqProfile = bitstream.readBits(3);\n const stillPicture = bitstream.readBits(1);\n const reducedStillPictureHeader = bitstream.readBits(1);\n let seqLevel = 0;\n let seqTier = 0;\n let bufferDelayLengthMinus1 = 0;\n if (reducedStillPictureHeader) {\n seqLevel = bitstream.readBits(5);\n } else {\n const timingInfoPresentFlag = bitstream.readBits(1);\n let decoderModelInfoPresentFlag = 0;\n if (timingInfoPresentFlag) {\n bitstream.skipBits(32);\n bitstream.skipBits(32);\n const equalPictureInterval = bitstream.readBits(1);\n if (equalPictureInterval) {\n let leadingZeros = 0;\n while (leadingZeros < 32 && !bitstream.readBits(1)) {\n leadingZeros++;\n }\n if (leadingZeros < 32) {\n bitstream.skipBits(leadingZeros);\n }\n }\n decoderModelInfoPresentFlag = bitstream.readBits(1);\n if (decoderModelInfoPresentFlag) {\n bufferDelayLengthMinus1 = bitstream.readBits(5);\n bitstream.skipBits(32);\n bitstream.skipBits(5);\n bitstream.skipBits(5);\n }\n }\n const initialDisplayDelayPresentFlag = bitstream.readBits(1);\n const operatingPointsCntMinus1 = bitstream.readBits(5);\n for (let i = 0;i <= operatingPointsCntMinus1; i++) {\n bitstream.skipBits(12);\n const seqLevelIdx = bitstream.readBits(5);\n if (i === 0) {\n seqLevel = seqLevelIdx;\n }\n if (seqLevelIdx > 7) {\n const seqTierTemp = bitstream.readBits(1);\n if (i === 0) {\n seqTier = seqTierTemp;\n }\n }\n if (decoderModelInfoPresentFlag) {\n const decoderModelPresentForThisOp = bitstream.readBits(1);\n if (decoderModelPresentForThisOp) {\n const n = bufferDelayLengthMinus1 + 1;\n bitstream.skipBits(n);\n bitstream.skipBits(n);\n bitstream.skipBits(1);\n }\n }\n if (initialDisplayDelayPresentFlag) {\n const initialDisplayDelayPresentForThisOp = bitstream.readBits(1);\n if (initialDisplayDelayPresentForThisOp) {\n bitstream.skipBits(4);\n }\n }\n }\n }\n const frameWidthBitsMinus1 = bitstream.readBits(4);\n const frameHeightBitsMinus1 = bitstream.readBits(4);\n const n1 = frameWidthBitsMinus1 + 1;\n bitstream.skipBits(n1);\n const n2 = frameHeightBitsMinus1 + 1;\n bitstream.skipBits(n2);\n let frameIdNumbersPresentFlag = 0;\n if (reducedStillPictureHeader) {\n frameIdNumbersPresentFlag = 0;\n } else {\n frameIdNumbersPresentFlag = bitstream.readBits(1);\n }\n if (frameIdNumbersPresentFlag) {\n bitstream.skipBits(4);\n bitstream.skipBits(3);\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n if (!reducedStillPictureHeader) {\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n const enableOrderHint = bitstream.readBits(1);\n if (enableOrderHint) {\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n }\n const seqChooseScreenContentTools = bitstream.readBits(1);\n let seqForceScreenContentTools = 0;\n if (seqChooseScreenContentTools) {\n seqForceScreenContentTools = 2;\n } else {\n seqForceScreenContentTools = bitstream.readBits(1);\n }\n if (seqForceScreenContentTools > 0) {\n const seqChooseIntegerMv = bitstream.readBits(1);\n if (!seqChooseIntegerMv) {\n bitstream.skipBits(1);\n }\n }\n if (enableOrderHint) {\n bitstream.skipBits(3);\n }\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n const highBitdepth = bitstream.readBits(1);\n let bitDepth = 8;\n if (seqProfile === 2 && highBitdepth) {\n const twelveBit = bitstream.readBits(1);\n bitDepth = twelveBit ? 12 : 10;\n } else if (seqProfile <= 2) {\n bitDepth = highBitdepth ? 10 : 8;\n }\n let monochrome = 0;\n if (seqProfile !== 1) {\n monochrome = bitstream.readBits(1);\n }\n let colourPrimaries = 2;\n let transferCharacteristics = 2;\n let matrixCoefficients = 2;\n const colorDescriptionPresentFlag = bitstream.readBits(1);\n if (colorDescriptionPresentFlag) {\n colourPrimaries = bitstream.readBits(8);\n transferCharacteristics = bitstream.readBits(8);\n matrixCoefficients = bitstream.readBits(8);\n }\n let videoFullRangeFlag = 0;\n let chromaSubsamplingX = 1;\n let chromaSubsamplingY = 1;\n let chromaSamplePosition = 0;\n if (monochrome) {\n videoFullRangeFlag = bitstream.readBits(1);\n } else if (colourPrimaries === 1 && transferCharacteristics === 13 && matrixCoefficients === 0) {\n videoFullRangeFlag = 1;\n chromaSubsamplingX = 0;\n chromaSubsamplingY = 0;\n } else {\n videoFullRangeFlag = bitstream.readBits(1);\n if (seqProfile === 0) {\n chromaSubsamplingX = 1;\n chromaSubsamplingY = 1;\n } else if (seqProfile === 1) {\n chromaSubsamplingX = 0;\n chromaSubsamplingY = 0;\n } else {\n if (bitDepth === 12) {\n chromaSubsamplingX = bitstream.readBits(1);\n chromaSubsamplingY = chromaSubsamplingX ? bitstream.readBits(1) : 0;\n } else {\n chromaSubsamplingX = 1;\n chromaSubsamplingY = 0;\n }\n }\n if (chromaSubsamplingX && chromaSubsamplingY) {\n chromaSamplePosition = bitstream.readBits(2);\n }\n }\n return {\n profile: seqProfile,\n level: seqLevel,\n tier: seqTier,\n bitDepth,\n monochrome,\n chromaSubsamplingX,\n chromaSubsamplingY,\n chromaSamplePosition,\n videoFullRangeFlag,\n colourPrimaries,\n transferCharacteristics,\n matrixCoefficients\n };\n }\n return null;\n };\n var av1CodecInfoHasColorInfo = (info) => {\n return info.colourPrimaries !== 2 || info.transferCharacteristics !== 2 || info.matrixCoefficients !== 2;\n };\n var parseOpusIdentificationHeader = (bytes) => {\n const view = toDataView(bytes);\n const outputChannelCount = view.getUint8(9);\n const preSkip = view.getUint16(10, true);\n const inputSampleRate = view.getUint32(12, true);\n const outputGain = view.getInt16(16, true);\n const channelMappingFamily = view.getUint8(18);\n let channelMappingTable = null;\n if (channelMappingFamily) {\n channelMappingTable = bytes.subarray(19, 19 + 2 + outputChannelCount);\n }\n return {\n outputChannelCount,\n preSkip,\n inputSampleRate,\n outputGain,\n channelMappingFamily,\n channelMappingTable\n };\n };\n var determineVideoPacketType = (codec, decoderConfig, packetData) => {\n switch (codec) {\n case \"avc\":\n {\n for (const loc of iterateAvcNalUnits(packetData, decoderConfig)) {\n const nalTypeByte = packetData[loc.offset];\n const type = extractNalUnitTypeForAvc(nalTypeByte);\n if (type >= AvcNalUnitType.NON_IDR_SLICE && type <= AvcNalUnitType.SLICE_DPC) {\n return \"delta\";\n }\n if (type === AvcNalUnitType.IDR) {\n return \"key\";\n }\n if (type === AvcNalUnitType.SEI && !isChromium()) {\n const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length);\n const bytes = removeEmulationPreventionBytes(nalUnit);\n let pos = 1;\n do {\n let payloadType = 0;\n while (true) {\n const nextByte = bytes[pos++];\n if (nextByte === undefined)\n break;\n payloadType += nextByte;\n if (nextByte < 255) {\n break;\n }\n }\n let payloadSize = 0;\n while (true) {\n const nextByte = bytes[pos++];\n if (nextByte === undefined)\n break;\n payloadSize += nextByte;\n if (nextByte < 255) {\n break;\n }\n }\n const PAYLOAD_TYPE_RECOVERY_POINT = 6;\n if (payloadType === PAYLOAD_TYPE_RECOVERY_POINT) {\n const bitstream = new Bitstream(bytes);\n bitstream.pos = 8 * pos;\n const recoveryFrameCount = readExpGolomb(bitstream);\n const exactMatchFlag = bitstream.readBits(1);\n if (recoveryFrameCount === 0 && exactMatchFlag === 1) {\n return \"key\";\n }\n }\n pos += payloadSize;\n } while (pos < bytes.length - 1);\n }\n }\n return \"delta\";\n }\n ;\n case \"hevc\":\n {\n for (const loc of iterateHevcNalUnits(packetData, decoderConfig)) {\n const type = extractNalUnitTypeForHevc(packetData[loc.offset]);\n if (type < HevcNalUnitType.BLA_W_LP) {\n return \"delta\";\n }\n if (type <= HevcNalUnitType.RSV_IRAP_VCL23) {\n return \"key\";\n }\n }\n return \"delta\";\n }\n ;\n case \"vp8\":\n {\n const frameType = packetData[0] & 1;\n return frameType === 0 ? \"key\" : \"delta\";\n }\n ;\n case \"vp9\":\n {\n const bitstream = new Bitstream(packetData);\n if (bitstream.readBits(2) !== 2) {\n return null;\n }\n const profileLowBit = bitstream.readBits(1);\n const profileHighBit = bitstream.readBits(1);\n const profile = (profileHighBit << 1) + profileLowBit;\n if (profile === 3) {\n bitstream.skipBits(1);\n }\n const showExistingFrame = bitstream.readBits(1);\n if (showExistingFrame) {\n return null;\n }\n const frameType = bitstream.readBits(1);\n return frameType === 0 ? \"key\" : \"delta\";\n }\n ;\n case \"av1\":\n {\n let reducedStillPictureHeader = false;\n for (const { type, data } of iterateAv1PacketObus(packetData)) {\n if (type === 1) {\n const bitstream = new Bitstream(data);\n bitstream.skipBits(4);\n reducedStillPictureHeader = !!bitstream.readBits(1);\n } else if (type === 3 || type === 6 || type === 7) {\n if (reducedStillPictureHeader) {\n return \"key\";\n }\n const bitstream = new Bitstream(data);\n const showExistingFrame = bitstream.readBits(1);\n if (showExistingFrame) {\n return null;\n }\n const frameType = bitstream.readBits(2);\n return frameType === 0 ? \"key\" : \"delta\";\n }\n }\n return null;\n }\n ;\n case \"prores\":\n {\n return \"key\";\n }\n ;\n default:\n {\n assertNever(codec);\n assert(false);\n }\n ;\n }\n };\n var FlacBlockType;\n (function(FlacBlockType) {\n FlacBlockType[FlacBlockType[\"STREAMINFO\"] = 0] = \"STREAMINFO\";\n FlacBlockType[FlacBlockType[\"VORBIS_COMMENT\"] = 4] = \"VORBIS_COMMENT\";\n FlacBlockType[FlacBlockType[\"PICTURE\"] = 6] = \"PICTURE\";\n })(FlacBlockType || (FlacBlockType = {}));\n var parseAc3SyncFrame = (data) => {\n if (data.length < 7) {\n return null;\n }\n if (data[0] !== 11 || data[1] !== 119) {\n return null;\n }\n const bitstream = new Bitstream(data);\n bitstream.skipBits(16);\n bitstream.skipBits(16);\n const fscod = bitstream.readBits(2);\n if (fscod === 3) {\n return null;\n }\n const frmsizecod = bitstream.readBits(6);\n const bsid = bitstream.readBits(5);\n if (bsid > 8) {\n return null;\n }\n const bsmod = bitstream.readBits(3);\n const acmod = bitstream.readBits(3);\n if ((acmod & 1) !== 0 && acmod !== 1) {\n bitstream.skipBits(2);\n }\n if ((acmod & 4) !== 0) {\n bitstream.skipBits(2);\n }\n if (acmod === 2) {\n bitstream.skipBits(2);\n }\n const lfeon = bitstream.readBits(1);\n const bitRateCode = Math.floor(frmsizecod / 2);\n return { fscod, bsid, bsmod, acmod, lfeon, bitRateCode };\n };\n var AC3_FRAME_SIZES = [\n 64 * 2,\n 69 * 2,\n 96 * 2,\n 64 * 2,\n 70 * 2,\n 96 * 2,\n 80 * 2,\n 87 * 2,\n 120 * 2,\n 80 * 2,\n 88 * 2,\n 120 * 2,\n 96 * 2,\n 104 * 2,\n 144 * 2,\n 96 * 2,\n 105 * 2,\n 144 * 2,\n 112 * 2,\n 121 * 2,\n 168 * 2,\n 112 * 2,\n 122 * 2,\n 168 * 2,\n 128 * 2,\n 139 * 2,\n 192 * 2,\n 128 * 2,\n 140 * 2,\n 192 * 2,\n 160 * 2,\n 174 * 2,\n 240 * 2,\n 160 * 2,\n 175 * 2,\n 240 * 2,\n 192 * 2,\n 208 * 2,\n 288 * 2,\n 192 * 2,\n 209 * 2,\n 288 * 2,\n 224 * 2,\n 243 * 2,\n 336 * 2,\n 224 * 2,\n 244 * 2,\n 336 * 2,\n 256 * 2,\n 278 * 2,\n 384 * 2,\n 256 * 2,\n 279 * 2,\n 384 * 2,\n 320 * 2,\n 348 * 2,\n 480 * 2,\n 320 * 2,\n 349 * 2,\n 480 * 2,\n 384 * 2,\n 417 * 2,\n 576 * 2,\n 384 * 2,\n 418 * 2,\n 576 * 2,\n 448 * 2,\n 487 * 2,\n 672 * 2,\n 448 * 2,\n 488 * 2,\n 672 * 2,\n 512 * 2,\n 557 * 2,\n 768 * 2,\n 512 * 2,\n 558 * 2,\n 768 * 2,\n 640 * 2,\n 696 * 2,\n 960 * 2,\n 640 * 2,\n 697 * 2,\n 960 * 2,\n 768 * 2,\n 835 * 2,\n 1152 * 2,\n 768 * 2,\n 836 * 2,\n 1152 * 2,\n 896 * 2,\n 975 * 2,\n 1344 * 2,\n 896 * 2,\n 976 * 2,\n 1344 * 2,\n 1024 * 2,\n 1114 * 2,\n 1536 * 2,\n 1024 * 2,\n 1115 * 2,\n 1536 * 2,\n 1152 * 2,\n 1253 * 2,\n 1728 * 2,\n 1152 * 2,\n 1254 * 2,\n 1728 * 2,\n 1280 * 2,\n 1393 * 2,\n 1920 * 2,\n 1280 * 2,\n 1394 * 2,\n 1920 * 2\n ];\n var AC3_REGISTRATION_DESCRIPTOR = new Uint8Array([5, 4, 65, 67, 45, 51]);\n var EAC3_REGISTRATION_DESCRIPTOR = new Uint8Array([5, 4, 69, 65, 67, 51]);\n var EAC3_NUMBLKS_TABLE = [1, 2, 3, 6];\n var parseEac3SyncFrame = (data) => {\n if (data.length < 6) {\n return null;\n }\n if (data[0] !== 11 || data[1] !== 119) {\n return null;\n }\n const bitstream = new Bitstream(data);\n bitstream.skipBits(16);\n const strmtyp = bitstream.readBits(2);\n bitstream.skipBits(3);\n if (strmtyp !== 0 && strmtyp !== 2) {\n return null;\n }\n const frmsiz = bitstream.readBits(11);\n const fscod = bitstream.readBits(2);\n let fscod2 = 0;\n let numblkscod;\n if (fscod === 3) {\n fscod2 = bitstream.readBits(2);\n numblkscod = 3;\n } else {\n numblkscod = bitstream.readBits(2);\n }\n const acmod = bitstream.readBits(3);\n const lfeon = bitstream.readBits(1);\n const bsid = bitstream.readBits(5);\n if (bsid < 11 || bsid > 16) {\n return null;\n }\n const numblks = EAC3_NUMBLKS_TABLE[numblkscod];\n let fs;\n if (fscod < 3) {\n fs = AC3_SAMPLE_RATES[fscod] / 1000;\n } else {\n fs = EAC3_REDUCED_SAMPLE_RATES[fscod2] / 1000;\n }\n const dataRate = Math.round((frmsiz + 1) * fs / (numblks * 16));\n const bsmod = 0;\n const numDepSub = 0;\n const chanLoc = 0;\n const substream = {\n fscod,\n fscod2,\n bsid,\n bsmod,\n acmod,\n lfeon,\n numDepSub,\n chanLoc\n };\n return {\n dataRate,\n substreams: [substream]\n };\n };\n var DTS_EXSS_SYNC_WORD = 1683496997;\n var DTS_CORE_FRAME_HEADER_SIZE = 18;\n var DTS_EXSS_HEADER_PREFIX_SIZE = 10;\n var DTS_PCM_BLOCK_SAMPLES = 32;\n var DTS_SPECIFIC_BOX_SIZE = 20;\n var DTS_SUBBAND_SAMPLES = 8;\n var DTS_CORE_SAMPLE_RATES = [\n 0,\n 8000,\n 16000,\n 32000,\n 0,\n 0,\n 11025,\n 22050,\n 44100,\n 0,\n 0,\n 12000,\n 24000,\n 48000,\n 96000,\n 192000\n ];\n var DTS_CORE_BIT_RATES = [\n 32000,\n 56000,\n 64000,\n 96000,\n 112000,\n 128000,\n 192000,\n 224000,\n 256000,\n 320000,\n 384000,\n 448000,\n 512000,\n 576000,\n 640000,\n 768000,\n 960000,\n 1024000,\n 1152000,\n 1280000,\n 1344000,\n 1408000,\n 1411200,\n 1472000,\n 1536000,\n 1920000,\n 2048000,\n 3072000,\n 3840000,\n 0,\n 0,\n 0\n ];\n var DTS_PCM_RESOLUTIONS = [16, 16, 20, 20, 0, 24, 24, 0];\n var DTS_AMODE_CHANNEL_COUNTS = [1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8];\n var DTS_AMODE_CHANNEL_LAYOUTS = [\n 1,\n 2,\n 2,\n 2,\n 2,\n 3,\n 18,\n 19,\n 6,\n 7,\n 518,\n 323,\n 83,\n 519,\n 582,\n 535\n ];\n var DTS_CHANNEL_LAYOUT_LFE1 = 8;\n var DTS_EXSS_REF_CLOCKS = [32000, 44100, 48000, 0];\n var DTS_EXSS_SAMPLE_RATES = [\n 8000,\n 16000,\n 32000,\n 64000,\n 128000,\n 22050,\n 44100,\n 88200,\n 176400,\n 352800,\n 12000,\n 24000,\n 48000,\n 96000,\n 192000,\n 384000\n ];\n var DTS_SPECIFIC_BOX_FRAME_DURATIONS = [512, 1024, 2048, 4096];\n var parseDtsFrame = (data) => {\n const core = parseDtsCoreFrameHeader(data);\n const view = toDataView(data);\n let offset = core ? Math.ceil(core.frameSize / 4) * 4 : 0;\n let firstExss = null;\n while (offset + 4 <= data.length && view.getUint32(offset) === DTS_EXSS_SYNC_WORD) {\n const exss = parseDtsExssHeader(data.subarray(offset));\n if (!exss) {\n break;\n }\n firstExss ??= exss;\n offset += exss.frameSize;\n }\n if (core) {\n return {\n frameSize: firstExss ? offset : core.frameSize,\n sampleRate: core.sampleRate,\n numberOfChannels: core.numberOfChannels,\n sampleCount: core.sampleCount,\n channelLayout: core.channelLayout,\n pcmResolution: core.pcmResolution,\n bitRate: core.bitRate,\n core,\n hasExtensions: firstExss !== null\n };\n }\n if (!firstExss?.asset) {\n return null;\n }\n const { asset } = firstExss;\n return {\n frameSize: offset,\n sampleRate: asset.sampleRate,\n numberOfChannels: asset.numberOfChannels,\n sampleCount: asset.sampleCount,\n channelLayout: asset.channelLayout,\n pcmResolution: asset.pcmResolution,\n bitRate: 0,\n core: null,\n hasExtensions: true\n };\n };\n var parseDtsCoreFrameHeader = (data) => {\n if (data.length < DTS_CORE_FRAME_HEADER_SIZE) {\n return null;\n }\n if (data[0] !== 127 || data[1] !== 254 || data[2] !== 128 || data[3] !== 1) {\n return null;\n }\n const bitstream = new Bitstream(data);\n bitstream.skipBits(32);\n bitstream.skipBits(1);\n if (bitstream.readBits(5) !== DTS_PCM_BLOCK_SAMPLES - 1) {\n return null;\n }\n const cpf = bitstream.readBits(1);\n const npcmblocks = bitstream.readBits(7) + 1;\n if (npcmblocks % DTS_SUBBAND_SAMPLES !== 0) {\n return null;\n }\n const frameSize = bitstream.readBits(14) + 1;\n if (frameSize < 96) {\n return null;\n }\n const amode = bitstream.readBits(6);\n if (amode >= DTS_AMODE_CHANNEL_COUNTS.length) {\n return null;\n }\n const sampleRate = DTS_CORE_SAMPLE_RATES[bitstream.readBits(4)];\n if (sampleRate === 0) {\n return null;\n }\n const bitRate = DTS_CORE_BIT_RATES[bitstream.readBits(5)];\n if (bitstream.readBits(1) !== 0) {\n return null;\n }\n bitstream.skipBits(1 + 1 + 1 + 1);\n bitstream.skipBits(3 + 1 + 1);\n const lff = bitstream.readBits(2);\n if (lff === 3) {\n return null;\n }\n bitstream.skipBits(1);\n if (cpf) {\n bitstream.skipBits(16);\n }\n bitstream.skipBits(1 + 4 + 2);\n const pcmResolution = DTS_PCM_RESOLUTIONS[bitstream.readBits(3)];\n if (pcmResolution === 0) {\n return null;\n }\n const lfePresent = lff !== 0;\n return {\n frameSize,\n sampleRate,\n numberOfChannels: DTS_AMODE_CHANNEL_COUNTS[amode] + (lfePresent ? 1 : 0),\n sampleCount: npcmblocks * DTS_PCM_BLOCK_SAMPLES,\n channelLayout: DTS_AMODE_CHANNEL_LAYOUTS[amode] | (lfePresent ? DTS_CHANNEL_LAYOUT_LFE1 : 0),\n amode,\n lfePresent,\n bitRate,\n pcmResolution\n };\n };\n var parseDtsExssHeader = (data) => {\n if (data.length < DTS_EXSS_HEADER_PREFIX_SIZE) {\n return null;\n }\n if (data[0] !== 100 || data[1] !== 88 || data[2] !== 32 || data[3] !== 37) {\n return null;\n }\n const bitstream = new Bitstream(data);\n bitstream.skipBits(32);\n bitstream.skipBits(8);\n const extSsIndex = bitstream.readBits(2);\n const wideHeader = bitstream.readBits(1);\n const headerSizeBits = 8 + 4 * wideHeader;\n const frameSizeBits = 16 + 4 * wideHeader;\n bitstream.skipBits(headerSizeBits);\n const frameSize = bitstream.readBits(frameSizeBits) + 1;\n const incomplete = { frameSize, asset: null };\n if (!bitstream.readBits(1)) {\n return incomplete;\n }\n const refClock = DTS_EXSS_REF_CLOCKS[bitstream.readBits(2)];\n const frameDurationCycles = 512 * (bitstream.readBits(3) + 1);\n if (bitstream.readBits(1)) {\n bitstream.skipBits(32 + 4);\n }\n const numAudioPresentations = bitstream.readBits(3) + 1;\n const numAssets = bitstream.readBits(3) + 1;\n const activeExssMasks = [];\n for (let i = 0;i < numAudioPresentations; i++) {\n activeExssMasks.push(bitstream.readBits(extSsIndex + 1));\n }\n for (const mask of activeExssMasks) {\n bitstream.skipBits(8 * popcount(mask));\n }\n if (bitstream.readBits(1)) {\n bitstream.skipBits(2);\n const spkrMaskBits = bitstream.readBits(2) + 1 << 2;\n const numMixOutConfigs = bitstream.readBits(2) + 1;\n bitstream.skipBits(numMixOutConfigs * spkrMaskBits);\n }\n for (let i = 0;i < numAssets; i++) {\n bitstream.skipBits(frameSizeBits);\n }\n bitstream.skipBits(9);\n bitstream.skipBits(3);\n if (bitstream.readBits(1)) {\n bitstream.skipBits(4);\n }\n if (bitstream.readBits(1)) {\n bitstream.skipBits(24);\n }\n if (bitstream.readBits(1)) {\n bitstream.skipBits(8 * (bitstream.readBits(10) + 1));\n }\n const pcmResolution = bitstream.readBits(5) + 1;\n const sampleRate = DTS_EXSS_SAMPLE_RATES[bitstream.readBits(4)];\n const numberOfChannels = bitstream.readBits(8) + 1;\n let channelLayout = 0;\n if (bitstream.readBits(1)) {\n if (numberOfChannels > 2) {\n bitstream.skipBits(1);\n }\n if (numberOfChannels > 6) {\n bitstream.skipBits(1);\n }\n if (bitstream.readBits(1)) {\n const spkrMaskBits = bitstream.readBits(2) + 1 << 2;\n channelLayout = bitstream.readBits(spkrMaskBits);\n }\n }\n if (refClock === 0 || bitstream.getBitsLeft() < 0) {\n return incomplete;\n }\n return {\n frameSize,\n asset: {\n sampleRate,\n numberOfChannels,\n sampleCount: Math.round(frameDurationCycles * sampleRate / refClock),\n channelLayout,\n pcmResolution\n }\n };\n };\n var buildDtsSpecificBox = (frameInfo) => {\n const bytes = new Uint8Array(DTS_SPECIFIC_BOX_SIZE);\n const view = toDataView(bytes);\n view.setUint32(0, frameInfo.sampleRate);\n view.setUint32(4, frameInfo.bitRate);\n view.setUint32(8, frameInfo.bitRate);\n bytes[12] = frameInfo.pcmResolution;\n const streamConstruction = frameInfo.core && !frameInfo.hasExtensions ? 1 : 0;\n const bitstream = new Bitstream(bytes);\n bitstream.seekToByte(13);\n bitstream.writeBits(2, Math.max(DTS_SPECIFIC_BOX_FRAME_DURATIONS.indexOf(frameInfo.sampleCount), 0));\n bitstream.writeBits(5, streamConstruction);\n bitstream.writeBits(1, frameInfo.core?.lfePresent ? 1 : 0);\n bitstream.writeBits(6, frameInfo.core?.amode ?? 0);\n bitstream.writeBits(14, frameInfo.core ? frameInfo.core.frameSize - 1 : 0);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(3, 0);\n bitstream.writeBits(16, frameInfo.channelLayout);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(5, 0);\n return bytes;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/codec.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var VIDEO_CODECS = [\n \"avc\",\n \"hevc\",\n \"vp9\",\n \"av1\",\n \"vp8\",\n \"prores\"\n ];\n var PCM_AUDIO_CODECS = [\n \"pcm-s16\",\n \"pcm-s16be\",\n \"pcm-s24\",\n \"pcm-s24be\",\n \"pcm-s32\",\n \"pcm-s32be\",\n \"pcm-f32\",\n \"pcm-f32be\",\n \"pcm-f64\",\n \"pcm-f64be\",\n \"pcm-u8\",\n \"pcm-s8\",\n \"ulaw\",\n \"alaw\"\n ];\n var NON_PCM_AUDIO_CODECS = [\n \"aac\",\n \"opus\",\n \"mp3\",\n \"vorbis\",\n \"flac\",\n \"ac3\",\n \"eac3\",\n \"dts\"\n ];\n var AUDIO_CODECS = [\n ...NON_PCM_AUDIO_CODECS,\n ...PCM_AUDIO_CODECS\n ];\n var SUBTITLE_CODECS = [\n \"webvtt\"\n ];\n var AVC_LEVEL_TABLE = [\n { maxMacroblocks: 99, maxBitrate: 64000, maxDpbMbs: 396, level: 10 },\n { maxMacroblocks: 396, maxBitrate: 192000, maxDpbMbs: 900, level: 11 },\n { maxMacroblocks: 396, maxBitrate: 384000, maxDpbMbs: 2376, level: 12 },\n { maxMacroblocks: 396, maxBitrate: 768000, maxDpbMbs: 2376, level: 13 },\n { maxMacroblocks: 396, maxBitrate: 2000000, maxDpbMbs: 2376, level: 20 },\n { maxMacroblocks: 792, maxBitrate: 4000000, maxDpbMbs: 4752, level: 21 },\n { maxMacroblocks: 1620, maxBitrate: 4000000, maxDpbMbs: 8100, level: 22 },\n { maxMacroblocks: 1620, maxBitrate: 1e7, maxDpbMbs: 8100, level: 30 },\n { maxMacroblocks: 3600, maxBitrate: 14000000, maxDpbMbs: 18000, level: 31 },\n { maxMacroblocks: 5120, maxBitrate: 20000000, maxDpbMbs: 20480, level: 32 },\n { maxMacroblocks: 8192, maxBitrate: 20000000, maxDpbMbs: 32768, level: 40 },\n { maxMacroblocks: 8192, maxBitrate: 50000000, maxDpbMbs: 32768, level: 41 },\n { maxMacroblocks: 8704, maxBitrate: 50000000, maxDpbMbs: 34816, level: 42 },\n { maxMacroblocks: 22080, maxBitrate: 135000000, maxDpbMbs: 110400, level: 50 },\n { maxMacroblocks: 36864, maxBitrate: 240000000, maxDpbMbs: 184320, level: 51 },\n { maxMacroblocks: 36864, maxBitrate: 240000000, maxDpbMbs: 184320, level: 52 },\n { maxMacroblocks: 139264, maxBitrate: 240000000, maxDpbMbs: 696320, level: 60 },\n { maxMacroblocks: 139264, maxBitrate: 480000000, maxDpbMbs: 696320, level: 61 },\n { maxMacroblocks: 139264, maxBitrate: 800000000, maxDpbMbs: 696320, level: 62 }\n ];\n var HEVC_LEVEL_TABLE = [\n { maxPictureSize: 36864, maxBitrate: 128000, tier: \"L\", level: 30 },\n { maxPictureSize: 122880, maxBitrate: 1500000, tier: \"L\", level: 60 },\n { maxPictureSize: 245760, maxBitrate: 3000000, tier: \"L\", level: 63 },\n { maxPictureSize: 552960, maxBitrate: 6000000, tier: \"L\", level: 90 },\n { maxPictureSize: 983040, maxBitrate: 1e7, tier: \"L\", level: 93 },\n { maxPictureSize: 2228224, maxBitrate: 12000000, tier: \"L\", level: 120 },\n { maxPictureSize: 2228224, maxBitrate: 30000000, tier: \"H\", level: 120 },\n { maxPictureSize: 2228224, maxBitrate: 20000000, tier: \"L\", level: 123 },\n { maxPictureSize: 2228224, maxBitrate: 50000000, tier: \"H\", level: 123 },\n { maxPictureSize: 8912896, maxBitrate: 25000000, tier: \"L\", level: 150 },\n { maxPictureSize: 8912896, maxBitrate: 1e8, tier: \"H\", level: 150 },\n { maxPictureSize: 8912896, maxBitrate: 40000000, tier: \"L\", level: 153 },\n { maxPictureSize: 8912896, maxBitrate: 160000000, tier: \"H\", level: 153 },\n { maxPictureSize: 8912896, maxBitrate: 60000000, tier: \"L\", level: 156 },\n { maxPictureSize: 8912896, maxBitrate: 240000000, tier: \"H\", level: 156 },\n { maxPictureSize: 35651584, maxBitrate: 60000000, tier: \"L\", level: 180 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, tier: \"H\", level: 180 },\n { maxPictureSize: 35651584, maxBitrate: 120000000, tier: \"L\", level: 183 },\n { maxPictureSize: 35651584, maxBitrate: 480000000, tier: \"H\", level: 183 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, tier: \"L\", level: 186 },\n { maxPictureSize: 35651584, maxBitrate: 800000000, tier: \"H\", level: 186 }\n ];\n var VP9_LEVEL_TABLE = [\n { maxPictureSize: 36864, maxBitrate: 200000, level: 10 },\n { maxPictureSize: 73728, maxBitrate: 800000, level: 11 },\n { maxPictureSize: 122880, maxBitrate: 1800000, level: 20 },\n { maxPictureSize: 245760, maxBitrate: 3600000, level: 21 },\n { maxPictureSize: 552960, maxBitrate: 7200000, level: 30 },\n { maxPictureSize: 983040, maxBitrate: 12000000, level: 31 },\n { maxPictureSize: 2228224, maxBitrate: 18000000, level: 40 },\n { maxPictureSize: 2228224, maxBitrate: 30000000, level: 41 },\n { maxPictureSize: 8912896, maxBitrate: 60000000, level: 50 },\n { maxPictureSize: 8912896, maxBitrate: 120000000, level: 51 },\n { maxPictureSize: 8912896, maxBitrate: 180000000, level: 52 },\n { maxPictureSize: 35651584, maxBitrate: 180000000, level: 60 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, level: 61 },\n { maxPictureSize: 35651584, maxBitrate: 480000000, level: 62 }\n ];\n var AV1_LEVEL_TABLE = [\n { maxPictureSize: 147456, maxBitrate: 1500000, tier: \"M\", level: 0 },\n { maxPictureSize: 278784, maxBitrate: 3000000, tier: \"M\", level: 1 },\n { maxPictureSize: 665856, maxBitrate: 6000000, tier: \"M\", level: 4 },\n { maxPictureSize: 1065024, maxBitrate: 1e7, tier: \"M\", level: 5 },\n { maxPictureSize: 2359296, maxBitrate: 12000000, tier: \"M\", level: 8 },\n { maxPictureSize: 2359296, maxBitrate: 30000000, tier: \"H\", level: 8 },\n { maxPictureSize: 2359296, maxBitrate: 20000000, tier: \"M\", level: 9 },\n { maxPictureSize: 2359296, maxBitrate: 50000000, tier: \"H\", level: 9 },\n { maxPictureSize: 8912896, maxBitrate: 30000000, tier: \"M\", level: 12 },\n { maxPictureSize: 8912896, maxBitrate: 1e8, tier: \"H\", level: 12 },\n { maxPictureSize: 8912896, maxBitrate: 40000000, tier: \"M\", level: 13 },\n { maxPictureSize: 8912896, maxBitrate: 160000000, tier: \"H\", level: 13 },\n { maxPictureSize: 8912896, maxBitrate: 60000000, tier: \"M\", level: 14 },\n { maxPictureSize: 8912896, maxBitrate: 240000000, tier: \"H\", level: 14 },\n { maxPictureSize: 35651584, maxBitrate: 60000000, tier: \"M\", level: 15 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, tier: \"H\", level: 15 },\n { maxPictureSize: 35651584, maxBitrate: 60000000, tier: \"M\", level: 16 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, tier: \"H\", level: 16 },\n { maxPictureSize: 35651584, maxBitrate: 1e8, tier: \"M\", level: 17 },\n { maxPictureSize: 35651584, maxBitrate: 480000000, tier: \"H\", level: 17 },\n { maxPictureSize: 35651584, maxBitrate: 160000000, tier: \"M\", level: 18 },\n { maxPictureSize: 35651584, maxBitrate: 800000000, tier: \"H\", level: 18 },\n { maxPictureSize: 35651584, maxBitrate: 160000000, tier: \"M\", level: 19 },\n { maxPictureSize: 35651584, maxBitrate: 800000000, tier: \"H\", level: 19 }\n ];\n var VP9_DEFAULT_SUFFIX = \".01.01.01.01.00\";\n var AV1_DEFAULT_SUFFIX = \".0.110.01.01.01.0\";\n var PRORES_FOURCCS = [\n \"ap4x\",\n \"ap4h\",\n \"apch\",\n \"apcn\",\n \"apcs\",\n \"apco\"\n ];\n var DTS_FOURCCS = [\n \"dtsc\",\n \"dtsh\",\n \"dtsl\",\n \"dtse\"\n ];\n var PRORES_PROFILE_TARGET_BITRATES = [\n { fourCc: \"apco\", bitrate: 45000000, alpha: false },\n { fourCc: \"apcs\", bitrate: 102000000, alpha: false },\n { fourCc: \"apcn\", bitrate: 147000000, alpha: false },\n { fourCc: \"apch\", bitrate: 220000000, alpha: false },\n { fourCc: \"ap4h\", bitrate: 330000000, alpha: true },\n { fourCc: \"ap4x\", bitrate: 500000000, alpha: true }\n ];\n var buildVideoCodecString = (codec, width, height, bitrate, alpha) => {\n if (codec === \"avc\") {\n const profileIndication = 100;\n const totalMacroblocks = Math.ceil(width / 16) * Math.ceil(height / 16);\n const levelInfo = AVC_LEVEL_TABLE.find((level) => totalMacroblocks <= level.maxMacroblocks && bitrate <= level.maxBitrate) ?? last(AVC_LEVEL_TABLE);\n const levelIndication = levelInfo ? levelInfo.level : 0;\n const hexProfileIndication = profileIndication.toString(16).padStart(2, \"0\");\n const hexProfileCompatibility = \"00\";\n const hexLevelIndication = levelIndication.toString(16).padStart(2, \"0\");\n return `avc1.${hexProfileIndication}${hexProfileCompatibility}${hexLevelIndication}`;\n } else if (codec === \"hevc\") {\n const profilePrefix = \"\";\n const profileIdc = 1;\n const compatibilityFlags = \"6\";\n const pictureSize = width * height;\n const levelInfo = HEVC_LEVEL_TABLE.find((level) => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate) ?? last(HEVC_LEVEL_TABLE);\n const constraintFlags = \"B0\";\n return \"hev1.\" + `${profilePrefix}${profileIdc}.` + `${compatibilityFlags}.` + `${levelInfo.tier}${levelInfo.level}.` + `${constraintFlags}`;\n } else if (codec === \"vp8\") {\n return \"vp8\";\n } else if (codec === \"vp9\") {\n const profile = \"00\";\n const pictureSize = width * height;\n const levelInfo = VP9_LEVEL_TABLE.find((level) => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate) ?? last(VP9_LEVEL_TABLE);\n const bitDepth = \"08\";\n return `vp09.${profile}.${levelInfo.level.toString().padStart(2, \"0\")}.${bitDepth}`;\n } else if (codec === \"av1\") {\n const profile = 0;\n const pictureSize = width * height;\n const levelInfo = AV1_LEVEL_TABLE.find((level) => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate) ?? last(AV1_LEVEL_TABLE);\n const level = levelInfo.level.toString().padStart(2, \"0\");\n const bitDepth = \"08\";\n return `av01.${profile}.${level}${levelInfo.tier}.${bitDepth}`;\n } else if (codec === \"prores\") {\n const referencePixels = 1920 * 1080;\n const scaleFactor = Math.pow(width * height / referencePixels, 0.95);\n const candidates = PRORES_PROFILE_TARGET_BITRATES.filter((x) => x.alpha === alpha);\n let bestFourCc = candidates[0].fourCc;\n let smallestDifference = Infinity;\n for (const { fourCc, bitrate: targetBitrate } of candidates) {\n const difference = Math.abs(targetBitrate * scaleFactor - bitrate);\n if (difference < smallestDifference) {\n smallestDifference = difference;\n bestFourCc = fourCc;\n }\n }\n return bestFourCc;\n } else {\n assertNever(codec);\n }\n throw new TypeError(`Unhandled codec '${String(codec)}'.`);\n };\n var generateAv1CodecConfigurationFromCodecString = (codecString) => {\n const parts = codecString.split(\".\");\n const marker = 1;\n const version = 1;\n const firstByte = (marker << 7) + version;\n const profile = Number(parts[1]);\n const levelAndTier = parts[2];\n const level = Number(levelAndTier.slice(0, -1));\n const secondByte = (profile << 5) + level;\n const tier = levelAndTier.slice(-1) === \"H\" ? 1 : 0;\n const bitDepth = Number(parts[3]);\n const highBitDepth = bitDepth === 8 ? 0 : 1;\n const twelveBit = bitDepth === 12 ? 1 : 0;\n const monochrome = parts[4] ? Number(parts[4]) : 0;\n const chromaSubsamplingX = parts[5] ? Number(parts[5][0]) : 1;\n const chromaSubsamplingY = parts[5] ? Number(parts[5][1]) : 1;\n const chromaSamplePosition = parts[5] ? Number(parts[5][2]) : 0;\n const thirdByte = (tier << 7) + (highBitDepth << 6) + (twelveBit << 5) + (monochrome << 4) + (chromaSubsamplingX << 3) + (chromaSubsamplingY << 2) + chromaSamplePosition;\n const initialPresentationDelayPresent = 0;\n const fourthByte = initialPresentationDelayPresent;\n return [firstByte, secondByte, thirdByte, fourthByte];\n };\n var extractVideoCodecString = (trackInfo) => {\n const { codec, codecDescription, colorSpace, avcCodecInfo, hevcCodecInfo, vp9CodecInfo, av1CodecInfo, proresFormat } = trackInfo;\n if (codec === \"avc\") {\n assert(trackInfo.avcType !== null);\n if (avcCodecInfo) {\n const bytes = new Uint8Array([\n avcCodecInfo.avcProfileIndication,\n avcCodecInfo.profileCompatibility,\n avcCodecInfo.avcLevelIndication\n ]);\n return `avc${trackInfo.avcType}.${bytesToHexString(bytes)}`;\n }\n if (!codecDescription || codecDescription.byteLength < 4) {\n throw new TypeError(\"AVC decoder description is not provided or is not at least 4 bytes long.\");\n }\n return `avc${trackInfo.avcType}.${bytesToHexString(codecDescription.subarray(1, 4))}`;\n } else if (codec === \"hevc\") {\n let generalProfileSpace;\n let generalProfileIdc;\n let compatibilityFlags;\n let generalTierFlag;\n let generalLevelIdc;\n let constraintFlags;\n if (hevcCodecInfo) {\n generalProfileSpace = hevcCodecInfo.generalProfileSpace;\n generalProfileIdc = hevcCodecInfo.generalProfileIdc;\n compatibilityFlags = reverseBitsU32(hevcCodecInfo.generalProfileCompatibilityFlags);\n generalTierFlag = hevcCodecInfo.generalTierFlag;\n generalLevelIdc = hevcCodecInfo.generalLevelIdc;\n constraintFlags = [...hevcCodecInfo.generalConstraintIndicatorFlags];\n } else {\n if (!codecDescription || codecDescription.byteLength < 23) {\n throw new TypeError(\"HEVC decoder description is not provided or is not at least 23 bytes long.\");\n }\n const view = toDataView(codecDescription);\n const profileByte = view.getUint8(1);\n generalProfileSpace = profileByte >> 6 & 3;\n generalProfileIdc = profileByte & 31;\n compatibilityFlags = reverseBitsU32(view.getUint32(2));\n generalTierFlag = profileByte >> 5 & 1;\n generalLevelIdc = view.getUint8(12);\n constraintFlags = [];\n for (let i = 0;i < 6; i++) {\n constraintFlags.push(view.getUint8(6 + i));\n }\n }\n let codecString = \"hev1.\";\n codecString += [\"\", \"A\", \"B\", \"C\"][generalProfileSpace] + generalProfileIdc;\n codecString += \".\";\n codecString += compatibilityFlags.toString(16).toUpperCase();\n codecString += \".\";\n codecString += generalTierFlag === 0 ? \"L\" : \"H\";\n codecString += generalLevelIdc;\n while (constraintFlags.length > 0 && constraintFlags[constraintFlags.length - 1] === 0) {\n constraintFlags.pop();\n }\n if (constraintFlags.length > 0) {\n codecString += \".\";\n codecString += constraintFlags.map((x) => x.toString(16).toUpperCase()).join(\".\");\n }\n return codecString;\n } else if (codec === \"vp8\") {\n return \"vp8\";\n } else if (codec === \"vp9\") {\n if (!vp9CodecInfo) {\n const pictureSize = trackInfo.width * trackInfo.height;\n let level = last(VP9_LEVEL_TABLE).level;\n for (const entry of VP9_LEVEL_TABLE) {\n if (pictureSize <= entry.maxPictureSize) {\n level = entry.level;\n break;\n }\n }\n return `vp09.00.${level.toString().padStart(2, \"0\")}.08`;\n }\n const profile = vp9CodecInfo.profile.toString().padStart(2, \"0\");\n const level = vp9CodecInfo.level.toString().padStart(2, \"0\");\n const bitDepth = vp9CodecInfo.bitDepth.toString().padStart(2, \"0\");\n const chromaSubsampling = vp9CodecInfo.chromaSubsampling.toString().padStart(2, \"0\");\n const colourPrimaries = vp9CodecInfo.colourPrimaries.toString().padStart(2, \"0\");\n const transferCharacteristics = vp9CodecInfo.transferCharacteristics.toString().padStart(2, \"0\");\n const matrixCoefficients = vp9CodecInfo.matrixCoefficients.toString().padStart(2, \"0\");\n const videoFullRangeFlag = vp9CodecInfo.videoFullRangeFlag.toString().padStart(2, \"0\");\n let string = `vp09.${profile}.${level}.${bitDepth}.${chromaSubsampling}`;\n string += `.${colourPrimaries}.${transferCharacteristics}.${matrixCoefficients}.${videoFullRangeFlag}`;\n if (string.endsWith(VP9_DEFAULT_SUFFIX)) {\n string = string.slice(0, -VP9_DEFAULT_SUFFIX.length);\n }\n return string;\n } else if (codec === \"av1\") {\n if (!av1CodecInfo) {\n const pictureSize = trackInfo.width * trackInfo.height;\n let level = last(VP9_LEVEL_TABLE).level;\n for (const entry of VP9_LEVEL_TABLE) {\n if (pictureSize <= entry.maxPictureSize) {\n level = entry.level;\n break;\n }\n }\n return `av01.0.${level.toString().padStart(2, \"0\")}M.08`;\n }\n const profile = av1CodecInfo.profile;\n const level = av1CodecInfo.level.toString().padStart(2, \"0\");\n const tier = av1CodecInfo.tier ? \"H\" : \"M\";\n const bitDepth = av1CodecInfo.bitDepth.toString().padStart(2, \"0\");\n const monochrome = av1CodecInfo.monochrome ? \"1\" : \"0\";\n const chromaSubsampling = 100 * av1CodecInfo.chromaSubsamplingX + 10 * av1CodecInfo.chromaSubsamplingY + 1 * (av1CodecInfo.chromaSubsamplingX && av1CodecInfo.chromaSubsamplingY ? av1CodecInfo.chromaSamplePosition : 0);\n let colorPrimaries;\n let transferCharacteristics;\n let matrixCoefficients;\n let videoFullRangeFlag;\n if (av1CodecInfoHasColorInfo(av1CodecInfo)) {\n colorPrimaries = av1CodecInfo.colourPrimaries;\n transferCharacteristics = av1CodecInfo.transferCharacteristics;\n matrixCoefficients = av1CodecInfo.matrixCoefficients;\n videoFullRangeFlag = av1CodecInfo.videoFullRangeFlag;\n } else {\n colorPrimaries = colorSpace?.primaries ? COLOR_PRIMARIES_MAP[colorSpace.primaries] : 1;\n transferCharacteristics = colorSpace?.transfer ? TRANSFER_CHARACTERISTICS_MAP[colorSpace.transfer] : 1;\n matrixCoefficients = colorSpace?.matrix ? MATRIX_COEFFICIENTS_MAP[colorSpace.matrix] : 1;\n videoFullRangeFlag = colorSpace?.fullRange ? 1 : 0;\n }\n let string = `av01.${profile}.${level}${tier}.${bitDepth}`;\n string += `.${monochrome}.${chromaSubsampling.toString().padStart(3, \"0\")}`;\n string += `.${colorPrimaries.toString().padStart(2, \"0\")}`;\n string += `.${transferCharacteristics.toString().padStart(2, \"0\")}`;\n string += `.${matrixCoefficients.toString().padStart(2, \"0\")}`;\n string += `.${videoFullRangeFlag}`;\n if (string.endsWith(AV1_DEFAULT_SUFFIX)) {\n string = string.slice(0, -AV1_DEFAULT_SUFFIX.length);\n }\n return string;\n } else if (codec === \"prores\") {\n return proresFormat ?? \"apch\";\n } else if (codec !== null) {\n assertNever(codec);\n }\n throw new TypeError(`Unhandled codec '${codec}'.`);\n };\n var buildAudioCodecString = (codec, numberOfChannels, sampleRate) => {\n if (codec === \"aac\") {\n if (numberOfChannels >= 2 && sampleRate <= 24000) {\n return \"mp4a.40.29\";\n }\n if (sampleRate <= 24000) {\n return \"mp4a.40.5\";\n }\n return \"mp4a.40.2\";\n } else if (codec === \"mp3\") {\n return \"mp3\";\n } else if (codec === \"opus\") {\n return \"opus\";\n } else if (codec === \"vorbis\") {\n return \"vorbis\";\n } else if (codec === \"flac\") {\n return \"flac\";\n } else if (codec === \"ac3\") {\n return \"ac-3\";\n } else if (codec === \"eac3\") {\n return \"ec-3\";\n } else if (codec === \"dts\") {\n return \"dtsc\";\n } else if (PCM_AUDIO_CODECS.includes(codec)) {\n return codec;\n }\n throw new TypeError(`Unhandled codec '${codec}'.`);\n };\n var PCM_CODEC_REGEX = /^pcm-([usf])(\\d+)(be)?$/;\n var parsePcmCodec = (codec) => {\n assert(PCM_AUDIO_CODECS.includes(codec));\n if (codec === \"ulaw\") {\n return { dataType: \"ulaw\", sampleSize: 1, littleEndian: true, silentValue: 255 };\n } else if (codec === \"alaw\") {\n return { dataType: \"alaw\", sampleSize: 1, littleEndian: true, silentValue: 213 };\n }\n const match = PCM_CODEC_REGEX.exec(codec);\n assert(match);\n let dataType;\n if (match[1] === \"u\") {\n dataType = \"unsigned\";\n } else if (match[1] === \"s\") {\n dataType = \"signed\";\n } else {\n dataType = \"float\";\n }\n const sampleSize = Number(match[2]) / 8;\n const littleEndian = match[3] !== \"be\";\n const silentValue = codec === \"pcm-u8\" ? 2 ** 7 : 0;\n return { dataType, sampleSize, littleEndian, silentValue };\n };\n var inferCodecFromCodecString = (codecString) => {\n if (codecString.startsWith(\"avc1\") || codecString.startsWith(\"avc3\")) {\n return \"avc\";\n } else if (codecString.startsWith(\"hev1\") || codecString.startsWith(\"hvc1\")) {\n return \"hevc\";\n } else if (codecString === \"vp8\") {\n return \"vp8\";\n } else if (codecString.startsWith(\"vp09\")) {\n return \"vp9\";\n } else if (codecString.startsWith(\"av01\")) {\n return \"av1\";\n } else if (PRORES_FOURCCS.includes(codecString)) {\n return \"prores\";\n }\n if (codecString === \"mp3\" || codecString === \"mp4a.69\" || codecString === \"mp4a.6B\" || codecString === \"mp4a.6b\" || codecString === \"mp4a.40.34\") {\n return \"mp3\";\n } else if (codecString.startsWith(\"mp4a.40.\") || codecString === \"mp4a.67\") {\n return \"aac\";\n } else if (codecString === \"opus\" || codecString === \"Opus\") {\n return \"opus\";\n } else if (codecString === \"vorbis\") {\n return \"vorbis\";\n } else if (codecString === \"flac\") {\n return \"flac\";\n } else if (codecString === \"ac-3\" || codecString === \"ac3\") {\n return \"ac3\";\n } else if (codecString === \"ec-3\" || codecString === \"eac3\") {\n return \"eac3\";\n } else if (DTS_FOURCCS.includes(codecString)) {\n return \"dts\";\n } else if (codecString === \"ulaw\") {\n return \"ulaw\";\n } else if (codecString === \"alaw\") {\n return \"alaw\";\n } else if (PCM_CODEC_REGEX.test(codecString)) {\n return codecString;\n }\n if (codecString === \"webvtt\") {\n return \"webvtt\";\n }\n return null;\n };\n var getVideoEncoderConfigExtension = (codec) => {\n if (codec === \"avc\") {\n return {\n avc: {\n format: \"avc\"\n }\n };\n } else if (codec === \"hevc\") {\n return {\n hevc: {\n format: \"hevc\"\n }\n };\n }\n return {};\n };\n var getAudioEncoderConfigExtension = (codec) => {\n if (codec === \"aac\") {\n return {\n aac: {\n format: \"aac\"\n }\n };\n } else if (codec === \"opus\") {\n return {\n opus: {\n format: \"opus\"\n }\n };\n }\n return {};\n };\n var VALID_VIDEO_CODEC_STRING_PREFIXES = [\"avc1\", \"avc3\", \"hev1\", \"hvc1\", \"vp8\", \"vp09\", \"av01\", ...PRORES_FOURCCS];\n var AVC_CODEC_STRING_REGEX = /^(avc1|avc3)\\.[0-9a-fA-F]{6}$/;\n var HEVC_CODEC_STRING_REGEX = /^(hev1|hvc1)\\.(?:[ABC]?\\d+)\\.[0-9a-fA-F]{1,8}\\.[LH]\\d+(?:\\.[0-9a-fA-F]{1,2}){0,6}$/;\n var VP9_CODEC_STRING_REGEX = /^vp09(?:\\.\\d{2}){3}(?:(?:\\.\\d{2}){5})?$/;\n var AV1_CODEC_STRING_REGEX = /^av01\\.\\d\\.\\d{2}[MH]\\.\\d{2}(?:\\.\\d\\.\\d{3}\\.\\d{2}\\.\\d{2}\\.\\d{2}\\.\\d)?$/;\n var validateVideoChunkMetadata = (metadata, trackCodec) => {\n if (!metadata) {\n throw new TypeError(\"Video chunk metadata must be provided.\");\n }\n if (typeof metadata !== \"object\") {\n throw new TypeError(\"Video chunk metadata must be an object.\");\n }\n if (!metadata.decoderConfig) {\n throw new TypeError(\"Video chunk metadata must include a decoder configuration.\");\n }\n if (typeof metadata.decoderConfig !== \"object\") {\n throw new TypeError(\"Video chunk metadata decoder configuration must be an object.\");\n }\n if (typeof metadata.decoderConfig.codec !== \"string\") {\n throw new TypeError(\"Video chunk metadata decoder configuration must specify a codec string.\");\n }\n if (!VALID_VIDEO_CODEC_STRING_PREFIXES.some((prefix) => metadata.decoderConfig.codec.startsWith(prefix))) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string must be a valid video codec string as specified in\" + \" the Mediabunny Codec Registry.\");\n }\n if (!Number.isInteger(metadata.decoderConfig.codedWidth) || metadata.decoderConfig.codedWidth <= 0) {\n throw new TypeError(\"Video chunk metadata decoder configuration must specify a valid codedWidth (positive integer).\");\n }\n if (!Number.isInteger(metadata.decoderConfig.codedHeight) || metadata.decoderConfig.codedHeight <= 0) {\n throw new TypeError(\"Video chunk metadata decoder configuration must specify a valid codedHeight (positive integer).\");\n }\n if (metadata.decoderConfig.displayAspectWidth !== undefined && (!Number.isInteger(metadata.decoderConfig.displayAspectWidth) || metadata.decoderConfig.displayAspectWidth <= 0)) {\n throw new TypeError(\"Video chunk metadata decoder configuration displayAspectWidth, when defined, must be a positive integer.\");\n }\n if (metadata.decoderConfig.displayAspectHeight !== undefined && (!Number.isInteger(metadata.decoderConfig.displayAspectHeight) || metadata.decoderConfig.displayAspectHeight <= 0)) {\n throw new TypeError(\"Video chunk metadata decoder configuration displayAspectHeight, when defined, must be a positive integer.\");\n }\n if (metadata.decoderConfig.displayAspectWidth !== undefined !== (metadata.decoderConfig.displayAspectHeight !== undefined)) {\n throw new TypeError(\"Video chunk metadata decoder configuration must specify both displayAspectWidth and displayAspectHeight,\" + \" or neither.\");\n }\n if (metadata.decoderConfig.description !== undefined) {\n if (!isAllowSharedBufferSource(metadata.decoderConfig.description)) {\n throw new TypeError(\"Video chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an\" + \" ArrayBuffer view.\");\n }\n }\n if (metadata.decoderConfig.colorSpace !== undefined) {\n const { colorSpace } = metadata.decoderConfig;\n if (typeof colorSpace !== \"object\") {\n throw new TypeError(\"Video chunk metadata decoder configuration colorSpace, when provided, must be an object.\");\n }\n const primariesValues = Object.keys(COLOR_PRIMARIES_MAP);\n if (colorSpace.primaries != null && !primariesValues.includes(colorSpace.primaries)) {\n throw new TypeError(`Video chunk metadata decoder configuration colorSpace primaries, when defined, must be one of` + ` ${primariesValues.join(\", \")}.`);\n }\n const transferValues = Object.keys(TRANSFER_CHARACTERISTICS_MAP);\n if (colorSpace.transfer != null && !transferValues.includes(colorSpace.transfer)) {\n throw new TypeError(`Video chunk metadata decoder configuration colorSpace transfer, when defined, must be one of` + ` ${transferValues.join(\", \")}.`);\n }\n const matrixValues = Object.keys(MATRIX_COEFFICIENTS_MAP);\n if (colorSpace.matrix != null && !matrixValues.includes(colorSpace.matrix)) {\n throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of` + ` ${matrixValues.join(\", \")}.`);\n }\n if (colorSpace.fullRange != null && typeof colorSpace.fullRange !== \"boolean\") {\n throw new TypeError(\"Video chunk metadata decoder configuration colorSpace fullRange, when defined, must be a boolean.\");\n }\n }\n if (metadata.decoderConfig.codec.startsWith(\"avc1\") || metadata.decoderConfig.codec.startsWith(\"avc3\")) {\n if (!AVC_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string for AVC must be a valid AVC codec string as\" + \" specified in Section 3.4 of RFC 6381.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"hev1\") || metadata.decoderConfig.codec.startsWith(\"hvc1\")) {\n if (!HEVC_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string for HEVC must be a valid HEVC codec string as\" + \" specified in Section E.3 of ISO 14496-15.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"vp8\")) {\n if (metadata.decoderConfig.codec !== \"vp8\") {\n throw new TypeError('Video chunk metadata decoder configuration codec string for VP8 must be \"vp8\".');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"vp09\")) {\n if (!VP9_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string for VP9 must be a valid VP9 codec string as\" + ' specified in Section \"Codecs Parameter String\" of https://www.webmproject.org/vp9/mp4/.');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"av01\")) {\n if (!AV1_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string for AV1 must be a valid AV1 codec string as\" + ' specified in Section \"Codecs Parameter String\" of https://aomediacodec.github.io/av1-isobmff/.');\n }\n } else if (PRORES_FOURCCS.some((x) => metadata.decoderConfig.codec.startsWith(x))) {\n if (!PRORES_FOURCCS.some((x) => metadata.decoderConfig.codec === x)) {\n throw new TypeError(`Video chunk metadata decoder configuration codec string for ProRes must be one of the valid ProRes` + ` four-character codes: ${PRORES_FOURCCS.join(\", \")}.`);\n }\n }\n if (trackCodec !== null && inferCodecFromCodecString(metadata.decoderConfig.codec) !== trackCodec) {\n throw new TypeError(`Video chunk metadata decoder configuration codec string '${metadata.decoderConfig.codec}' does not fit to` + ` the track codec '${trackCodec}'.`);\n }\n };\n var VALID_AUDIO_CODEC_STRING_PREFIXES = [\n \"mp4a\",\n \"mp3\",\n \"opus\",\n \"vorbis\",\n \"flac\",\n \"ulaw\",\n \"alaw\",\n \"pcm\",\n \"ac-3\",\n \"ec-3\",\n \"dts\"\n ];\n var validateAudioChunkMetadata = (metadata, trackCodec) => {\n if (!metadata) {\n throw new TypeError(\"Audio chunk metadata must be provided.\");\n }\n if (typeof metadata !== \"object\") {\n throw new TypeError(\"Audio chunk metadata must be an object.\");\n }\n if (!metadata.decoderConfig) {\n throw new TypeError(\"Audio chunk metadata must include a decoder configuration.\");\n }\n if (typeof metadata.decoderConfig !== \"object\") {\n throw new TypeError(\"Audio chunk metadata decoder configuration must be an object.\");\n }\n if (typeof metadata.decoderConfig.codec !== \"string\") {\n throw new TypeError(\"Audio chunk metadata decoder configuration must specify a codec string.\");\n }\n if (!VALID_AUDIO_CODEC_STRING_PREFIXES.some((prefix) => metadata.decoderConfig.codec.startsWith(prefix))) {\n throw new TypeError(\"Audio chunk metadata decoder configuration codec string must be a valid audio codec string as specified in\" + \" the Mediabunny Codec Registry.\");\n }\n if (!Number.isInteger(metadata.decoderConfig.sampleRate) || metadata.decoderConfig.sampleRate <= 0) {\n throw new TypeError(\"Audio chunk metadata decoder configuration must specify a valid sampleRate (positive integer).\");\n }\n if (!Number.isInteger(metadata.decoderConfig.numberOfChannels) || metadata.decoderConfig.numberOfChannels <= 0) {\n throw new TypeError(\"Audio chunk metadata decoder configuration must specify a valid numberOfChannels (positive integer).\");\n }\n if (metadata.decoderConfig.description !== undefined) {\n if (!isAllowSharedBufferSource(metadata.decoderConfig.description)) {\n throw new TypeError(\"Audio chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an\" + \" ArrayBuffer view.\");\n }\n }\n if (metadata.decoderConfig.codec.startsWith(\"mp4a\") && metadata.decoderConfig.codec !== \"mp4a.69\" && metadata.decoderConfig.codec !== \"mp4a.6B\" && metadata.decoderConfig.codec !== \"mp4a.6b\") {\n const validStrings = [\"mp4a.40.2\", \"mp4a.40.02\", \"mp4a.40.5\", \"mp4a.40.05\", \"mp4a.40.29\", \"mp4a.67\"];\n if (!validStrings.includes(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Audio chunk metadata decoder configuration codec string for AAC must be a valid AAC codec string as\" + \" specified in https://www.w3.org/TR/webcodecs-aac-codec-registration/.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"mp3\") || metadata.decoderConfig.codec.startsWith(\"mp4a\")) {\n if (metadata.decoderConfig.codec !== \"mp3\" && metadata.decoderConfig.codec !== \"mp4a.69\" && metadata.decoderConfig.codec !== \"mp4a.6B\" && metadata.decoderConfig.codec !== \"mp4a.6b\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for MP3 must be \"mp3\", \"mp4a.69\" or' + ' \"mp4a.6B\".');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"opus\")) {\n if (metadata.decoderConfig.codec !== \"opus\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for Opus must be \"opus\".');\n }\n if (metadata.decoderConfig.description && metadata.decoderConfig.description.byteLength < 18) {\n throw new TypeError(\"Audio chunk metadata decoder configuration description, when specified, is expected to be an\" + \" Identification Header as specified in Section 5.1 of RFC 7845.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"vorbis\")) {\n if (metadata.decoderConfig.codec !== \"vorbis\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for Vorbis must be \"vorbis\".');\n }\n if (!metadata.decoderConfig.description) {\n throw new TypeError(\"Audio chunk metadata decoder configuration for Vorbis must include a description, which is expected to\" + \" adhere to the format described in https://www.w3.org/TR/webcodecs-vorbis-codec-registration/.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"flac\")) {\n if (metadata.decoderConfig.codec !== \"flac\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for FLAC must be \"flac\".');\n }\n const minDescriptionSize = 4 + 4 + 34;\n if (!metadata.decoderConfig.description || metadata.decoderConfig.description.byteLength < minDescriptionSize) {\n throw new TypeError(\"Audio chunk metadata decoder configuration for FLAC must include a description, which is expected to\" + \" adhere to the format described in https://www.w3.org/TR/webcodecs-flac-codec-registration/.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"ac-3\") || metadata.decoderConfig.codec.startsWith(\"ac3\")) {\n if (metadata.decoderConfig.codec !== \"ac-3\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for AC-3 must be \"ac-3\".');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"ec-3\") || metadata.decoderConfig.codec.startsWith(\"eac3\")) {\n if (metadata.decoderConfig.codec !== \"ec-3\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for EC-3 must be \"ec-3\".');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"dts\")) {\n if (!DTS_FOURCCS.includes(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Audio chunk metadata decoder configuration codec string for DTS must be one of the following\" + ` four-character codes: ${DTS_FOURCCS.join(\", \")}.`);\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"pcm\") || metadata.decoderConfig.codec.startsWith(\"ulaw\") || metadata.decoderConfig.codec.startsWith(\"alaw\")) {\n if (!PCM_AUDIO_CODECS.includes(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Audio chunk metadata decoder configuration codec string for PCM must be one of the supported PCM\" + ` codecs (${PCM_AUDIO_CODECS.join(\", \")}).`);\n }\n }\n if (trackCodec !== null && inferCodecFromCodecString(metadata.decoderConfig.codec) !== trackCodec) {\n throw new TypeError(`Audio chunk metadata decoder configuration codec string '${metadata.decoderConfig.codec}' does not fit to` + ` the track codec '${trackCodec}'.`);\n }\n };\n var validateSubtitleMetadata = (metadata) => {\n if (!metadata) {\n throw new TypeError(\"Subtitle metadata must be provided.\");\n }\n if (typeof metadata !== \"object\") {\n throw new TypeError(\"Subtitle metadata must be an object.\");\n }\n if (!metadata.config) {\n throw new TypeError(\"Subtitle metadata must include a config object.\");\n }\n if (typeof metadata.config !== \"object\") {\n throw new TypeError(\"Subtitle metadata config must be an object.\");\n }\n if (typeof metadata.config.description !== \"string\") {\n throw new TypeError(\"Subtitle metadata config description must be a string.\");\n }\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/packet.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var PLACEHOLDER_DATA = /* @__PURE__ */ new Uint8Array(0);\n\n class EncodedPacket {\n constructor(data, type, timestamp, duration, sequenceNumber = -1, byteLength, sideData) {\n this.data = data;\n this.type = type;\n this.timestamp = timestamp;\n this.duration = duration;\n this.sequenceNumber = sequenceNumber;\n if (data === PLACEHOLDER_DATA && byteLength === undefined) {\n throw new Error(\"Internal error: byteLength must be explicitly provided when constructing metadata-only packets.\");\n }\n if (byteLength === undefined) {\n byteLength = data.byteLength;\n }\n if (!(data instanceof Uint8Array)) {\n throw new TypeError(\"data must be a Uint8Array.\");\n }\n if (type !== \"key\" && type !== \"delta\") {\n throw new TypeError('type must be either \"key\" or \"delta\".');\n }\n if (!Number.isFinite(timestamp)) {\n throw new TypeError(\"timestamp must be a number.\");\n }\n if (!Number.isFinite(duration) || duration < 0) {\n throw new TypeError(\"duration must be a non-negative number.\");\n }\n if (!Number.isFinite(sequenceNumber)) {\n throw new TypeError(\"sequenceNumber must be a number.\");\n }\n if (!Number.isInteger(byteLength) || byteLength < 0) {\n throw new TypeError(\"byteLength must be a non-negative integer.\");\n }\n if (sideData !== undefined && (typeof sideData !== \"object\" || !sideData)) {\n throw new TypeError(\"sideData, when provided, must be an object.\");\n }\n if (sideData?.alpha !== undefined && !(sideData.alpha instanceof Uint8Array)) {\n throw new TypeError(\"sideData.alpha, when provided, must be a Uint8Array.\");\n }\n if (sideData?.alphaByteLength !== undefined && (!Number.isInteger(sideData.alphaByteLength) || sideData.alphaByteLength < 0)) {\n throw new TypeError(\"sideData.alphaByteLength, when provided, must be a non-negative integer.\");\n }\n this.byteLength = byteLength;\n this.sideData = sideData ?? {};\n if (this.sideData.alpha && this.sideData.alphaByteLength === undefined) {\n this.sideData.alphaByteLength = this.sideData.alpha.byteLength;\n }\n }\n get isMetadataOnly() {\n return this.data === PLACEHOLDER_DATA;\n }\n get microsecondTimestamp() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);\n }\n get microsecondDuration() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);\n }\n toEncodedVideoChunk() {\n if (this.isMetadataOnly) {\n throw new TypeError(\"Metadata-only packets cannot be converted to a video chunk.\");\n }\n if (typeof EncodedVideoChunk === \"undefined\") {\n throw new Error(\"EncodedVideoChunk is not available in this environment.\");\n }\n return new EncodedVideoChunk({\n data: this.data,\n type: this.type,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration\n });\n }\n alphaToEncodedVideoChunk(type = this.type) {\n if (!this.sideData.alpha) {\n throw new TypeError(\"This packet does not contain alpha side data.\");\n }\n if (this.isMetadataOnly) {\n throw new TypeError(\"Metadata-only packets cannot be converted to a video chunk.\");\n }\n if (typeof EncodedVideoChunk === \"undefined\") {\n throw new Error(\"EncodedVideoChunk is not available in this environment.\");\n }\n return new EncodedVideoChunk({\n data: this.sideData.alpha,\n type,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration\n });\n }\n toEncodedAudioChunk() {\n if (this.isMetadataOnly) {\n throw new TypeError(\"Metadata-only packets cannot be converted to an audio chunk.\");\n }\n if (typeof EncodedAudioChunk === \"undefined\") {\n throw new Error(\"EncodedAudioChunk is not available in this environment.\");\n }\n return new EncodedAudioChunk({\n data: this.data,\n type: this.type,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration\n });\n }\n static fromEncodedChunk(chunk, sideData) {\n if (!(chunk instanceof EncodedVideoChunk || chunk instanceof EncodedAudioChunk)) {\n throw new TypeError(\"chunk must be an EncodedVideoChunk or EncodedAudioChunk.\");\n }\n const data = new Uint8Array(chunk.byteLength);\n chunk.copyTo(data);\n return new EncodedPacket(data, chunk.type, chunk.timestamp / 1e6, (chunk.duration ?? 0) / 1e6, undefined, undefined, sideData);\n }\n clone(options) {\n if (options !== undefined && (typeof options !== \"object\" || options === null)) {\n throw new TypeError(\"options, when provided, must be an object.\");\n }\n if (options?.data !== undefined && !(options.data instanceof Uint8Array)) {\n throw new TypeError(\"options.data, when provided, must be a Uint8Array.\");\n }\n if (options?.type !== undefined && options.type !== \"key\" && options.type !== \"delta\") {\n throw new TypeError('options.type, when provided, must be either \"key\" or \"delta\".');\n }\n if (options?.timestamp !== undefined && !Number.isFinite(options.timestamp)) {\n throw new TypeError(\"options.timestamp, when provided, must be a number.\");\n }\n if (options?.duration !== undefined && !Number.isFinite(options.duration)) {\n throw new TypeError(\"options.duration, when provided, must be a number.\");\n }\n if (options?.sequenceNumber !== undefined && !Number.isFinite(options.sequenceNumber)) {\n throw new TypeError(\"options.sequenceNumber, when provided, must be a number.\");\n }\n if (options?.sideData !== undefined && (typeof options.sideData !== \"object\" || options.sideData === null)) {\n throw new TypeError(\"options.sideData, when provided, must be an object.\");\n }\n return new EncodedPacket(options?.data ?? this.data, options?.type ?? this.type, options?.timestamp ?? this.timestamp, options?.duration ?? this.duration, options?.sequenceNumber ?? this.sequenceNumber, this.byteLength, options?.sideData ?? this.sideData);\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-misc.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var buildIsobmffMimeType = (info) => {\n const base = info.hasVideo ? \"video/\" : info.hasAudio ? \"audio/\" : \"application/\";\n let string = base + (info.isQuickTime ? \"quicktime\" : \"mp4\");\n if (info.codecStrings.length > 0) {\n const uniqueCodecMimeTypes = [...new Set(info.codecStrings)];\n string += `; codecs=\"${uniqueCodecMimeTypes.join(\", \")}\"`;\n }\n return string;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-reader.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var MIN_BOX_HEADER_SIZE = 8;\n var MAX_BOX_HEADER_SIZE = 16;\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/adts/adts-reader.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var MIN_ADTS_FRAME_HEADER_SIZE = 7;\n var MAX_ADTS_FRAME_HEADER_SIZE = 9;\n var readAdtsFrameHeader = (slice) => {\n const startPos = slice.filePos;\n const bytes = readBytes(slice, 9);\n const bitstream = new Bitstream(bytes);\n const syncword = bitstream.readBits(12);\n if (syncword !== 4095) {\n return null;\n }\n bitstream.skipBits(1);\n const layer = bitstream.readBits(2);\n if (layer !== 0) {\n return null;\n }\n const protectionAbsence = bitstream.readBits(1);\n const objectType = bitstream.readBits(2) + 1;\n const samplingFrequencyIndex = bitstream.readBits(4);\n if (samplingFrequencyIndex === 15) {\n return null;\n }\n bitstream.skipBits(1);\n const channelConfiguration = bitstream.readBits(3);\n if (channelConfiguration === 0) {\n throw new Error(\"ADTS frames with channel configuration 0 are not supported.\");\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n const frameLength = bitstream.readBits(13);\n bitstream.skipBits(11);\n const numberOfAacFrames = bitstream.readBits(2) + 1;\n if (numberOfAacFrames !== 1) {\n throw new Error(\"ADTS frames with more than one AAC frame are not supported.\");\n }\n let crcCheck = null;\n if (protectionAbsence === 1) {\n slice.filePos -= 2;\n } else {\n crcCheck = bitstream.readBits(16);\n }\n return {\n objectType,\n samplingFrequencyIndex,\n channelConfiguration,\n frameLength,\n numberOfAacFrames,\n crcCheck,\n startPos\n };\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/sample.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var __addDisposableResource = function(env, value, async) {\n if (value !== null && value !== undefined) {\n if (typeof value !== \"object\" && typeof value !== \"function\")\n throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose)\n throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === undefined) {\n if (!Symbol.dispose)\n throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async)\n inner = dispose;\n }\n if (typeof dispose !== \"function\")\n throw new TypeError(\"Object not disposable.\");\n if (inner)\n dispose = function() {\n try {\n inner.call(this);\n } catch (e) {\n return Promise.reject(e);\n }\n };\n env.stack.push({ value, dispose, async });\n } else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n };\n var __disposeResources = function(SuppressedError2) {\n return function(env) {\n function fail(e) {\n env.error = env.hasError ? new SuppressedError2(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1)\n return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async)\n return s |= 2, Promise.resolve(result).then(next, function(e) {\n fail(e);\n return next();\n });\n } else\n s |= 1;\n } catch (e) {\n fail(e);\n }\n }\n if (s === 1)\n return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError)\n throw env.error;\n }\n return next();\n };\n }(typeof SuppressedError === \"function\" ? SuppressedError : function(error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n });\n polyfillSymbolDispose();\n var lastVideoGcErrorLog = -Infinity;\n var lastAudioGcErrorLog = -Infinity;\n var finalizationRegistry = null;\n if (typeof FinalizationRegistry !== \"undefined\") {\n finalizationRegistry = new FinalizationRegistry((value) => {\n const now = performance.now();\n if (value.type === \"video\") {\n if (now - lastVideoGcErrorLog >= 1000) {\n Logging._error(`A VideoSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your VideoSamples as soon as you're done using them.`);\n lastVideoGcErrorLog = now;\n }\n if (typeof VideoFrame !== \"undefined\" && value.data instanceof VideoFrame) {\n value.data.close();\n }\n } else {\n if (now - lastAudioGcErrorLog >= 1000) {\n Logging._error(`An AudioSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your AudioSamples as soon as you're done using them.`);\n lastAudioGcErrorLog = now;\n }\n if (typeof AudioData !== \"undefined\" && value.data instanceof AudioData) {\n value.data.close();\n }\n }\n });\n }\n\n class VideoSampleResource {\n constructor() {\n this._referenceCount = 0;\n this._lastAllocationBuffer = null;\n }\n }\n var VIDEO_SAMPLE_PIXEL_FORMATS = [\n \"I420\",\n \"I420P10\",\n \"I420P12\",\n \"I420A\",\n \"I420AP10\",\n \"I420AP12\",\n \"I422\",\n \"I422P10\",\n \"I422P12\",\n \"I422A\",\n \"I422AP10\",\n \"I422AP12\",\n \"I444\",\n \"I444P10\",\n \"I444P12\",\n \"I444A\",\n \"I444AP10\",\n \"I444AP12\",\n \"NV12\",\n \"RGBA\",\n \"RGBX\",\n \"BGRA\",\n \"BGRX\"\n ];\n var VIDEO_SAMPLE_PIXEL_FORMATS_SET = new Set(VIDEO_SAMPLE_PIXEL_FORMATS);\n\n class VideoSample {\n get codedWidth() {\n return this.visibleRect.width;\n }\n get codedHeight() {\n return this.visibleRect.height;\n }\n get displayWidth() {\n return this.rotation % 180 === 0 ? this.squarePixelWidth : this.squarePixelHeight;\n }\n get displayHeight() {\n return this.rotation % 180 === 0 ? this.squarePixelHeight : this.squarePixelWidth;\n }\n get microsecondTimestamp() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);\n }\n get microsecondDuration() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);\n }\n get hasAlpha() {\n return this.format && this.format.includes(\"A\");\n }\n constructor(data, init) {\n this._closed = false;\n if (data instanceof ArrayBuffer || typeof SharedArrayBuffer !== \"undefined\" && data instanceof SharedArrayBuffer || ArrayBuffer.isView(data)) {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"init must be an object.\");\n }\n if (init.format === undefined || !VIDEO_SAMPLE_PIXEL_FORMATS_SET.has(init.format)) {\n throw new TypeError(\"init.format must be one of: \" + VIDEO_SAMPLE_PIXEL_FORMATS.join(\", \"));\n }\n if (!Number.isInteger(init.codedWidth) || init.codedWidth <= 0) {\n throw new TypeError(\"init.codedWidth must be a positive integer.\");\n }\n if (!Number.isInteger(init.codedHeight) || init.codedHeight <= 0) {\n throw new TypeError(\"init.codedHeight must be a positive integer.\");\n }\n if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {\n throw new TypeError(\"init.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (init.flip !== undefined && typeof init.flip !== \"boolean\") {\n throw new TypeError(\"init.flip, when provided, must be a boolean.\");\n }\n if (!Number.isFinite(init.timestamp)) {\n throw new TypeError(\"init.timestamp must be a number.\");\n }\n if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {\n throw new TypeError(\"init.duration, when provided, must be a non-negative number.\");\n }\n if (init.layout !== undefined) {\n if (!Array.isArray(init.layout)) {\n throw new TypeError(\"init.layout, when provided, must be an array.\");\n }\n for (const plane of init.layout) {\n if (!plane || typeof plane !== \"object\" || Array.isArray(plane)) {\n throw new TypeError(\"Each entry in init.layout must be an object.\");\n }\n if (!Number.isInteger(plane.offset) || plane.offset < 0) {\n throw new TypeError(\"plane.offset must be a non-negative integer.\");\n }\n if (!Number.isInteger(plane.stride) || plane.stride < 0) {\n throw new TypeError(\"plane.stride must be a non-negative integer.\");\n }\n }\n }\n if (init.visibleRect !== undefined) {\n validateRectangle(init.visibleRect, \"init.visibleRect\");\n }\n if (init.displayWidth !== undefined && (!Number.isInteger(init.displayWidth) || init.displayWidth <= 0)) {\n throw new TypeError(\"init.displayWidth, when provided, must be a positive integer.\");\n }\n if (init.displayHeight !== undefined && (!Number.isInteger(init.displayHeight) || init.displayHeight <= 0)) {\n throw new TypeError(\"init.displayHeight, when provided, must be a positive integer.\");\n }\n if (init.displayWidth !== undefined !== (init.displayHeight !== undefined)) {\n throw new TypeError(\"init.displayWidth and init.displayHeight must be either both provided or both omitted.\");\n }\n this.format = init.format;\n this.rotation = init.rotation ?? 0;\n this.flip = init.flip ?? false;\n this.timestamp = init.timestamp;\n this.duration = init.duration ?? 0;\n const layout = init.layout ?? createDefaultPlaneLayout(init.format, init.codedWidth, init.codedHeight);\n let colorSpaceInit = init.colorSpace ?? null;\n if (colorSpaceInit === null) {\n if (this.format === \"RGBA\" || this.format === \"RGBX\" || this.format === \"BGRA\" || this.format === \"BGRX\") {\n colorSpaceInit = {\n primaries: \"bt709\",\n transfer: \"iec61966-2-1\",\n matrix: \"rgb\",\n fullRange: true\n };\n } else {\n colorSpaceInit = {\n primaries: \"bt709\",\n transfer: \"bt709\",\n matrix: \"bt709\",\n fullRange: false\n };\n }\n }\n this.visibleRect = {\n left: init.visibleRect?.left ?? 0,\n top: init.visibleRect?.top ?? 0,\n width: init.visibleRect?.width ?? init.codedWidth,\n height: init.visibleRect?.height ?? init.codedHeight\n };\n if (init.displayWidth !== undefined) {\n this.squarePixelWidth = this.rotation % 180 === 0 ? init.displayWidth : init.displayHeight;\n this.squarePixelHeight = this.rotation % 180 === 0 ? init.displayHeight : init.displayWidth;\n } else {\n this.squarePixelWidth = this.visibleRect.width;\n this.squarePixelHeight = this.visibleRect.height;\n }\n this._data = init._doNotCopy ? toUint8Array(data) : toUint8Array(data).slice();\n this._layout = layout;\n this.colorSpace = new VideoSampleColorSpace(colorSpaceInit);\n } else if (typeof VideoFrame !== \"undefined\" && data instanceof VideoFrame) {\n if (init?.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {\n throw new TypeError(\"init.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (init?.flip !== undefined && typeof init.flip !== \"boolean\") {\n throw new TypeError(\"init.flip, when provided, must be a boolean.\");\n }\n if (init?.timestamp !== undefined && !Number.isFinite(init?.timestamp)) {\n throw new TypeError(\"init.timestamp, when provided, must be a number.\");\n }\n if (init?.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {\n throw new TypeError(\"init.duration, when provided, must be a non-negative number.\");\n }\n if (init?.visibleRect !== undefined) {\n validateRectangle(init.visibleRect, \"init.visibleRect\");\n }\n this._data = data;\n this._layout = null;\n this.format = data.format;\n this.visibleRect = {\n left: data.visibleRect?.x ?? 0,\n top: data.visibleRect?.y ?? 0,\n width: data.visibleRect?.width ?? data.codedWidth,\n height: data.visibleRect?.height ?? data.codedHeight\n };\n this.rotation = init?.rotation ?? 0;\n this.flip = init?.flip ?? false;\n this.squarePixelWidth = data.displayWidth;\n this.squarePixelHeight = data.displayHeight;\n this.timestamp = init?.timestamp ?? data.timestamp / 1e6;\n this.duration = init?.duration ?? (data.duration ?? 0) / 1e6;\n this.colorSpace = new VideoSampleColorSpace(data.colorSpace);\n } else if (typeof HTMLImageElement !== \"undefined\" && data instanceof HTMLImageElement || typeof SVGImageElement !== \"undefined\" && data instanceof SVGImageElement || typeof ImageBitmap !== \"undefined\" && data instanceof ImageBitmap || typeof HTMLVideoElement !== \"undefined\" && data instanceof HTMLVideoElement || typeof HTMLCanvasElement !== \"undefined\" && data instanceof HTMLCanvasElement || typeof OffscreenCanvas !== \"undefined\" && data instanceof OffscreenCanvas) {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"init must be an object.\");\n }\n if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {\n throw new TypeError(\"init.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (init.flip !== undefined && typeof init.flip !== \"boolean\") {\n throw new TypeError(\"init.flip, when provided, must be a boolean.\");\n }\n if (!Number.isFinite(init.timestamp)) {\n throw new TypeError(\"init.timestamp must be a number.\");\n }\n if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {\n throw new TypeError(\"init.duration, when provided, must be a non-negative number.\");\n }\n if (init.visibleRect !== undefined) {\n validateRectangle(init.visibleRect, \"init.visibleRect\");\n }\n if (typeof VideoFrame !== \"undefined\") {\n return new VideoSample(new VideoFrame(data, {\n timestamp: Math.trunc(init.timestamp * SECOND_TO_MICROSECOND_FACTOR),\n duration: Math.trunc((init.duration ?? 0) * SECOND_TO_MICROSECOND_FACTOR) || undefined,\n visibleRect: init.visibleRect && {\n x: init.visibleRect.left,\n y: init.visibleRect.top,\n width: init.visibleRect.width,\n height: init.visibleRect.height\n }\n }), init);\n }\n let width = 0;\n let height = 0;\n if (\"naturalWidth\" in data) {\n width = data.naturalWidth;\n height = data.naturalHeight;\n } else if (\"videoWidth\" in data) {\n width = data.videoWidth;\n height = data.videoHeight;\n } else if (\"width\" in data) {\n width = Number(data.width);\n height = Number(data.height);\n }\n if (!width || !height) {\n throw new TypeError(\"Could not determine dimensions.\");\n }\n const visibleRect = init.visibleRect ?? { left: 0, top: 0, width, height };\n const canvas = new OffscreenCanvas(visibleRect.width, visibleRect.height);\n const context = canvas.getContext(\"2d\", {\n alpha: isFirefox(),\n willReadFrequently: true\n });\n if (!context) {\n throw new Error(\"OffscreenCanvas must have support for the '2d' context in order to create a VideoSample from\" + \" this data.\");\n }\n context.drawImage(data, -visibleRect.left, -visibleRect.top);\n this._data = canvas;\n this._layout = null;\n this.format = \"RGBX\";\n this.visibleRect = { left: 0, top: 0, width: visibleRect.width, height: visibleRect.height };\n this.squarePixelWidth = visibleRect.width;\n this.squarePixelHeight = visibleRect.height;\n this.rotation = init.rotation ?? 0;\n this.flip = init.flip ?? false;\n this.timestamp = init.timestamp;\n this.duration = init.duration ?? 0;\n this.colorSpace = new VideoSampleColorSpace({\n matrix: \"rgb\",\n primaries: \"bt709\",\n transfer: \"iec61966-2-1\",\n fullRange: true\n });\n } else if (data instanceof VideoSampleResource) {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"init must be an object.\");\n }\n if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {\n throw new TypeError(\"init.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (init.flip !== undefined && typeof init.flip !== \"boolean\") {\n throw new TypeError(\"init.flip, when provided, must be a boolean.\");\n }\n if (!Number.isFinite(init.timestamp)) {\n throw new TypeError(\"init.timestamp must be a number.\");\n }\n if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {\n throw new TypeError(\"init.duration, when provided, must be a non-negative number.\");\n }\n this._data = data;\n data._referenceCount++;\n this.format = data.getFormat();\n if (this.format !== null && !VIDEO_SAMPLE_PIXEL_FORMATS.includes(this.format)) {\n throw new TypeError(\"getFormat() must return a VideoSamplePixelFormat or null.\");\n }\n this.visibleRect = {\n left: 0,\n top: 0,\n width: data.getCodedWidth(),\n height: data.getCodedHeight()\n };\n if (!Number.isInteger(this.visibleRect.width) || this.visibleRect.width <= 0) {\n throw new TypeError(\"getCodedWidth() must return a positive integer.\");\n }\n if (!Number.isInteger(this.visibleRect.height) || this.visibleRect.height <= 0) {\n throw new TypeError(\"getCodedHeight() must return a positive integer.\");\n }\n this.squarePixelWidth = data.getSquarePixelWidth();\n if (!Number.isInteger(this.squarePixelWidth) || this.squarePixelWidth <= 0) {\n throw new TypeError(\"getSquarePixelWidth() must return a positive integer.\");\n }\n this.squarePixelHeight = data.getSquarePixelHeight();\n if (!Number.isInteger(this.squarePixelHeight) || this.squarePixelHeight <= 0) {\n throw new TypeError(\"getSquarePixelHeight() must return a positive integer.\");\n }\n this.rotation = init.rotation ?? 0;\n this.flip = init.flip ?? false;\n this.timestamp = init.timestamp;\n this.duration = init.duration ?? 0;\n this.colorSpace = data.getColorSpace();\n } else {\n throw new TypeError(\"Invalid data type: Must be a BufferSource, CanvasImageSource, or VideoSampleResource.\");\n }\n this.encodeOptions = init?.encodeOptions ?? {};\n this.pixelAspectRatio = simplifyRational({\n num: this.squarePixelWidth * this.codedHeight,\n den: this.squarePixelHeight * this.codedWidth\n });\n finalizationRegistry?.register(this, { type: \"video\", data: this._data }, this);\n }\n clone() {\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n assert(this._data !== null);\n if (this._data instanceof VideoSampleResource) {\n return new VideoSample(this._data, {\n timestamp: this.timestamp,\n duration: this.duration,\n rotation: this.rotation,\n flip: this.flip,\n encodeOptions: this.encodeOptions\n });\n } else if (isVideoFrame(this._data)) {\n return new VideoSample(this._data.clone(), {\n timestamp: this.timestamp,\n duration: this.duration,\n rotation: this.rotation,\n flip: this.flip,\n encodeOptions: this.encodeOptions\n });\n } else if (this._data instanceof Uint8Array) {\n assert(this._layout);\n return new VideoSample(this._data, {\n format: this.format,\n layout: this._layout,\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n timestamp: this.timestamp,\n duration: this.duration,\n colorSpace: this.colorSpace,\n rotation: this.rotation,\n flip: this.flip,\n visibleRect: this.visibleRect,\n displayWidth: this.displayWidth,\n displayHeight: this.displayHeight,\n encodeOptions: this.encodeOptions,\n _doNotCopy: true\n });\n } else {\n return new VideoSample(this._data, {\n format: this.format,\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n timestamp: this.timestamp,\n duration: this.duration,\n colorSpace: this.colorSpace,\n rotation: this.rotation,\n flip: this.flip,\n visibleRect: this.visibleRect,\n displayWidth: this.displayWidth,\n displayHeight: this.displayHeight,\n encodeOptions: this.encodeOptions\n });\n }\n }\n close() {\n if (this._closed) {\n return;\n }\n finalizationRegistry?.unregister(this);\n if (this._data instanceof VideoSampleResource) {\n this._data._referenceCount--;\n if (this._data._referenceCount === 0) {\n this._data.close();\n }\n } else if (isVideoFrame(this._data)) {\n this._data.close();\n } else {\n this._data = null;\n }\n this._closed = true;\n }\n allocationSize(options = {}) {\n validateVideoFrameCopyToOptions(options);\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n if ((options.format ?? this.format) == null) {\n throw new Error(\"Cannot get allocation size when format is null.\");\n }\n if (isVideoFrame(this._data)) {\n return this._data.allocationSize(options);\n }\n const combinedLayout = ParseVideoFrameCopyToOptions(this, options);\n return combinedLayout.allocationSize;\n }\n async copyTo(destination, options = {}) {\n if (!isAllowSharedBufferSource(destination)) {\n throw new TypeError(\"destination must be an ArrayBuffer or an ArrayBuffer view.\");\n }\n validateVideoFrameCopyToOptions(options);\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n if ((options.format ?? this.format) == null) {\n throw new Error(\"Cannot copy video sample data when format is null.\");\n }\n assert(this._data !== null);\n if (isVideoFrame(this._data)) {\n return this._data.copyTo(destination, options);\n }\n if (options.format && ![\"RGBA\", \"RGBX\", \"BGRA\", \"BGRX\"].includes(this.format) && [\"RGBA\", \"RGBX\", \"BGRA\", \"BGRX\"].includes(options.format)) {\n if (this._data instanceof VideoSampleResource) {\n const env_1 = { stack: [], error: undefined, hasError: false };\n try {\n const rgbSample = __addDisposableResource(env_1, await this._data.toRgbSample({\n timestamp: this.timestamp,\n duration: this.duration,\n rotation: this.rotation,\n flip: this.flip\n }, options.colorSpace ?? \"srgb\"), false);\n if (!(rgbSample instanceof VideoSample)) {\n throw new TypeError(\"toRgbSample() must return a VideoSample.\");\n }\n if (![\"RGBA\", \"RGBX\", \"BGRA\", \"BGRX\"].includes(rgbSample.format)) {\n throw new Error(`Sample returned by toRgbSample was expected to have an RGB format, got` + ` '${rgbSample.format}' instead.`);\n }\n return await rgbSample.copyTo(destination, options);\n } catch (e_1) {\n env_1.error = e_1;\n env_1.hasError = true;\n } finally {\n __disposeResources(env_1);\n }\n } else {\n if (typeof VideoFrame === \"undefined\") {\n throw new Error(\"For this sample, converting from a non-RGB to an RGB format requires VideoFrame to\" + \" be defined.\");\n }\n const tempFrame = this.toVideoFrame();\n const result = await tempFrame.copyTo(destination, options);\n tempFrame.close();\n return result;\n }\n }\n const combinedLayout = ParseVideoFrameCopyToOptions(this, options);\n assert(this.format);\n const destBytes = toUint8Array(destination);\n if (destBytes.byteLength < combinedLayout.allocationSize) {\n throw new TypeError(`Destination buffer too small. Required: ${combinedLayout.allocationSize},` + ` Available: ${destBytes.byteLength}`);\n }\n const planeConfigs = getPlaneConfigs(this.format);\n let dataPlanes;\n if (this._data instanceof VideoSampleResource) {\n let result = this._data.getDataPlanes();\n if (isThenable(result))\n result = await result;\n if (!Array.isArray(result) || result.some((x) => !(x.data instanceof Uint8Array) || !Number.isInteger(x.stride) || x.stride < 0)) {\n throw new TypeError('getDataPlanes() must return an array of objects with a Uint8Array \"data\" property and a' + ' non-negative integer \"stride\" property.');\n }\n dataPlanes = result;\n } else if (this._data instanceof Uint8Array) {\n assert(this._layout);\n assert(this._layout.length === planeConfigs.length);\n dataPlanes = this._layout.map((planeLayout, i) => {\n const height = Math.ceil(this.codedHeight / planeConfigs[i].heightDivisor);\n return {\n data: this._data.subarray(planeLayout.offset, planeLayout.offset + planeLayout.stride * height),\n stride: planeLayout.stride\n };\n });\n } else {\n const canvas = this._data;\n const context = canvas.getContext(\"2d\");\n assert(context);\n const imageData = context.getImageData(0, 0, this.codedWidth, this.codedHeight);\n dataPlanes = [{\n data: toUint8Array(imageData.data),\n stride: 4 * this.codedWidth\n }];\n }\n const planeLayouts = [];\n const numPlanes = planeConfigs.length;\n for (let planeIndex = 0;planeIndex < numPlanes; planeIndex++) {\n const computedLayout = combinedLayout.computedLayouts[planeIndex];\n const sourceStride = dataPlanes[planeIndex].stride;\n const sourceData = dataPlanes[planeIndex].data;\n let sourceOffset = computedLayout.sourceTop * sourceStride;\n sourceOffset += computedLayout.sourceLeftBytes;\n let destinationOffset = computedLayout.destinationOffset;\n const rowBytes = computedLayout.sourceWidthBytes;\n const layout = {\n offset: destinationOffset,\n stride: computedLayout.destinationStride\n };\n for (let row = 0;row < computedLayout.sourceHeight; row++) {\n if (sourceOffset + rowBytes > sourceData.byteLength) {\n throw new Error(`Source buffer OOB read.`);\n }\n if (destinationOffset + rowBytes > destBytes.byteLength) {\n throw new Error(`Destination buffer OOB write.`);\n }\n const srcSub = sourceData.subarray(sourceOffset, sourceOffset + rowBytes);\n destBytes.set(srcSub, destinationOffset);\n sourceOffset += sourceStride;\n destinationOffset += computedLayout.destinationStride;\n }\n planeLayouts.push(layout);\n }\n if (options.format !== undefined) {\n const needsRgbConversion = this.format.startsWith(\"RGB\") !== options.format.startsWith(\"RGB\");\n const needsAlphaConversion = this.format.includes(\"X\") && options.format.includes(\"A\");\n if (needsRgbConversion || needsAlphaConversion) {\n for (let i = 0;i < combinedLayout.allocationSize; i += 4) {\n if (needsRgbConversion) {\n const r = destBytes[i];\n const b = destBytes[i + 2];\n destBytes[i] = b;\n destBytes[i + 2] = r;\n }\n if (needsAlphaConversion) {\n destBytes[i + 3] = 255;\n }\n }\n }\n }\n return planeLayouts;\n }\n toVideoFrame() {\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n assert(this._data !== null);\n if (this._data instanceof VideoSampleResource) {\n if (this.format === null) {\n throw new Error(\"Cannot convert a VideoSampleResource-backed VideoSample to VideoFrame if format is null.\");\n }\n const planes = this._data.getDataPlanes();\n if (isThenable(planes)) {\n throw new Error(\"Cannot convert a VideoSampleResource-backed VideoSample to VideoFrame if getDataPlanes() returns\" + \" a promise.\");\n }\n const size = planes.reduce((a, b) => a + b.data.byteLength, 0);\n const buffer = new Uint8Array(size);\n let offset = 0;\n const offsets = [];\n for (const plane of planes) {\n buffer.set(plane.data, offset);\n offsets.push(offset);\n offset += plane.data.byteLength;\n }\n return new VideoFrame(buffer, {\n format: this.format,\n layout: planes.map((x, i) => ({\n offset: offsets[i],\n stride: x.stride\n })),\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration,\n colorSpace: this.colorSpace,\n visibleRect: this.visibleRect,\n displayWidth: this.squarePixelWidth,\n displayHeight: this.squarePixelHeight\n });\n } else if (isVideoFrame(this._data)) {\n return new VideoFrame(this._data, {\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration || undefined\n });\n } else if (this._data instanceof Uint8Array) {\n assert(this._layout);\n return new VideoFrame(this._data, {\n format: this.format,\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n layout: this._layout,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration || undefined,\n colorSpace: this.colorSpace,\n visibleRect: this.visibleRect,\n displayWidth: this.squarePixelWidth,\n displayHeight: this.squarePixelHeight\n });\n } else {\n return new VideoFrame(this._data, {\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration || undefined\n });\n }\n }\n draw(context, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {\n let sx = 0;\n let sy = 0;\n let sWidth = this.displayWidth;\n let sHeight = this.displayHeight;\n let dx = 0;\n let dy = 0;\n let dWidth = this.displayWidth;\n let dHeight = this.displayHeight;\n if (arg5 !== undefined) {\n sx = arg1;\n sy = arg2;\n sWidth = arg3;\n sHeight = arg4;\n dx = arg5;\n dy = arg6;\n if (arg7 !== undefined) {\n dWidth = arg7;\n dHeight = arg8;\n } else {\n dWidth = sWidth;\n dHeight = sHeight;\n }\n } else {\n dx = arg1;\n dy = arg2;\n if (arg3 !== undefined) {\n dWidth = arg3;\n dHeight = arg4;\n }\n }\n if (!(typeof CanvasRenderingContext2D !== \"undefined\" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== \"undefined\" && context instanceof OffscreenCanvasRenderingContext2D)) {\n throw new TypeError(\"context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D.\");\n }\n if (!Number.isFinite(sx)) {\n throw new TypeError(\"sx must be a number.\");\n }\n if (!Number.isFinite(sy)) {\n throw new TypeError(\"sy must be a number.\");\n }\n if (!Number.isFinite(sWidth) || sWidth < 0) {\n throw new TypeError(\"sWidth must be a non-negative number.\");\n }\n if (!Number.isFinite(sHeight) || sHeight < 0) {\n throw new TypeError(\"sHeight must be a non-negative number.\");\n }\n if (!Number.isFinite(dx)) {\n throw new TypeError(\"dx must be a number.\");\n }\n if (!Number.isFinite(dy)) {\n throw new TypeError(\"dy must be a number.\");\n }\n if (!Number.isFinite(dWidth) || dWidth < 0) {\n throw new TypeError(\"dWidth must be a non-negative number.\");\n }\n if (!Number.isFinite(dHeight) || dHeight < 0) {\n throw new TypeError(\"dHeight must be a non-negative number.\");\n }\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n ({ sx, sy, sWidth, sHeight } = this._unmapSourceRegion(sx, sy, sWidth, sHeight, this.rotation, this.flip));\n const source = this.toCanvasImageSource();\n context.save();\n const centerX = dx + dWidth / 2;\n const centerY = dy + dHeight / 2;\n context.translate(centerX, centerY);\n if (this.flip) {\n context.scale(-1, 1);\n }\n context.rotate(this.rotation * Math.PI / 180);\n const aspectRatioChange = this.rotation % 180 === 0 ? 1 : dWidth / dHeight;\n context.scale(1 / aspectRatioChange, aspectRatioChange);\n context.drawImage(source, sx, sy, sWidth, sHeight, -dWidth / 2, -dHeight / 2, dWidth, dHeight);\n context.restore();\n }\n drawWithFit(context, options) {\n if (!(typeof CanvasRenderingContext2D !== \"undefined\" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== \"undefined\" && context instanceof OffscreenCanvasRenderingContext2D)) {\n throw new TypeError(\"context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D.\");\n }\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (![\"fill\", \"contain\", \"cover\"].includes(options.fit)) {\n throw new TypeError(\"options.fit must be 'fill', 'contain', or 'cover'.\");\n }\n if (options.rotation !== undefined && ![0, 90, 180, 270].includes(options.rotation)) {\n throw new TypeError(\"options.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (options.flip !== undefined && typeof options.flip !== \"boolean\") {\n throw new TypeError(\"options.flip, when provided, must be a boolean.\");\n }\n if (options.crop !== undefined) {\n validateCropRectangle(options.crop, \"options.\");\n }\n const canvasWidth = context.canvas.width;\n const canvasHeight = context.canvas.height;\n const rotation = options.rotation ?? this.rotation;\n const flip = options.flip ?? this.flip;\n const [rotatedWidth, rotatedHeight] = rotation % 180 === 0 ? [this.squarePixelWidth, this.squarePixelHeight] : [this.squarePixelHeight, this.squarePixelWidth];\n let finalCrop = options.crop;\n if (finalCrop) {\n finalCrop = clampCropRectangle(finalCrop, rotatedWidth, rotatedHeight);\n }\n let dx;\n let dy;\n let newWidth;\n let newHeight;\n const { sx, sy, sWidth, sHeight } = this._unmapSourceRegion(options.crop?.left ?? 0, options.crop?.top ?? 0, options.crop?.width ?? rotatedWidth, options.crop?.height ?? rotatedHeight, rotation, flip);\n if (options.fit === \"fill\") {\n dx = 0;\n dy = 0;\n newWidth = canvasWidth;\n newHeight = canvasHeight;\n } else {\n const [sampleWidth, sampleHeight] = options.crop ? [options.crop.width, options.crop.height] : [rotatedWidth, rotatedHeight];\n const scale = options.fit === \"contain\" ? Math.min(canvasWidth / sampleWidth, canvasHeight / sampleHeight) : Math.max(canvasWidth / sampleWidth, canvasHeight / sampleHeight);\n newWidth = sampleWidth * scale;\n newHeight = sampleHeight * scale;\n dx = (canvasWidth - newWidth) / 2;\n dy = (canvasHeight - newHeight) / 2;\n }\n context.save();\n const aspectRatioChange = rotation % 180 === 0 ? 1 : newWidth / newHeight;\n context.translate(canvasWidth / 2, canvasHeight / 2);\n if (flip) {\n context.scale(-1, 1);\n }\n context.rotate(rotation * Math.PI / 180);\n context.scale(1 / aspectRatioChange, aspectRatioChange);\n context.translate(-canvasWidth / 2, -canvasHeight / 2);\n context.drawImage(this.toCanvasImageSource(), sx, sy, sWidth, sHeight, dx, dy, newWidth, newHeight);\n context.restore();\n }\n _unmapSourceRegion(sx, sy, sWidth, sHeight, rotation, flip) {\n if (flip) {\n const rotatedWidth = rotation % 180 === 0 ? this.squarePixelWidth : this.squarePixelHeight;\n sx = rotatedWidth - sx - sWidth;\n }\n if (rotation === 90) {\n [sx, sy, sWidth, sHeight] = [\n sy,\n this.squarePixelHeight - sx - sWidth,\n sHeight,\n sWidth\n ];\n } else if (rotation === 180) {\n [sx, sy] = [\n this.squarePixelWidth - sx - sWidth,\n this.squarePixelHeight - sy - sHeight\n ];\n } else if (rotation === 270) {\n [sx, sy, sWidth, sHeight] = [\n this.squarePixelWidth - sy - sHeight,\n sx,\n sHeight,\n sWidth\n ];\n }\n return { sx, sy, sWidth, sHeight };\n }\n _drawWithFitAndMipmapping(targetCanvas, targetContext, options) {\n const targetWidth = targetCanvas.width;\n const targetHeight = targetCanvas.height;\n const [rotatedWidth, rotatedHeight] = options.rotation % 180 === 0 ? [this.squarePixelWidth, this.squarePixelHeight] : [this.squarePixelHeight, this.squarePixelWidth];\n const sourceWidth = options.crop ? options.crop.width : rotatedWidth;\n const sourceHeight = options.crop ? options.crop.height : rotatedHeight;\n let mipLevels = 0;\n if (2 * targetWidth < sourceWidth && 2 * targetHeight < sourceHeight) {\n mipLevels = Math.floor(Math.log2(Math.min(sourceWidth / targetWidth, sourceHeight / targetHeight)));\n }\n const drawWidth = targetWidth * 2 ** mipLevels;\n const drawHeight = targetHeight * 2 ** mipLevels;\n const { canvas, context, isNew } = mipLevels > 0 ? getTransformationCanvas(drawWidth, drawHeight) : { canvas: targetCanvas, context: targetContext, isNew: options.targetIsFresh };\n context.imageSmoothingQuality = \"high\";\n if (options.fillBlack) {\n context.fillStyle = \"black\";\n context.fillRect(0, 0, drawWidth, drawHeight);\n } else if (!isNew) {\n context.clearRect(0, 0, drawWidth, drawHeight);\n }\n this.drawWithFit(context, {\n fit: options.fit,\n rotation: options.rotation,\n flip: options.flip,\n crop: options.crop\n });\n context.globalCompositeOperation = \"copy\";\n for (let i = mipLevels;i > 1; i--) {\n const levelWidth = targetWidth * 2 ** i;\n const levelHeight = targetHeight * 2 ** i;\n context.drawImage(canvas, 0, 0, levelWidth, levelHeight, 0, 0, levelWidth / 2, levelHeight / 2);\n }\n context.globalCompositeOperation = \"source-over\";\n if (mipLevels > 0) {\n targetContext.imageSmoothingQuality = \"high\";\n targetContext.globalCompositeOperation = \"copy\";\n targetContext.drawImage(canvas, 0, 0, 2 * targetWidth, 2 * targetHeight, 0, 0, targetWidth, targetHeight);\n targetContext.globalCompositeOperation = \"source-over\";\n }\n }\n toCanvasImageSource() {\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n assert(this._data !== null);\n if (this._data instanceof VideoSampleResource || this._data instanceof Uint8Array) {\n const videoFrame = this.toVideoFrame();\n queueMicrotask(() => videoFrame.close());\n return videoFrame;\n } else {\n return this._data;\n }\n }\n async transform(options) {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (options.width !== undefined && (!Number.isInteger(options.width) || options.width <= 0)) {\n throw new TypeError(\"options.width, when provided, must be a positive integer.\");\n }\n if (options.height !== undefined && (!Number.isInteger(options.height) || options.height <= 0)) {\n throw new TypeError(\"options.height, when provided, must be a positive integer.\");\n }\n if (options.roundDimensionsTo !== undefined && (!Number.isInteger(options.roundDimensionsTo) || options.roundDimensionsTo <= 0)) {\n throw new TypeError(\"options.roundDimensionsTo, when provided, must be a positive integer.\");\n }\n if (options.fit !== undefined && ![\"fill\", \"contain\", \"cover\"].includes(options.fit)) {\n throw new TypeError('options.fit, when provided, must be one of \"fill\", \"contain\", or \"cover\".');\n }\n if (options.width !== undefined && options.height !== undefined && options.fit === undefined) {\n throw new TypeError(\"When both options.width and options.height are provided, options.fit must also be provided.\");\n }\n if (options.rotate !== undefined && ![0, 90, 180, 270].includes(options.rotate)) {\n throw new TypeError(\"options.rotate, when provided, must be 0, 90, 180 or 270.\");\n }\n if (options.flip !== undefined && typeof options.flip !== \"boolean\") {\n throw new TypeError(\"options.flip, when provided, must be a boolean.\");\n }\n if (options.crop !== undefined) {\n validateCropRectangle(options.crop, \"options.\");\n }\n if (options.alpha !== undefined && ![\"keep\", \"discard\"].includes(options.alpha)) {\n throw new TypeError(\"options.alpha, when provided, must be 'keep' or 'discard'.\");\n }\n const { rotation, flip } = composeRotationAndFlip(this.rotation, this.flip, options.rotate ?? 0, options.flip ?? false);\n const [rotatedWidth, rotatedHeight] = rotation % 180 === 0 ? [this.squarePixelWidth, this.squarePixelHeight] : [this.squarePixelHeight, this.squarePixelWidth];\n let finalCrop = options.crop;\n if (finalCrop) {\n finalCrop = clampCropRectangle(finalCrop, rotatedWidth, rotatedHeight);\n }\n const cropWidth = finalCrop ? finalCrop.width : rotatedWidth;\n const cropHeight = finalCrop ? finalCrop.height : rotatedHeight;\n const originalAspectRatio = cropWidth / cropHeight;\n let targetWidth;\n let targetHeight;\n if (options.width !== undefined && options.height === undefined) {\n targetWidth = options.width;\n targetHeight = targetWidth / originalAspectRatio;\n } else if (options.width === undefined && options.height !== undefined) {\n targetHeight = options.height;\n targetWidth = targetHeight * originalAspectRatio;\n } else if (options.width !== undefined && options.height !== undefined) {\n targetWidth = options.width;\n targetHeight = options.height;\n } else {\n targetWidth = cropWidth;\n targetHeight = cropHeight;\n }\n targetWidth = roundToMultiple(targetWidth, options.roundDimensionsTo ?? 1);\n targetHeight = roundToMultiple(targetHeight, options.roundDimensionsTo ?? 1);\n const description = {\n width: targetWidth,\n height: targetHeight,\n fit: options.fit ?? \"fill\",\n rotation,\n flip,\n crop: finalCrop ?? {\n left: 0,\n top: 0,\n width: rotatedWidth,\n height: rotatedHeight\n },\n alpha: options.alpha ?? \"keep\"\n };\n for (const transformer of registeredVideoSampleTransformers) {\n let result = transformer(this, description);\n if (isThenable(result))\n result = await result;\n if (result !== null) {\n return result;\n }\n }\n const { canvas, context, isNew } = getTransformationCanvas(description.width, description.height);\n this._drawWithFitAndMipmapping(canvas, context, {\n fit: description.fit,\n rotation: description.rotation,\n flip: description.flip,\n crop: description.crop,\n targetIsFresh: isNew,\n fillBlack: description.alpha === \"discard\"\n });\n return new VideoSample(canvas, {\n timestamp: this.timestamp,\n duration: this.duration,\n rotation: 0,\n flip: false\n });\n }\n setRotation(newRotation) {\n if (![0, 90, 180, 270].includes(newRotation)) {\n throw new TypeError(\"newRotation must be 0, 90, 180, or 270.\");\n }\n this.rotation = newRotation;\n }\n setFlip(newFlip) {\n if (typeof newFlip !== \"boolean\") {\n throw new TypeError(\"newFlip must be a boolean.\");\n }\n this.flip = newFlip;\n }\n setTimestamp(newTimestamp) {\n if (!Number.isFinite(newTimestamp)) {\n throw new TypeError(\"newTimestamp must be a number.\");\n }\n this.timestamp = newTimestamp;\n }\n setDuration(newDuration) {\n if (!Number.isFinite(newDuration) || newDuration < 0) {\n throw new TypeError(\"newDuration must be a non-negative number.\");\n }\n this.duration = newDuration;\n }\n setEncodeOptions(newEncodeOptions) {\n if (!newEncodeOptions || typeof newEncodeOptions !== \"object\") {\n throw new TypeError(\"newEncodeOptions must be an object.\");\n }\n this.encodeOptions = newEncodeOptions;\n }\n [Symbol.dispose]() {\n this.close();\n }\n }\n var registeredVideoSampleTransformers = [];\n var TRANSFORMATION_CANVAS_CACHE_MAX_SIZE = 3;\n var transformationCanvasCache = [];\n var transformationCanvasCacheNextAge = 0;\n var getTransformationCanvas = (width, height) => {\n for (const entry of transformationCanvasCache) {\n if (entry.canvas.width === width && entry.canvas.height === height) {\n entry.age = transformationCanvasCacheNextAge++;\n return { canvas: entry.canvas, context: entry.context, isNew: false };\n }\n }\n let canvas;\n if (typeof OffscreenCanvas !== \"undefined\") {\n canvas = new OffscreenCanvas(width, height);\n } else {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n throw new Error(\"Cannot transform VideoSamples in this environment. Either run in an environment with\" + \" OffscreenCanvas or HTMLCanvasElement, or supply a custom VideoSample transformer using\" + \" registerVideoSampleTransformer().\");\n }\n canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n }\n const context = canvas.getContext(\"2d\", {\n alpha: true,\n willReadFrequently: false\n });\n if (!context) {\n throw new Error(\"The '2d' canvas context is required to transform VideoSamples. Register a custom transformer using\" + \" registerVideoSampleTransformer to work around this limitation.\");\n }\n if (transformationCanvasCache.length >= TRANSFORMATION_CANVAS_CACHE_MAX_SIZE) {\n transformationCanvasCache.splice(arrayArgmin(transformationCanvasCache, (x) => x.age), 1);\n }\n transformationCanvasCache.push({\n canvas,\n context,\n age: transformationCanvasCacheNextAge++\n });\n return { canvas, context, isNew: true };\n };\n\n class VideoSampleColorSpace {\n constructor(init) {\n if (init !== undefined) {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"init.colorSpace, when provided, must be an object.\");\n }\n const primariesValues = Object.keys(COLOR_PRIMARIES_MAP);\n if (init.primaries != null && !primariesValues.includes(init.primaries)) {\n throw new TypeError(`init.colorSpace.primaries, when provided, must be one of ${primariesValues.join(\", \")}.`);\n }\n const transferValues = Object.keys(TRANSFER_CHARACTERISTICS_MAP);\n if (init.transfer != null && !transferValues.includes(init.transfer)) {\n throw new TypeError(`init.colorSpace.transfer, when provided, must be one of ${transferValues.join(\", \")}.`);\n }\n const matrixValues = Object.keys(MATRIX_COEFFICIENTS_MAP);\n if (init.matrix != null && !matrixValues.includes(init.matrix)) {\n throw new TypeError(`init.colorSpace.matrix, when provided, must be one of ${matrixValues.join(\", \")}.`);\n }\n if (init.fullRange != null && typeof init.fullRange !== \"boolean\") {\n throw new TypeError(\"init.colorSpace.fullRange, when provided, must be a boolean.\");\n }\n }\n this.primaries = init?.primaries ?? null;\n this.transfer = init?.transfer ?? null;\n this.matrix = init?.matrix ?? null;\n this.fullRange = init?.fullRange ?? null;\n }\n toJSON() {\n return {\n primaries: this.primaries,\n transfer: this.transfer,\n matrix: this.matrix,\n fullRange: this.fullRange\n };\n }\n }\n var isVideoFrame = (x) => {\n return typeof VideoFrame !== \"undefined\" && x instanceof VideoFrame;\n };\n var clampCropRectangle = (crop, outerWidth, outerHeight) => {\n const left = Math.min(crop.left, outerWidth);\n const top = Math.min(crop.top, outerHeight);\n const width = Math.min(crop.width, outerWidth - left);\n const height = Math.min(crop.height, outerHeight - top);\n assert(width >= 0);\n assert(height >= 0);\n return { left, top, width, height };\n };\n var validateCropRectangle = (crop, prefix) => {\n if (!crop || typeof crop !== \"object\") {\n throw new TypeError(prefix + \"crop, when provided, must be an object.\");\n }\n if (!Number.isInteger(crop.left) || crop.left < 0) {\n throw new TypeError(prefix + \"crop.left must be a non-negative integer.\");\n }\n if (!Number.isInteger(crop.top) || crop.top < 0) {\n throw new TypeError(prefix + \"crop.top must be a non-negative integer.\");\n }\n if (!Number.isInteger(crop.width) || crop.width < 0) {\n throw new TypeError(prefix + \"crop.width must be a non-negative integer.\");\n }\n if (!Number.isInteger(crop.height) || crop.height < 0) {\n throw new TypeError(prefix + \"crop.height must be a non-negative integer.\");\n }\n };\n var validateVideoFrameCopyToOptions = (options) => {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (options.colorSpace !== undefined && ![\"display-p3\", \"srgb\"].includes(options.colorSpace)) {\n throw new TypeError(\"options.colorSpace, when provided, must be 'display-p3' or 'srgb'.\");\n }\n if (options.format !== undefined && typeof options.format !== \"string\") {\n throw new TypeError(\"options.format, when provided, must be a string.\");\n }\n if (options.layout !== undefined) {\n if (!Array.isArray(options.layout)) {\n throw new TypeError(\"options.layout, when provided, must be an array.\");\n }\n for (const plane of options.layout) {\n if (!plane || typeof plane !== \"object\") {\n throw new TypeError(\"Each entry in options.layout must be an object.\");\n }\n if (!Number.isInteger(plane.offset) || plane.offset < 0) {\n throw new TypeError(\"plane.offset must be a non-negative integer.\");\n }\n if (!Number.isInteger(plane.stride) || plane.stride < 0) {\n throw new TypeError(\"plane.stride must be a non-negative integer.\");\n }\n }\n }\n if (options.rect !== undefined) {\n if (!options.rect || typeof options.rect !== \"object\") {\n throw new TypeError(\"options.rect, when provided, must be an object.\");\n }\n if (options.rect.x !== undefined && (!Number.isInteger(options.rect.x) || options.rect.x < 0)) {\n throw new TypeError(\"options.rect.x, when provided, must be a non-negative integer.\");\n }\n if (options.rect.y !== undefined && (!Number.isInteger(options.rect.y) || options.rect.y < 0)) {\n throw new TypeError(\"options.rect.y, when provided, must be a non-negative integer.\");\n }\n if (options.rect.width !== undefined && (!Number.isInteger(options.rect.width) || options.rect.width < 0)) {\n throw new TypeError(\"options.rect.width, when provided, must be a non-negative integer.\");\n }\n if (options.rect.height !== undefined && (!Number.isInteger(options.rect.height) || options.rect.height < 0)) {\n throw new TypeError(\"options.rect.height, when provided, must be a non-negative integer.\");\n }\n }\n };\n var createDefaultPlaneLayout = (format, codedWidth, codedHeight) => {\n const planes = getPlaneConfigs(format);\n const layouts = [];\n let currentOffset = 0;\n for (const plane of planes) {\n const planeWidth = Math.ceil(codedWidth / plane.widthDivisor);\n const planeHeight = Math.ceil(codedHeight / plane.heightDivisor);\n const stride = planeWidth * plane.sampleBytes;\n const planeSize = stride * planeHeight;\n layouts.push({\n offset: currentOffset,\n stride\n });\n currentOffset += planeSize;\n }\n return layouts;\n };\n var getPlaneConfigs = (format) => {\n const yuv = (yBytes, uvBytes, subX, subY, hasAlpha) => {\n const configs = [\n { sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 },\n { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY },\n { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY }\n ];\n if (hasAlpha) {\n configs.push({ sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 });\n }\n return configs;\n };\n switch (format) {\n case \"I420\":\n return yuv(1, 1, 2, 2, false);\n case \"I420P10\":\n case \"I420P12\":\n return yuv(2, 2, 2, 2, false);\n case \"I420A\":\n return yuv(1, 1, 2, 2, true);\n case \"I420AP10\":\n case \"I420AP12\":\n return yuv(2, 2, 2, 2, true);\n case \"I422\":\n return yuv(1, 1, 2, 1, false);\n case \"I422P10\":\n case \"I422P12\":\n return yuv(2, 2, 2, 1, false);\n case \"I422A\":\n return yuv(1, 1, 2, 1, true);\n case \"I422AP10\":\n case \"I422AP12\":\n return yuv(2, 2, 2, 1, true);\n case \"I444\":\n return yuv(1, 1, 1, 1, false);\n case \"I444P10\":\n case \"I444P12\":\n return yuv(2, 2, 1, 1, false);\n case \"I444A\":\n return yuv(1, 1, 1, 1, true);\n case \"I444AP10\":\n case \"I444AP12\":\n return yuv(2, 2, 1, 1, true);\n case \"NV12\":\n return [\n { sampleBytes: 1, widthDivisor: 1, heightDivisor: 1 },\n { sampleBytes: 2, widthDivisor: 2, heightDivisor: 2 }\n ];\n case \"RGBA\":\n case \"RGBX\":\n case \"BGRA\":\n case \"BGRX\":\n return [\n { sampleBytes: 4, widthDivisor: 1, heightDivisor: 1 }\n ];\n default:\n assertNever(format);\n assert(false);\n }\n };\n var ParseVideoFrameCopyToOptions = (sample, options) => {\n const defaultRect = {\n left: 0,\n top: 0,\n width: sample.codedWidth,\n height: sample.codedHeight\n };\n const overrideRect = options.rect;\n const parsedRect = ParseVisibleRect(defaultRect, overrideRect, sample.codedWidth, sample.codedHeight, sample.format);\n const optLayout = options.layout;\n let format;\n if (!options.format || options.format === sample.format) {\n format = sample.format;\n } else if ([\"RGBA\", \"RGBX\", \"BGRA\", \"BGRX\"].includes(options.format)) {\n format = options.format;\n } else {\n throw new Error(\"NotSupportedError: Invalid destination format.\");\n }\n return ComputeLayoutAndAllocationSize(parsedRect, format, optLayout);\n };\n var ParseVisibleRect = (defaultRect, overrideRect, codedWidth, codedHeight, format) => {\n const sourceRect = { ...defaultRect };\n if (overrideRect !== undefined) {\n if (overrideRect.width === 0 || overrideRect.height === 0) {\n throw new TypeError(\"visibleRect dimensions cannot be zero.\");\n }\n if ((overrideRect.x || 0) + (overrideRect.width || 0) > codedWidth) {\n throw new TypeError(\"visibleRect exceeds codedWidth.\");\n }\n if ((overrideRect.y || 0) + (overrideRect.height || 0) > codedHeight) {\n throw new TypeError(\"visibleRect exceeds codedHeight.\");\n }\n sourceRect.x = overrideRect.x || 0;\n sourceRect.y = overrideRect.y || 0;\n sourceRect.width = overrideRect.width || 0;\n sourceRect.height = overrideRect.height || 0;\n }\n const validAlignment = VerifyRectOffsetAlignment(format, sourceRect);\n if (!validAlignment) {\n throw new TypeError(\"visibleRect alignment is invalid for the format.\");\n }\n return sourceRect;\n };\n var VerifyRectOffsetAlignment = (format, rect) => {\n if (format === null)\n return true;\n const planes = getPlaneConfigs(format);\n for (let planeIndex = 0;planeIndex < planes.length; planeIndex++) {\n const plane = planes[planeIndex];\n const sampleWidth = plane.widthDivisor;\n const sampleHeight = plane.heightDivisor;\n if ((rect.x || 0) % sampleWidth !== 0)\n return false;\n if ((rect.y || 0) % sampleHeight !== 0)\n return false;\n }\n return true;\n };\n var ComputeLayoutAndAllocationSize = (parsedRect, format, layout) => {\n const planes = getPlaneConfigs(format);\n const numPlanes = planes.length;\n if (layout !== undefined && layout.length !== numPlanes) {\n throw new TypeError(`Layout must have ${numPlanes} planes.`);\n }\n let minAllocationSize = 0;\n const computedLayouts = [];\n const endOffsets = [];\n for (let planeIndex = 0;planeIndex < numPlanes; planeIndex++) {\n const plane = planes[planeIndex];\n const sampleBytes = plane.sampleBytes;\n const sampleWidth = plane.widthDivisor;\n const sampleHeight = plane.heightDivisor;\n const computedLayout = {\n destinationOffset: 0,\n destinationStride: 0,\n sourceTop: 0,\n sourceHeight: 0,\n sourceLeftBytes: 0,\n sourceWidthBytes: 0\n };\n computedLayout.sourceTop = Math.ceil(Math.trunc(parsedRect.y || 0) / sampleHeight);\n computedLayout.sourceHeight = Math.ceil(Math.trunc(parsedRect.height || 0) / sampleHeight);\n computedLayout.sourceLeftBytes = Math.floor(Math.trunc(parsedRect.x || 0) / sampleWidth) * sampleBytes;\n computedLayout.sourceWidthBytes = Math.floor(Math.trunc(parsedRect.width || 0) / sampleWidth) * sampleBytes;\n if (layout !== undefined) {\n const planeLayout = layout[planeIndex];\n if (planeLayout.stride < computedLayout.sourceWidthBytes) {\n throw new TypeError(`Stride for plane ${planeIndex} is too small.`);\n }\n computedLayout.destinationOffset = planeLayout.offset;\n computedLayout.destinationStride = planeLayout.stride;\n } else {\n computedLayout.destinationOffset = minAllocationSize;\n computedLayout.destinationStride = computedLayout.sourceWidthBytes;\n }\n const planeSize = computedLayout.destinationStride * computedLayout.sourceHeight;\n const planeEnd = planeSize + computedLayout.destinationOffset;\n if (planeEnd > 4294967295) {\n throw new TypeError(\"Allocation size exceeds limit.\");\n }\n endOffsets.push(planeEnd);\n minAllocationSize = Math.max(minAllocationSize, planeEnd);\n for (let earlierPlaneIndex = 0;earlierPlaneIndex < planeIndex; earlierPlaneIndex++) {\n const earlierLayout = computedLayouts[earlierPlaneIndex];\n if (endOffsets[planeIndex] <= earlierLayout.destinationOffset || endOffsets[earlierPlaneIndex] <= computedLayout.destinationOffset) {\n continue;\n }\n throw new TypeError(\"Planes overlap.\");\n }\n computedLayouts.push(computedLayout);\n }\n return {\n allocationSize: minAllocationSize,\n computedLayouts\n };\n };\n var AUDIO_SAMPLE_FORMATS = new Set([\"f32\", \"f32-planar\", \"s16\", \"s16-planar\", \"s32\", \"s32-planar\", \"u8\", \"u8-planar\"]);\n\n class AudioSampleResource {\n constructor() {\n this._referenceCount = 0;\n }\n }\n\n class AudioSample {\n get microsecondTimestamp() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);\n }\n get microsecondDuration() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);\n }\n constructor(init) {\n this._closed = false;\n if (isAudioData(init)) {\n if (init.format === null) {\n throw new TypeError(\"AudioData with null format is not supported.\");\n }\n this._data = init;\n this.format = init.format;\n this.sampleRate = init.sampleRate;\n this.numberOfFrames = init.numberOfFrames;\n this.numberOfChannels = init.numberOfChannels;\n this.timestamp = init.timestamp / 1e6;\n this.duration = init.numberOfFrames / init.sampleRate;\n } else if (init instanceof AudioSampleResource) {\n this._data = init;\n init._referenceCount++;\n this.format = init.getFormat();\n if (!AUDIO_SAMPLE_FORMATS.has(this.format)) {\n throw new TypeError(\"getFormat() must return an AudioSampleFormat.\");\n }\n this.sampleRate = init.getSampleRate();\n if (!Number.isInteger(this.sampleRate) || this.sampleRate <= 0) {\n throw new TypeError(\"getSampleRate() must return a positive integer.\");\n }\n this.numberOfFrames = init.getNumberOfFrames();\n if (!Number.isInteger(this.numberOfFrames) || this.numberOfFrames < 0) {\n throw new TypeError(\"getNumberOfFrames() must return a non-negative integer.\");\n }\n this.numberOfChannels = init.getNumberOfChannels();\n if (!Number.isInteger(this.numberOfChannels) || this.numberOfChannels <= 0) {\n throw new TypeError(\"getNumberOfChannels() must return a positive integer.\");\n }\n this.timestamp = init.getTimestamp();\n if (!Number.isFinite(this.timestamp)) {\n throw new TypeError(\"getTimestamp() must return a finite number.\");\n }\n this.duration = this.numberOfFrames / this.sampleRate;\n } else {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"Invalid AudioDataInit: must be an object.\");\n }\n if (!AUDIO_SAMPLE_FORMATS.has(init.format)) {\n throw new TypeError(\"Invalid AudioDataInit: invalid format.\");\n }\n if (!Number.isFinite(init.sampleRate) || init.sampleRate <= 0) {\n throw new TypeError(\"Invalid AudioDataInit: sampleRate must be > 0.\");\n }\n if (!Number.isInteger(init.numberOfChannels) || init.numberOfChannels === 0) {\n throw new TypeError(\"Invalid AudioDataInit: numberOfChannels must be an integer > 0.\");\n }\n if (!Number.isFinite(init?.timestamp)) {\n throw new TypeError(\"init.timestamp must be a number.\");\n }\n const numberOfFrames = init.data.byteLength / (getBytesPerSample(init.format) * init.numberOfChannels);\n if (!Number.isInteger(numberOfFrames)) {\n throw new TypeError(\"Invalid AudioDataInit: data size is not a multiple of frame size.\");\n }\n this.format = init.format;\n this.sampleRate = init.sampleRate;\n this.numberOfFrames = numberOfFrames;\n this.numberOfChannels = init.numberOfChannels;\n this.timestamp = init.timestamp;\n this.duration = numberOfFrames / init.sampleRate;\n let dataBuffer;\n if (init.data instanceof ArrayBuffer) {\n dataBuffer = new Uint8Array(init.data);\n } else if (ArrayBuffer.isView(init.data)) {\n dataBuffer = new Uint8Array(init.data.buffer, init.data.byteOffset, init.data.byteLength);\n } else {\n throw new TypeError(\"Invalid AudioDataInit: data is not a BufferSource.\");\n }\n const expectedSize = this.numberOfFrames * this.numberOfChannels * getBytesPerSample(this.format);\n if (dataBuffer.byteLength < expectedSize) {\n throw new TypeError(\"Invalid AudioDataInit: insufficient data size.\");\n }\n this._data = dataBuffer;\n }\n finalizationRegistry?.register(this, { type: \"audio\", data: this._data }, this);\n }\n allocationSize(options) {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (!Number.isInteger(options.planeIndex) || options.planeIndex < 0) {\n throw new TypeError(\"planeIndex must be a non-negative integer.\");\n }\n if (options.format !== undefined && !AUDIO_SAMPLE_FORMATS.has(options.format)) {\n throw new TypeError(\"Invalid format.\");\n }\n if (options.frameOffset !== undefined && (!Number.isInteger(options.frameOffset) || options.frameOffset < 0)) {\n throw new TypeError(\"frameOffset must be a non-negative integer.\");\n }\n if (options.frameCount !== undefined && (!Number.isInteger(options.frameCount) || options.frameCount < 0)) {\n throw new TypeError(\"frameCount must be a non-negative integer.\");\n }\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n const destFormat = options.format ?? this.format;\n const frameOffset = options.frameOffset ?? 0;\n if (frameOffset >= this.numberOfFrames) {\n throw new RangeError(\"frameOffset out of range\");\n }\n const copyFrameCount = options.frameCount !== undefined ? options.frameCount : this.numberOfFrames - frameOffset;\n if (copyFrameCount > this.numberOfFrames - frameOffset) {\n throw new RangeError(\"frameCount out of range\");\n }\n const bytesPerSample = getBytesPerSample(destFormat);\n const isPlanar = formatIsPlanar(destFormat);\n if (isPlanar && options.planeIndex >= this.numberOfChannels) {\n throw new RangeError(\"planeIndex out of range\");\n }\n if (!isPlanar && options.planeIndex !== 0) {\n throw new RangeError(\"planeIndex out of range\");\n }\n const elementCount = isPlanar ? copyFrameCount : copyFrameCount * this.numberOfChannels;\n return elementCount * bytesPerSample;\n }\n copyTo(destination, options) {\n if (!isAllowSharedBufferSource(destination)) {\n throw new TypeError(\"destination must be an ArrayBuffer or an ArrayBuffer view.\");\n }\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (!Number.isInteger(options.planeIndex) || options.planeIndex < 0) {\n throw new TypeError(\"planeIndex must be a non-negative integer.\");\n }\n if (options.format !== undefined && !AUDIO_SAMPLE_FORMATS.has(options.format)) {\n throw new TypeError(\"Invalid format.\");\n }\n if (options.frameOffset !== undefined && (!Number.isInteger(options.frameOffset) || options.frameOffset < 0)) {\n throw new TypeError(\"frameOffset must be a non-negative integer.\");\n }\n if (options.frameCount !== undefined && (!Number.isInteger(options.frameCount) || options.frameCount < 0)) {\n throw new TypeError(\"frameCount must be a non-negative integer.\");\n }\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n const { format, frameCount: optFrameCount, frameOffset: optFrameOffset } = options;\n let { planeIndex } = options;\n let srcFormat = this.format;\n const destFormat = format ?? this.format;\n if (!destFormat)\n throw new Error(\"Destination format not determined\");\n const numFrames = this.numberOfFrames;\n const numChannels = this.numberOfChannels;\n const frameOffset = optFrameOffset ?? 0;\n if (frameOffset >= numFrames) {\n throw new RangeError(\"frameOffset out of range\");\n }\n const copyFrameCount = optFrameCount !== undefined ? optFrameCount : numFrames - frameOffset;\n if (copyFrameCount > numFrames - frameOffset) {\n throw new RangeError(\"frameCount out of range\");\n }\n const destBytesPerSample = getBytesPerSample(destFormat);\n const destIsPlanar = formatIsPlanar(destFormat);\n if (destIsPlanar && planeIndex >= numChannels) {\n throw new RangeError(\"planeIndex out of range\");\n }\n if (!destIsPlanar && planeIndex !== 0) {\n throw new RangeError(\"planeIndex out of range\");\n }\n const destElementCount = destIsPlanar ? copyFrameCount : copyFrameCount * numChannels;\n const requiredSize = destElementCount * destBytesPerSample;\n if (destination.byteLength < requiredSize) {\n throw new RangeError(\"Destination buffer is too small\");\n }\n const destView = toDataView(destination);\n const writeFn = getWriteFunction(destFormat);\n if (isAudioData(this._data)) {\n if (isWebKit() && numChannels > 2 && destFormat !== srcFormat) {\n doAudioDataCopyToWebKitWorkaround(this._data, destView, srcFormat, destFormat, numChannels, planeIndex, frameOffset, copyFrameCount);\n return;\n } else {\n try {\n this._data.copyTo(destination, {\n planeIndex,\n frameOffset,\n frameCount: copyFrameCount,\n format: destFormat\n });\n return;\n } catch (error) {\n if (destFormat === \"f32-planar\") {\n throw error;\n }\n srcFormat = \"f32-planar\";\n }\n }\n }\n const readFn = getReadFunction(srcFormat);\n const srcBytesPerSample = getBytesPerSample(srcFormat);\n const srcIsPlanar = formatIsPlanar(srcFormat);\n let uint8Data;\n if (this._data instanceof AudioSampleResource) {\n const getDataPlaneValidated = (index) => {\n const result = this._data.getDataPlane(index);\n if (!(result instanceof Uint8Array)) {\n throw new TypeError(\"getDataPlane() must return a Uint8Array.\");\n }\n const expectedSize = numFrames * srcBytesPerSample * (srcIsPlanar ? 1 : numChannels);\n if (result.byteLength !== expectedSize) {\n throw new TypeError(`Data plane ${index} has invalid size. Expected exactly ${expectedSize} bytes, got` + ` ${result.byteLength} bytes.`);\n }\n return result;\n };\n if (srcIsPlanar) {\n if (destIsPlanar) {\n uint8Data = getDataPlaneValidated(planeIndex);\n planeIndex = 0;\n } else {\n uint8Data = new Uint8Array(numFrames * srcBytesPerSample * numChannels);\n for (let ch = 0;ch < numChannels; ch++) {\n const planeData = getDataPlaneValidated(ch);\n uint8Data.set(planeData, ch * numFrames * srcBytesPerSample);\n }\n }\n } else {\n uint8Data = getDataPlaneValidated(0);\n }\n } else if (this._data instanceof Uint8Array) {\n uint8Data = this._data;\n } else {\n assert(srcFormat === \"f32-planar\");\n if (destIsPlanar) {\n uint8Data = new Uint8Array(this._data.allocationSize({\n format: \"f32-planar\",\n planeIndex\n }));\n this._data.copyTo(uint8Data, {\n format: \"f32-planar\",\n planeIndex\n });\n planeIndex = 0;\n } else {\n uint8Data = new Uint8Array(this._data.allocationSize({\n format: \"f32-planar\",\n planeIndex: 0\n }) * numChannels);\n for (let ch = 0;ch < numChannels; ch++) {\n this._data.copyTo(uint8Data.subarray(ch * numFrames * srcBytesPerSample, (ch + 1) * numFrames * srcBytesPerSample), {\n format: \"f32-planar\",\n planeIndex: ch\n });\n }\n }\n }\n const srcView = toDataView(uint8Data);\n for (let i = 0;i < copyFrameCount; i++) {\n if (destIsPlanar) {\n const destOffset = i * destBytesPerSample;\n let srcOffset;\n if (srcIsPlanar) {\n srcOffset = (planeIndex * numFrames + (i + frameOffset)) * srcBytesPerSample;\n } else {\n srcOffset = ((i + frameOffset) * numChannels + planeIndex) * srcBytesPerSample;\n }\n const normalized = readFn(srcView, srcOffset);\n writeFn(destView, destOffset, normalized);\n } else {\n for (let ch = 0;ch < numChannels; ch++) {\n const destIndex = i * numChannels + ch;\n const destOffset = destIndex * destBytesPerSample;\n let srcOffset;\n if (srcIsPlanar) {\n srcOffset = (ch * numFrames + (i + frameOffset)) * srcBytesPerSample;\n } else {\n srcOffset = ((i + frameOffset) * numChannels + ch) * srcBytesPerSample;\n }\n const normalized = readFn(srcView, srcOffset);\n writeFn(destView, destOffset, normalized);\n }\n }\n }\n }\n clone() {\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n if (this._data instanceof AudioSampleResource) {\n const sample = new AudioSample(this._data);\n sample.setTimestamp(this.timestamp);\n return sample;\n } else if (isAudioData(this._data)) {\n const sample = new AudioSample(this._data.clone());\n sample.setTimestamp(this.timestamp);\n return sample;\n } else {\n return new AudioSample({\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfFrames: this.numberOfFrames,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.timestamp,\n data: this._data\n });\n }\n }\n trim(startFrame, endFrame = this.numberOfFrames) {\n if (!Number.isInteger(startFrame) || startFrame < 0) {\n throw new TypeError(\"startFrame must be a non-negative integer.\");\n }\n if (!Number.isInteger(endFrame) || endFrame < 0) {\n throw new TypeError(\"endFrame must be a non-negative integer.\");\n }\n if (startFrame > this.numberOfFrames) {\n throw new RangeError(\"startFrame out of range.\");\n }\n if (endFrame > this.numberOfFrames) {\n throw new RangeError(\"endFrame out of range.\");\n }\n if (endFrame < startFrame) {\n throw new RangeError(\"endFrame must not be less than startFrame.\");\n }\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n const frameCount = endFrame - startFrame;\n const bytesPerSample = getBytesPerSample(this.format);\n let data;\n if (formatIsPlanar(this.format)) {\n const planeSize = frameCount * bytesPerSample;\n data = new Uint8Array(planeSize * this.numberOfChannels);\n if (frameCount > 0) {\n for (let i = 0;i < this.numberOfChannels; i++) {\n this.copyTo(data.subarray(i * planeSize, (i + 1) * planeSize), {\n planeIndex: i,\n format: this.format,\n frameOffset: startFrame,\n frameCount\n });\n }\n }\n } else {\n data = new Uint8Array(frameCount * this.numberOfChannels * bytesPerSample);\n if (frameCount > 0) {\n this.copyTo(data, {\n planeIndex: 0,\n format: this.format,\n frameOffset: startFrame,\n frameCount\n });\n }\n }\n return new AudioSample({\n data,\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.timestamp + startFrame / this.sampleRate\n });\n }\n close() {\n if (this._closed) {\n return;\n }\n finalizationRegistry?.unregister(this);\n if (this._data instanceof AudioSampleResource) {\n this._data._referenceCount--;\n if (this._data._referenceCount === 0) {\n this._data.close();\n }\n } else if (isAudioData(this._data)) {\n this._data.close();\n } else {\n this._data = new Uint8Array(0);\n }\n this._closed = true;\n }\n toAudioData() {\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n if (this._data instanceof AudioSampleResource) {\n return this._createAudioDataFromData();\n } else if (isAudioData(this._data)) {\n if (this._data.timestamp === this.microsecondTimestamp) {\n return this._data.clone();\n } else {\n return this._createAudioDataFromData();\n }\n } else {\n return new AudioData({\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfFrames: this.numberOfFrames,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.microsecondTimestamp,\n data: this._data.buffer instanceof ArrayBuffer ? this._data.buffer : this._data.slice()\n });\n }\n }\n _createAudioDataFromData() {\n if (formatIsPlanar(this.format)) {\n const size = this.allocationSize({ planeIndex: 0, format: this.format });\n const data = new ArrayBuffer(size * this.numberOfChannels);\n for (let i = 0;i < this.numberOfChannels; i++) {\n this.copyTo(new Uint8Array(data, i * size, size), { planeIndex: i, format: this.format });\n }\n return new AudioData({\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfFrames: this.numberOfFrames,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.microsecondTimestamp,\n data\n });\n } else {\n const data = new ArrayBuffer(this.allocationSize({ planeIndex: 0, format: this.format }));\n this.copyTo(data, { planeIndex: 0, format: this.format });\n return new AudioData({\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfFrames: this.numberOfFrames,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.microsecondTimestamp,\n data\n });\n }\n }\n toAudioBuffer() {\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n const audioBuffer = new AudioBuffer({\n numberOfChannels: this.numberOfChannels,\n length: this.numberOfFrames,\n sampleRate: this.sampleRate\n });\n const dataBytes = new Float32Array(this.allocationSize({ planeIndex: 0, format: \"f32-planar\" }) / 4);\n for (let i = 0;i < this.numberOfChannels; i++) {\n this.copyTo(dataBytes, { planeIndex: i, format: \"f32-planar\" });\n audioBuffer.copyToChannel(dataBytes, i);\n }\n return audioBuffer;\n }\n setTimestamp(newTimestamp) {\n if (!Number.isFinite(newTimestamp)) {\n throw new TypeError(\"newTimestamp must be a number.\");\n }\n this.timestamp = newTimestamp;\n }\n [Symbol.dispose]() {\n this.close();\n }\n static *_fromAudioBuffer(audioBuffer, timestamp) {\n if (!(audioBuffer instanceof AudioBuffer)) {\n throw new TypeError(\"audioBuffer must be an AudioBuffer.\");\n }\n const MAX_FLOAT_COUNT = 48000 * 5;\n const numberOfChannels = audioBuffer.numberOfChannels;\n const sampleRate = audioBuffer.sampleRate;\n const totalFrames = audioBuffer.length;\n const maxFramesPerChunk = Math.floor(MAX_FLOAT_COUNT / numberOfChannels);\n let currentRelativeFrame = 0;\n let remainingFrames = totalFrames;\n while (remainingFrames > 0) {\n const framesToCopy = Math.min(maxFramesPerChunk, remainingFrames);\n const chunkData = new Float32Array(numberOfChannels * framesToCopy);\n for (let channel = 0;channel < numberOfChannels; channel++) {\n audioBuffer.copyFromChannel(chunkData.subarray(channel * framesToCopy, (channel + 1) * framesToCopy), channel, currentRelativeFrame);\n }\n yield new AudioSample({\n format: \"f32-planar\",\n sampleRate,\n numberOfFrames: framesToCopy,\n numberOfChannels,\n timestamp: timestamp + currentRelativeFrame / sampleRate,\n data: chunkData\n });\n currentRelativeFrame += framesToCopy;\n remainingFrames -= framesToCopy;\n }\n }\n static fromAudioBuffer(audioBuffer, timestamp) {\n if (!(audioBuffer instanceof AudioBuffer)) {\n throw new TypeError(\"audioBuffer must be an AudioBuffer.\");\n }\n const MAX_FLOAT_COUNT = 48000 * 5;\n const numberOfChannels = audioBuffer.numberOfChannels;\n const sampleRate = audioBuffer.sampleRate;\n const totalFrames = audioBuffer.length;\n const maxFramesPerChunk = Math.floor(MAX_FLOAT_COUNT / numberOfChannels);\n let currentRelativeFrame = 0;\n let remainingFrames = totalFrames;\n const result = [];\n while (remainingFrames > 0) {\n const framesToCopy = Math.min(maxFramesPerChunk, remainingFrames);\n const chunkData = new Float32Array(numberOfChannels * framesToCopy);\n for (let channel = 0;channel < numberOfChannels; channel++) {\n audioBuffer.copyFromChannel(chunkData.subarray(channel * framesToCopy, (channel + 1) * framesToCopy), channel, currentRelativeFrame);\n }\n const audioSample = new AudioSample({\n format: \"f32-planar\",\n sampleRate,\n numberOfFrames: framesToCopy,\n numberOfChannels,\n timestamp: timestamp + currentRelativeFrame / sampleRate,\n data: chunkData\n });\n result.push(audioSample);\n currentRelativeFrame += framesToCopy;\n remainingFrames -= framesToCopy;\n }\n return result;\n }\n }\n var getBytesPerSample = (format) => {\n switch (format) {\n case \"u8\":\n case \"u8-planar\":\n return 1;\n case \"s16\":\n case \"s16-planar\":\n return 2;\n case \"s32\":\n case \"s32-planar\":\n return 4;\n case \"f32\":\n case \"f32-planar\":\n return 4;\n default:\n throw new Error(\"Unknown AudioSampleFormat\");\n }\n };\n var formatIsPlanar = (format) => {\n switch (format) {\n case \"u8-planar\":\n case \"s16-planar\":\n case \"s32-planar\":\n case \"f32-planar\":\n return true;\n default:\n return false;\n }\n };\n var getReadFunction = (format) => {\n switch (format) {\n case \"u8\":\n case \"u8-planar\":\n return (view, offset) => (view.getUint8(offset) - 128) / 128;\n case \"s16\":\n case \"s16-planar\":\n return (view, offset) => view.getInt16(offset, true) / 32768;\n case \"s32\":\n case \"s32-planar\":\n return (view, offset) => view.getInt32(offset, true) / 2147483648;\n case \"f32\":\n case \"f32-planar\":\n return (view, offset) => view.getFloat32(offset, true);\n }\n };\n var getWriteFunction = (format) => {\n switch (format) {\n case \"u8\":\n case \"u8-planar\":\n return (view, offset, value) => view.setUint8(offset, clamp(Math.round(value * 128) + 128, 0, 255));\n case \"s16\":\n case \"s16-planar\":\n return (view, offset, value) => view.setInt16(offset, clamp(Math.round(value * 32768), -32768, 32767), true);\n case \"s32\":\n case \"s32-planar\":\n return (view, offset, value) => view.setInt32(offset, clamp(Math.round(value * 2147483648), -2147483648, 2147483647), true);\n case \"f32\":\n case \"f32-planar\":\n return (view, offset, value) => view.setFloat32(offset, value, true);\n }\n };\n var isAudioData = (x) => {\n return typeof AudioData !== \"undefined\" && x instanceof AudioData;\n };\n var toInterleavedAudioFormat = (format) => {\n switch (format) {\n case \"u8-planar\":\n return \"u8\";\n case \"s16-planar\":\n return \"s16\";\n case \"s32-planar\":\n return \"s32\";\n case \"f32-planar\":\n return \"f32\";\n default:\n return format;\n }\n };\n var doAudioDataCopyToWebKitWorkaround = (audioData, destView, srcFormat, destFormat, numChannels, planeIndex, frameOffset, copyFrameCount) => {\n const readFn = getReadFunction(srcFormat);\n const writeFn = getWriteFunction(destFormat);\n const srcBytesPerSample = getBytesPerSample(srcFormat);\n const destBytesPerSample = getBytesPerSample(destFormat);\n const srcIsPlanar = formatIsPlanar(srcFormat);\n const destIsPlanar = formatIsPlanar(destFormat);\n if (destIsPlanar) {\n if (srcIsPlanar) {\n const data = new ArrayBuffer(copyFrameCount * srcBytesPerSample);\n const dataView = toDataView(data);\n audioData.copyTo(data, {\n planeIndex,\n frameOffset,\n frameCount: copyFrameCount,\n format: srcFormat\n });\n for (let i = 0;i < copyFrameCount; i++) {\n const srcOffset = i * srcBytesPerSample;\n const destOffset = i * destBytesPerSample;\n const sample = readFn(dataView, srcOffset);\n writeFn(destView, destOffset, sample);\n }\n } else {\n const data = new ArrayBuffer(copyFrameCount * numChannels * srcBytesPerSample);\n const dataView = toDataView(data);\n audioData.copyTo(data, {\n planeIndex: 0,\n frameOffset,\n frameCount: copyFrameCount,\n format: srcFormat\n });\n for (let i = 0;i < copyFrameCount; i++) {\n const srcOffset = (i * numChannels + planeIndex) * srcBytesPerSample;\n const destOffset = i * destBytesPerSample;\n const sample = readFn(dataView, srcOffset);\n writeFn(destView, destOffset, sample);\n }\n }\n } else {\n if (srcIsPlanar) {\n const planeSize = copyFrameCount * srcBytesPerSample;\n const data = new ArrayBuffer(planeSize);\n const dataView = toDataView(data);\n for (let ch = 0;ch < numChannels; ch++) {\n audioData.copyTo(data, {\n planeIndex: ch,\n frameOffset,\n frameCount: copyFrameCount,\n format: srcFormat\n });\n for (let i = 0;i < copyFrameCount; i++) {\n const srcOffset = i * srcBytesPerSample;\n const destOffset = (i * numChannels + ch) * destBytesPerSample;\n const sample = readFn(dataView, srcOffset);\n writeFn(destView, destOffset, sample);\n }\n }\n } else {\n const data = new ArrayBuffer(copyFrameCount * numChannels * srcBytesPerSample);\n const dataView = toDataView(data);\n audioData.copyTo(data, {\n planeIndex: 0,\n frameOffset,\n frameCount: copyFrameCount,\n format: srcFormat\n });\n for (let i = 0;i < copyFrameCount; i++) {\n for (let ch = 0;ch < numChannels; ch++) {\n const idx = i * numChannels + ch;\n const srcOffset = idx * srcBytesPerSample;\n const destOffset = idx * destBytesPerSample;\n const sample = readFn(dataView, srcOffset);\n writeFn(destView, destOffset, sample);\n }\n }\n }\n }\n };\n var audioSampleToInterleavedFormat = (sample, format) => {\n const size = sample.allocationSize({ format, planeIndex: 0 });\n const buffer = new ArrayBuffer(size);\n sample.copyTo(buffer, { format, planeIndex: 0 });\n return new AudioSample({\n data: buffer,\n format,\n numberOfChannels: sample.numberOfChannels,\n sampleRate: sample.sampleRate,\n timestamp: sample.timestamp,\n duration: sample.duration\n });\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/encode.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var canEncodeVideoMemo = new Map;\n var canEncodeAudioMemo = new Map;\n var validateVideoEncodingConfig = (config) => {\n if (!config || typeof config !== \"object\") {\n throw new TypeError(\"Encoding config must be an object.\");\n }\n if (!VIDEO_CODECS.includes(config.codec)) {\n throw new TypeError(`Invalid video codec '${config.codec}'. Must be one of: ${VIDEO_CODECS.join(\", \")}.`);\n }\n const bitrate = config.bitrate;\n if (config.quality === undefined && bitrate === undefined) {\n throw new TypeError(\"config.quality must be provided.\");\n }\n if (config.quality !== undefined && bitrate !== undefined) {\n throw new TypeError(\"config.quality and config.bitrate cannot both be provided.\");\n }\n if (config.quality !== undefined && !(config.quality instanceof Quality)) {\n throw new TypeError(\"config.quality, when provided, must be a Quality.\");\n }\n if (bitrate !== undefined && !(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {\n throw new TypeError(\"config.bitrate, when provided, must be a positive integer or a quality.\");\n }\n if (config.keyFrameInterval !== undefined && (!Number.isFinite(config.keyFrameInterval) || config.keyFrameInterval < 0)) {\n throw new TypeError(\"config.keyFrameInterval, when provided, must be a non-negative number.\");\n }\n if (config.sizeChangeBehavior !== undefined && ![\"deny\", \"passThrough\", \"fill\", \"contain\", \"cover\"].includes(config.sizeChangeBehavior)) {\n throw new TypeError(\"config.sizeChangeBehavior, when provided, must be 'deny', 'passThrough', 'fill', 'contain'\" + \" or 'cover'.\");\n }\n if (config.transform !== undefined) {\n if (typeof config.transform !== \"object\" || !config.transform) {\n throw new TypeError(\"config.transform, when provided, must be an object.\");\n }\n if (config.transform.width !== undefined && (!Number.isInteger(config.transform.width) || config.transform.width <= 0)) {\n throw new TypeError(\"config.transform.width, when provided, must be a positive integer.\");\n }\n if (config.transform.height !== undefined && (!Number.isInteger(config.transform.height) || config.transform.height <= 0)) {\n throw new TypeError(\"config.transform.height, when provided, must be a positive integer.\");\n }\n if (config.transform.fit !== undefined && ![\"fill\", \"contain\", \"cover\"].includes(config.transform.fit)) {\n throw new TypeError('config.transform.fit, when provided, must be one of \"fill\", \"contain\", or \"cover\".');\n }\n if (config.transform.width !== undefined && config.transform.height !== undefined && config.transform.fit === undefined && ![\"fill\", \"contain\", \"cover\"].includes(config.sizeChangeBehavior)) {\n throw new TypeError(\"When both config.transform.width and config.transform.height are provided, config.transform.fit\" + \" must also be provided.\");\n }\n if (config.transform.fit !== undefined && [\"fill\", \"contain\", \"cover\"].includes(config.sizeChangeBehavior) && config.transform.fit !== config.sizeChangeBehavior) {\n throw new TypeError(\"config.transform.fit, when provided, cannot differ from config.sizeChangeBehavior when\" + \" config.sizeChangeBehavior is 'fill', 'contain' or 'cover', as sizeChangeBehavior already\" + \" determines the fitting algorithm.\");\n }\n if (config.transform.rotate !== undefined && ![0, 90, 180, 270].includes(config.transform.rotate)) {\n throw new TypeError(\"config.transform.rotate, when provided, must be 0, 90, 180 or 270.\");\n }\n if (config.transform.flip !== undefined && typeof config.transform.flip !== \"boolean\") {\n throw new TypeError(\"config.transform.flip, when provided, must be a boolean.\");\n }\n if (config.transform.crop !== undefined) {\n validateCropRectangle(config.transform.crop, \"config.transform.\");\n }\n if (config.transform.process !== undefined && typeof config.transform.process !== \"function\") {\n throw new TypeError(\"config.transform.process, when provided, must be a function.\");\n }\n if (config.transform.frameRate !== undefined && (!Number.isFinite(config.transform.frameRate) || config.transform.frameRate <= 0)) {\n throw new TypeError(\"config.transform.frameRate, when provided, must be a finite positive number.\");\n }\n if (config.transform.force !== undefined && typeof config.transform.force !== \"boolean\") {\n throw new TypeError(\"config.transform.force, when provided, must be a boolean.\");\n }\n }\n if (config.onEncodedPacket !== undefined && typeof config.onEncodedPacket !== \"function\") {\n throw new TypeError(\"config.onEncodedPacket, when provided, must be a function.\");\n }\n if (config.onEncoderConfig !== undefined && typeof config.onEncoderConfig !== \"function\") {\n throw new TypeError(\"config.onEncoderConfig, when provided, must be a function.\");\n }\n if (config.onEncodedSample !== undefined && typeof config.onEncodedSample !== \"function\") {\n throw new TypeError(\"config.onEncodedSample, when provided, must be a function.\");\n }\n validateVideoEncodingAdditionalOptions(config.codec, config);\n };\n var validateVideoEncodingAdditionalOptions = (codec, options) => {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"Encoding options must be an object.\");\n }\n if (options.alpha !== undefined && ![\"discard\", \"keep\"].includes(options.alpha)) {\n throw new TypeError(\"options.alpha, when provided, must be 'discard' or 'keep'.\");\n }\n const bitrateMode = options.bitrateMode;\n if (bitrateMode !== undefined && ![\"constant\", \"variable\"].includes(bitrateMode)) {\n throw new TypeError(\"bitrateMode, when provided, must be 'constant' or 'variable'.\");\n }\n if (options.latencyMode !== undefined && ![\"quality\", \"realtime\"].includes(options.latencyMode)) {\n throw new TypeError(\"latencyMode, when provided, must be 'quality' or 'realtime'.\");\n }\n if (options.fullCodecString !== undefined && typeof options.fullCodecString !== \"string\") {\n throw new TypeError(\"fullCodecString, when provided, must be a string.\");\n }\n if (options.fullCodecString !== undefined && inferCodecFromCodecString(options.fullCodecString) !== codec) {\n throw new TypeError(`fullCodecString, when provided, must be a string that matches the specified codec (${codec}).`);\n }\n if (options.hardwareAcceleration !== undefined && ![\"no-preference\", \"prefer-hardware\", \"prefer-software\"].includes(options.hardwareAcceleration)) {\n throw new TypeError(\"hardwareAcceleration, when provided, must be 'no-preference', 'prefer-hardware' or\" + \" 'prefer-software'.\");\n }\n if (options.scalabilityMode !== undefined && typeof options.scalabilityMode !== \"string\") {\n throw new TypeError(\"scalabilityMode, when provided, must be a string.\");\n }\n if (options.contentHint !== undefined && typeof options.contentHint !== \"string\") {\n throw new TypeError(\"contentHint, when provided, must be a string.\");\n }\n };\n var buildVideoEncoderConfigs = (options) => {\n const fallbackBitrateMode = options.bitrateMode;\n const rateControl = options.quality._toVideoRateControl(options.codec, options.width, options.height, fallbackBitrateMode);\n const buildConfig = (bitrate, bitrateMode, bitrateEstimate) => ({\n codec: options.fullCodecString ?? buildVideoCodecString(options.codec, options.width, options.height, bitrateEstimate, options.alpha === \"keep\"),\n width: options.width,\n height: options.height,\n displayWidth: options.squarePixelWidth,\n displayHeight: options.squarePixelHeight,\n bitrate,\n bitrateMode,\n alpha: options.alpha ?? \"discard\",\n framerate: options.framerate,\n latencyMode: options.latencyMode,\n hardwareAcceleration: options.hardwareAcceleration,\n scalabilityMode: options.scalabilityMode,\n contentHint: options.contentHint,\n ...getVideoEncoderConfigExtension(options.codec)\n });\n const candidates = [];\n if (rateControl.quantizer !== null) {\n candidates.push({\n config: buildConfig(undefined, \"quantizer\", rateControl.bitrate),\n quantizer: rateControl.quantizer\n });\n }\n if (rateControl.bitrateMode !== \"quantizer\") {\n candidates.push({\n config: buildConfig(rateControl.bitrate, rateControl.bitrateMode, rateControl.bitrate),\n quantizer: null\n });\n }\n assert(candidates.length > 0);\n return candidates;\n };\n var validateAudioEncodingConfig = (config) => {\n if (!config || typeof config !== \"object\") {\n throw new TypeError(\"Encoding config must be an object.\");\n }\n if (!AUDIO_CODECS.includes(config.codec)) {\n throw new TypeError(`Invalid audio codec '${config.codec}'. Must be one of: ${AUDIO_CODECS.join(\", \")}.`);\n }\n const bitrate = config.bitrate;\n if (config.quality === undefined && bitrate === undefined && !(PCM_AUDIO_CODECS.includes(config.codec) || config.codec === \"flac\")) {\n throw new TypeError(\"config.quality must be provided for compressed audio codecs.\");\n }\n if (config.quality !== undefined && bitrate !== undefined) {\n throw new TypeError(\"config.quality and config.bitrate cannot both be provided.\");\n }\n if (config.quality !== undefined && !(config.quality instanceof Quality)) {\n throw new TypeError(\"config.quality, when provided, must be a Quality.\");\n }\n if (bitrate !== undefined && !(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {\n throw new TypeError(\"config.bitrate, when provided, must be a positive integer or a quality.\");\n }\n if (config.transform !== undefined) {\n if (typeof config.transform !== \"object\" || !config.transform) {\n throw new TypeError(\"config.transform, when provided, must be an object.\");\n }\n if (config.transform.numberOfChannels !== undefined && (!Number.isInteger(config.transform.numberOfChannels) || config.transform.numberOfChannels <= 0)) {\n throw new TypeError(\"config.transform.numberOfChannels, when provided, must be a positive integer.\");\n }\n if (config.transform.sampleRate !== undefined && (!Number.isInteger(config.transform.sampleRate) || config.transform.sampleRate <= 0)) {\n throw new TypeError(\"config.transform.sampleRate, when provided, must be a positive integer.\");\n }\n if (config.transform.sampleFormat !== undefined && ![\"u8\", \"s16\", \"s32\", \"f32\"].includes(config.transform.sampleFormat)) {\n throw new TypeError(\"config.transform.sampleFormat, when provided, must be one of: u8, s16, s32, f32.\");\n }\n if (config.transform.process !== undefined && typeof config.transform.process !== \"function\") {\n throw new TypeError(\"config.transform.process, when provided, must be a function.\");\n }\n }\n if (config.onEncodedPacket !== undefined && typeof config.onEncodedPacket !== \"function\") {\n throw new TypeError(\"config.onEncodedPacket, when provided, must be a function.\");\n }\n if (config.onEncoderConfig !== undefined && typeof config.onEncoderConfig !== \"function\") {\n throw new TypeError(\"config.onEncoderConfig, when provided, must be a function.\");\n }\n if (config.onEncodedSample !== undefined && typeof config.onEncodedSample !== \"function\") {\n throw new TypeError(\"config.onEncodedSample, when provided, must be a function.\");\n }\n validateAudioEncodingAdditionalOptions(config.codec, config);\n };\n var validateAudioEncodingAdditionalOptions = (codec, options) => {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"Encoding options must be an object.\");\n }\n const bitrateMode = options.bitrateMode;\n if (bitrateMode !== undefined && ![\"constant\", \"variable\"].includes(bitrateMode)) {\n throw new TypeError(\"bitrateMode, when provided, must be 'constant' or 'variable'.\");\n }\n if (options.fullCodecString !== undefined && typeof options.fullCodecString !== \"string\") {\n throw new TypeError(\"fullCodecString, when provided, must be a string.\");\n }\n if (options.fullCodecString !== undefined && inferCodecFromCodecString(options.fullCodecString) !== codec) {\n throw new TypeError(`fullCodecString, when provided, must be a string that matches the specified codec (${codec}).`);\n }\n };\n var buildAudioEncoderConfig = (options) => {\n const fallbackBitrateMode = options.bitrateMode;\n return {\n codec: options.fullCodecString ?? buildAudioCodecString(options.codec, options.numberOfChannels, options.sampleRate),\n numberOfChannels: options.numberOfChannels,\n sampleRate: options.sampleRate,\n bitrate: options.quality?._toAudioBitrate(options.codec),\n bitrateMode: options.quality?._bitrateMode ?? fallbackBitrateMode,\n ...getAudioEncoderConfigExtension(options.codec)\n };\n };\n\n class Quality {\n constructor(options) {\n if (typeof options === \"number\" || typeof options === \"string\") {\n options = { quality: options };\n }\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (options.bitrateMode !== undefined && ![\"constant\", \"variable\"].includes(options.bitrateMode)) {\n throw new TypeError(\"options.bitrateMode, when provided, must be 'constant' or 'variable'.\");\n }\n if (\"quality\" in options) {\n if (typeof options.quality === \"string\" ? !(options.quality in QUALITY_LEVELS) : typeof options.quality !== \"number\" || Number.isNaN(options.quality)) {\n throw new TypeError(\"options.quality must be a number, or one of 'very-low', 'low', 'medium', 'high'\" + \" or 'very-high'.\");\n }\n if (options.preferBitrate !== undefined && typeof options.preferBitrate !== \"boolean\") {\n throw new TypeError(\"options.preferBitrate, when provided, must be a boolean.\");\n }\n if (\"bitrate\" in options || \"quantizer\" in options) {\n throw new TypeError(\"options.quality cannot be combined with options.bitrate or options.quantizer.\");\n }\n this._quality = typeof options.quality === \"string\" ? QUALITY_LEVELS[options.quality] : options.quality;\n this._preferBitrate = options.preferBitrate ?? false;\n this._bitrate = undefined;\n this._quantizer = undefined;\n } else {\n if (options.bitrate !== undefined && (!Number.isInteger(options.bitrate) || options.bitrate <= 0)) {\n throw new TypeError(\"options.bitrate, when provided, must be a positive integer.\");\n }\n if (options.quantizer !== undefined && (!Number.isInteger(options.quantizer) || options.quantizer < 0)) {\n throw new TypeError(\"options.quantizer, when provided, must be a non-negative integer.\");\n }\n if (options.bitrate === undefined && options.quantizer === undefined) {\n throw new TypeError(\"At least one of options.bitrate or options.quantizer must be set.\");\n }\n if (\"preferBitrate\" in options) {\n throw new TypeError(\"options.preferBitrate can only be combined with options.quality.\");\n }\n this._quality = undefined;\n this._preferBitrate = false;\n this._bitrate = options.bitrate;\n this._quantizer = options.quantizer;\n }\n this._bitrateMode = options.bitrateMode;\n }\n _toVideoRateControl(codec, width, height, fallbackBitrateMode) {\n const quantizerSupport = VIDEO_CODEC_QUANTIZER_SUPPORT[codec];\n let quantizer = null;\n let bitrateMode = this._bitrateMode ?? fallbackBitrateMode ?? \"variable\";\n if (this._quantizer !== undefined) {\n if (!quantizerSupport) {\n if (this._bitrate === undefined) {\n throw new Error(`Codec '${codec}' does not support quantizer-based encoding. Provide a bitrate in the Quality` + ` to define a fallback.`);\n }\n } else if (this._quantizer < quantizerSupport.min || this._quantizer > quantizerSupport.max) {\n if (this._bitrate === undefined) {\n throw new Error(`Quantizer ${this._quantizer} is out of range for codec '${codec}'; must be between` + ` ${quantizerSupport.min} and ${quantizerSupport.max}.`);\n }\n } else {\n quantizer = this._quantizer;\n if (this._bitrate === undefined) {\n bitrateMode = \"quantizer\";\n }\n }\n } else if (this._bitrate === undefined && quantizerSupport && !this._preferBitrate) {\n assert(this._quality !== undefined);\n quantizer = clamp(Math.round(lerp(quantizerSupport.worst, quantizerSupport.best, this._quality)), quantizerSupport.min, quantizerSupport.max);\n }\n let bitrate;\n if (this._bitrate !== undefined) {\n bitrate = this._bitrate;\n } else {\n let quality = this._quality;\n if (quality === undefined) {\n assert(quantizer !== null && quantizerSupport);\n quality = clamp((quantizer - quantizerSupport.worst) / (quantizerSupport.best - quantizerSupport.worst), 0, 1);\n }\n bitrate = computeVideoBitrate(codec, width, height, qualityToBitrateFactor(quality));\n }\n return { quantizer, bitrate, bitrateMode };\n }\n _toVideoBitrate(codec, width, height) {\n if (this._bitrate !== undefined) {\n return this._bitrate;\n }\n assert(this._quality !== undefined);\n return computeVideoBitrate(codec, width, height, qualityToBitrateFactor(this._quality));\n }\n _toAudioBitrate(codec) {\n if (PCM_AUDIO_CODECS.includes(codec) || codec === \"flac\") {\n return;\n }\n if (this._bitrate !== undefined) {\n return this._bitrate;\n }\n if (this._quality === undefined) {\n throw new Error(\"This Quality defines neither a quality level nor a bitrate and therefore cannot be used for audio\" + \" encoding.\");\n }\n const factor = qualityToBitrateFactor(this._quality);\n const baseRates = {\n aac: 128000,\n opus: 64000,\n mp3: 160000,\n vorbis: 64000,\n ac3: 384000,\n eac3: 192000,\n dts: 768000\n };\n const baseBitrate = baseRates[codec];\n if (!baseBitrate) {\n throw new Error(`Unhandled codec: ${codec}`);\n }\n let finalBitrate = baseBitrate * factor;\n if (codec === \"aac\") {\n const validRates = [96000, 128000, 160000, 192000];\n finalBitrate = validRates.reduce((prev, curr) => Math.abs(curr - finalBitrate) < Math.abs(prev - finalBitrate) ? curr : prev);\n } else if (codec === \"opus\" || codec === \"vorbis\") {\n finalBitrate = Math.max(6000, finalBitrate);\n } else if (codec === \"mp3\") {\n const validRates = [\n 8000,\n 16000,\n 24000,\n 32000,\n 40000,\n 48000,\n 64000,\n 80000,\n 96000,\n 112000,\n 128000,\n 160000,\n 192000,\n 224000,\n 256000,\n 320000\n ];\n finalBitrate = validRates.reduce((prev, curr) => Math.abs(curr - finalBitrate) < Math.abs(prev - finalBitrate) ? curr : prev);\n }\n return Math.round(finalBitrate / 1000) * 1000;\n }\n }\n var QUALITY_LEVELS = {\n \"very-low\": 0,\n low: 0.25,\n medium: 0.5,\n high: 0.75,\n \"very-high\": 1\n };\n var VIDEO_CODEC_QUANTIZER_SUPPORT = {\n avc: { min: 0, max: 51, worst: 41, best: 16 },\n hevc: { min: 0, max: 51, worst: 41, best: 16 },\n vp9: { min: 0, max: 63, worst: 52, best: 20 },\n av1: { min: 0, max: 255, worst: 208, best: 80 }\n };\n var qualityToBitrateFactor = (quality) => 0.3 * Math.exp(2.5538 * quality);\n var computeVideoBitrate = (codec, width, height, factor) => {\n const pixels = width * height;\n const referencePixels = 1920 * 1080;\n const referenceBitrate = 3000000;\n const scaleFactor = Math.pow(pixels / referencePixels, 0.95);\n const baseBitrate = referenceBitrate * scaleFactor;\n const codecEfficiencyFactors = {\n avc: 1,\n hevc: 0.6,\n vp9: 0.6,\n av1: 0.4,\n vp8: 1.2,\n prores: 220000000 / referenceBitrate\n };\n const codecAdjustedBitrate = baseBitrate * codecEfficiencyFactors[codec];\n const finalBitrate = codecAdjustedBitrate * factor;\n return Math.ceil(finalBitrate / 1000) * 1000;\n };\n var buildQuantizerEncodeOptions = (codec, quantizer) => {\n if (codec === \"avc\") {\n return { avc: { quantizer } };\n } else if (codec === \"hevc\") {\n return { hevc: { quantizer } };\n } else if (codec === \"vp9\") {\n return { vp9: { quantizer } };\n } else if (codec === \"av1\") {\n return { av1: { quantizer } };\n }\n assert(false);\n };\n var QUALITY_LOW = /* @__PURE__ */ new Quality(\"low\");\n var QUALITY_MEDIUM = /* @__PURE__ */ new Quality(\"medium\");\n var QUALITY_HIGH = /* @__PURE__ */ new Quality(\"high\");\n var QUALITY_VERY_HIGH = /* @__PURE__ */ new Quality(\"very-high\");\n var canEncodeVideo = async (codec, options = {}) => {\n const {\n width = 1280,\n height = 720,\n quality,\n bitrate,\n frameRate,\n ...restOptions\n } = options;\n if (!VIDEO_CODECS.includes(codec)) {\n return false;\n }\n if (!Number.isInteger(width) || width <= 0) {\n throw new TypeError(\"width must be a positive integer.\");\n }\n if (!Number.isInteger(height) || height <= 0) {\n throw new TypeError(\"height must be a positive integer.\");\n }\n if (quality !== undefined && !(quality instanceof Quality)) {\n throw new TypeError(\"quality, when provided, must be a Quality.\");\n }\n if (quality !== undefined && bitrate !== undefined) {\n throw new TypeError(\"quality and bitrate cannot both be provided.\");\n }\n if (bitrate !== undefined && !(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {\n throw new TypeError(\"bitrate must be a positive integer or a quality.\");\n }\n if (frameRate !== undefined && (!Number.isFinite(frameRate) || frameRate <= 0)) {\n throw new TypeError(\"frameRate, when provided, must be a finite positive number.\");\n }\n validateVideoEncodingAdditionalOptions(codec, restOptions);\n const resolvedQuality = resolveQuality(quality, bitrate) ?? new Quality(\"medium\");\n let candidates;\n try {\n candidates = buildVideoEncoderConfigs({\n codec,\n width,\n height,\n quality: resolvedQuality,\n framerate: frameRate,\n ...restOptions,\n alpha: \"discard\"\n });\n } catch {\n return false;\n }\n const key = JSON.stringify(candidates);\n const memoized = canEncodeVideoMemo.get(key);\n if (memoized) {\n return memoized;\n }\n const promise = (async () => {\n for (const { config } of candidates) {\n if (customVideoEncoders.some((x) => x.supports(codec, config))) {\n return true;\n }\n }\n if (typeof VideoEncoder === \"undefined\") {\n return false;\n }\n const hasOddDimension = width % 2 === 1 || height % 2 === 1;\n if (hasOddDimension && (codec === \"avc\" || codec === \"hevc\")) {\n return false;\n }\n for (const { config, quantizer } of candidates) {\n try {\n const support = await VideoEncoder.isConfigSupported(config);\n if (!support.supported) {\n continue;\n }\n } catch {\n continue;\n }\n if (!isFirefox()) {\n return true;\n }\n const success = await new Promise(async (resolve) => {\n try {\n const encoder = new VideoEncoder({\n output: () => {},\n error: () => resolve(false)\n });\n encoder.configure(config);\n const frameData = new Uint8Array(width * height * 4);\n const frame = new VideoFrame(frameData, {\n format: \"RGBA\",\n codedWidth: width,\n codedHeight: height,\n timestamp: 0\n });\n encoder.encode(frame, quantizer !== null ? buildQuantizerEncodeOptions(codec, quantizer) : undefined);\n frame.close();\n await encoder.flush();\n resolve(true);\n } catch {\n resolve(false);\n }\n });\n if (success) {\n return true;\n }\n }\n return false;\n })();\n canEncodeVideoMemo.set(key, promise);\n return promise;\n };\n var resolveQuality = (quality, bitrate) => {\n if (quality !== undefined) {\n return quality;\n }\n if (bitrate === undefined) {\n return;\n }\n return bitrate instanceof Quality ? bitrate : new Quality({ bitrate });\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/custom-coder.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var customVideoEncoders = [];\n var customAudioEncoders = [];\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/pcm.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var toUlaw = (s16) => {\n const MULAW_MAX = 8191;\n const MULAW_BIAS = 33;\n let number = s16 >> 2;\n let mask = 4096;\n let sign = 0;\n let position = 12;\n let lsb = 0;\n if (number < 0) {\n number = -number;\n sign = 128;\n }\n number += MULAW_BIAS;\n if (number > MULAW_MAX) {\n number = MULAW_MAX;\n }\n while ((number & mask) !== mask && position >= 5) {\n mask >>= 1;\n position--;\n }\n lsb = number >> position - 4 & 15;\n return ~(sign | position - 5 << 4 | lsb) & 255;\n };\n var toAlaw = (s16) => {\n const ALAW_MAX = 4095;\n let mask = 2048;\n let sign = 0;\n let position = 11;\n let lsb = 0;\n let number = s16 >> 3;\n if (number < 0) {\n number = -number - 1;\n } else {\n sign = 128;\n }\n if (number > ALAW_MAX) {\n number = ALAW_MAX;\n }\n while ((number & mask) !== mask && position >= 5) {\n mask >>= 1;\n position--;\n }\n lsb = number >> (position === 4 ? 1 : position - 4) & 15;\n return (sign | position - 4 << 4 | lsb) ^ 85;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/reader.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n class FileSlice {\n constructor(bytes, view, offset, start, end) {\n this.bytes = bytes;\n this.view = view;\n this.offset = offset;\n this.start = start;\n this.end = end;\n this.bufferPos = start - offset;\n }\n static tempFromBytes(bytes) {\n return new FileSlice(bytes, toDataView(bytes), 0, 0, bytes.length);\n }\n get length() {\n return this.end - this.start;\n }\n get filePos() {\n return this.offset + this.bufferPos;\n }\n set filePos(value) {\n this.bufferPos = value - this.offset;\n }\n get remainingLength() {\n return Math.max(this.end - this.filePos, 0);\n }\n skip(byteCount) {\n this.bufferPos += byteCount;\n }\n slice(filePos, length = this.end - filePos) {\n if (filePos < this.start || filePos + length > this.end) {\n throw new RangeError(\"Slicing outside of original slice.\");\n }\n return new FileSlice(this.bytes, this.view, this.offset, filePos, filePos + length);\n }\n }\n var checkIsInRange = (slice, bytesToRead) => {\n if (slice.filePos < slice.start || slice.filePos + bytesToRead > slice.end) {\n throw new RangeError(`Tried reading [${slice.filePos}, ${slice.filePos + bytesToRead}), but slice is` + ` [${slice.start}, ${slice.end}). This is likely an internal error, please report it alongside the file` + ` that caused it.`);\n }\n };\n var readBytes = (slice, length) => {\n checkIsInRange(slice, length);\n const bytes = slice.bytes.subarray(slice.bufferPos, slice.bufferPos + length);\n slice.bufferPos += length;\n return bytes;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/muxer.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class Muxer {\n constructor(output) {\n this.mutex = new AsyncMutex;\n this.trackTimestampInfo = new WeakMap;\n this.output = output;\n }\n onTrackClose(track) {}\n validateTimestamp(track, timestampInSeconds, isKeyPacket) {\n let timestampInfo = this.trackTimestampInfo.get(track);\n if (!timestampInfo) {\n if (!isKeyPacket) {\n throw new Error(\"First packet must be a key packet.\");\n }\n timestampInfo = {\n maxTimestamp: timestampInSeconds,\n maxTimestampBeforeLastKeyPacket: null\n };\n this.trackTimestampInfo.set(track, timestampInfo);\n } else {\n if (isKeyPacket) {\n timestampInfo.maxTimestampBeforeLastKeyPacket = timestampInfo.maxTimestamp;\n }\n if (timestampInfo.maxTimestampBeforeLastKeyPacket !== null && timestampInSeconds < timestampInfo.maxTimestampBeforeLastKeyPacket) {\n throw new Error(`Timestamps cannot be smaller than the largest timestamp of the previous GOP (a GOP begins with a` + ` key packet and ends right before the next key packet). Got ${timestampInSeconds}s, but largest` + ` timestamp is ${timestampInfo.maxTimestampBeforeLastKeyPacket}s.`);\n }\n timestampInfo.maxTimestamp = Math.max(timestampInfo.maxTimestamp, timestampInSeconds);\n }\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/subtitles.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var inlineTimestampRegex = /<(?:(\\d{2}):)?(\\d{2}):(\\d{2}).(\\d{3})>/g;\n var formatSubtitleTimestamp = (timestamp) => {\n const hours = Math.floor(timestamp / (60 * 60 * 1000));\n const minutes = Math.floor(timestamp % (60 * 60 * 1000) / (60 * 1000));\n const seconds = Math.floor(timestamp % (60 * 1000) / 1000);\n const milliseconds = timestamp % 1000;\n return hours.toString().padStart(2, \"0\") + \":\" + minutes.toString().padStart(2, \"0\") + \":\" + seconds.toString().padStart(2, \"0\") + \".\" + milliseconds.toString().padStart(3, \"0\");\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-boxes.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class IsobmffBoxWriter {\n constructor(writer) {\n this.writer = writer;\n this.helper = new Uint8Array(8);\n this.helperView = new DataView(this.helper.buffer);\n this.offsets = new WeakMap;\n }\n writeU32(value) {\n this.helperView.setUint32(0, value, false);\n this.writer.write(this.helper.subarray(0, 4));\n }\n writeU64(value) {\n this.helperView.setUint32(0, Math.floor(value / 2 ** 32), false);\n this.helperView.setUint32(4, value, false);\n this.writer.write(this.helper.subarray(0, 8));\n }\n writeAscii(text) {\n for (let i = 0;i < text.length; i++) {\n this.helperView.setUint8(i % 8, text.charCodeAt(i));\n if (i % 8 === 7)\n this.writer.write(this.helper);\n }\n if (text.length % 8 !== 0) {\n this.writer.write(this.helper.subarray(0, text.length % 8));\n }\n }\n writeBox(box) {\n this.offsets.set(box, this.writer.getPos());\n if (box.contents && !box.children) {\n this.writeBoxHeader(box, box.size ?? box.contents.byteLength + 8);\n this.writer.write(box.contents);\n } else {\n const startPos = this.writer.getPos();\n this.writeBoxHeader(box, 0);\n if (box.contents)\n this.writer.write(box.contents);\n if (box.children) {\n for (const child of box.children)\n if (child)\n this.writeBox(child);\n }\n const endPos = this.writer.getPos();\n const size = box.size ?? endPos - startPos;\n this.writer.seek(startPos);\n this.writeBoxHeader(box, size);\n this.writer.seek(endPos);\n }\n }\n writeBoxHeader(box, size) {\n this.writeU32(box.largeSize ? 1 : size);\n this.writeAscii(box.type);\n if (box.largeSize)\n this.writeU64(size);\n }\n measureBoxHeader(box) {\n return 8 + (box.largeSize ? 8 : 0);\n }\n patchBox(box) {\n const boxOffset = this.offsets.get(box);\n assert(boxOffset !== undefined);\n const endPos = this.writer.getPos();\n this.writer.seek(boxOffset);\n this.writeBox(box);\n this.writer.seek(endPos);\n }\n measureBox(box) {\n if (box.contents && !box.children) {\n const headerSize = this.measureBoxHeader(box);\n return headerSize + box.contents.byteLength;\n } else {\n let result = this.measureBoxHeader(box);\n if (box.contents)\n result += box.contents.byteLength;\n if (box.children) {\n for (const child of box.children)\n if (child)\n result += this.measureBox(child);\n }\n return result;\n }\n }\n }\n var bytes = /* @__PURE__ */ new Uint8Array(8);\n var view = /* @__PURE__ */ new DataView(bytes.buffer);\n var u8 = (value) => {\n return [(value % 256 + 256) % 256];\n };\n var u16 = (value) => {\n view.setUint16(0, value, false);\n return [bytes[0], bytes[1]];\n };\n var i16 = (value) => {\n view.setInt16(0, value, false);\n return [bytes[0], bytes[1]];\n };\n var u24 = (value) => {\n view.setUint32(0, value, false);\n return [bytes[1], bytes[2], bytes[3]];\n };\n var u32 = (value) => {\n view.setUint32(0, value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3]];\n };\n var i32 = (value) => {\n view.setInt32(0, value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3]];\n };\n var u64 = (value) => {\n view.setUint32(0, Math.floor(value / 2 ** 32), false);\n view.setUint32(4, value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]];\n };\n var i64 = (value) => {\n view.setInt32(0, Math.floor(value / 2 ** 32), false);\n view.setUint32(4, value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]];\n };\n var fixed_8_8 = (value) => {\n view.setInt16(0, 2 ** 8 * value, false);\n return [bytes[0], bytes[1]];\n };\n var fixed_16_16 = (value) => {\n view.setInt32(0, 2 ** 16 * value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3]];\n };\n var fixed_2_30 = (value) => {\n view.setInt32(0, 2 ** 30 * value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3]];\n };\n var variableUnsignedInt = (value, byteLength) => {\n const bytes = [];\n let remaining = value;\n do {\n let byte = remaining & 127;\n remaining >>= 7;\n if (bytes.length > 0) {\n byte |= 128;\n }\n bytes.push(byte);\n if (byteLength !== undefined) {\n byteLength--;\n }\n } while (remaining > 0 || byteLength);\n return bytes.reverse();\n };\n var ascii = (text, nullTerminated = false) => {\n const bytes = Array(text.length).fill(null).map((_, i) => text.charCodeAt(i));\n if (nullTerminated)\n bytes.push(0);\n return bytes;\n };\n var matrixToBytes = (matrix) => {\n return [\n fixed_16_16(matrix[0]),\n fixed_16_16(matrix[1]),\n fixed_2_30(matrix[2]),\n fixed_16_16(matrix[3]),\n fixed_16_16(matrix[4]),\n fixed_2_30(matrix[5]),\n fixed_16_16(matrix[6]),\n fixed_16_16(matrix[7]),\n fixed_2_30(matrix[8])\n ];\n };\n var box = (type, contents, children) => ({\n type,\n contents: contents && new Uint8Array(contents.flat(10)),\n children\n });\n var fullBox = (type, version, flags, contents, children) => box(type, [u8(version), u24(flags), contents ?? []], children);\n var ftyp = (details) => {\n const minorVersion = 512;\n if (details.isQuickTime) {\n return box(\"ftyp\", [\n ascii(\"qt \"),\n u32(minorVersion),\n ascii(\"qt \")\n ]);\n }\n if (details.fragmented) {\n if (details.cmaf) {\n return box(\"ftyp\", [\n ascii(\"iso5\"),\n u32(minorVersion),\n ascii(\"iso5\"),\n ascii(\"iso6\"),\n ascii(\"mp41\"),\n ascii(\"cmfc\"),\n ascii(\"dash\")\n ]);\n } else {\n return box(\"ftyp\", [\n ascii(\"iso5\"),\n u32(minorVersion),\n ascii(\"iso5\"),\n ascii(\"iso6\"),\n ascii(\"mp41\")\n ]);\n }\n }\n return box(\"ftyp\", [\n ascii(\"isom\"),\n u32(minorVersion),\n ascii(\"isom\"),\n details.holdsAvc ? ascii(\"avc1\") : [],\n ascii(\"mp41\")\n ]);\n };\n var styp = () => box(\"styp\", [\n ascii(\"iso5\"),\n u32(0),\n ascii(\"iso5\"),\n ascii(\"iso6\"),\n ascii(\"mp41\"),\n ascii(\"cmfc\"),\n ascii(\"dash\")\n ]);\n var sidx = (muxer, referencedSize) => {\n const earliestPresentationTime = Math.max(0, muxer.minWrittenTimestamp);\n let duration = Math.max(0, muxer.maxWrittenEndTimestamp - earliestPresentationTime);\n if (!Number.isFinite(duration)) {\n duration = 0;\n }\n return fullBox(\"sidx\", 1, 0, [\n u32(1),\n u32(GLOBAL_TIMESCALE),\n u64(intoTimescale(earliestPresentationTime, GLOBAL_TIMESCALE)),\n u64(0),\n u16(0),\n u16(1),\n u32(referencedSize & 2147483647),\n u32(intoTimescale(duration, GLOBAL_TIMESCALE)),\n u32(0)\n ]);\n };\n var mdat = (reserveLargeSize) => ({ type: \"mdat\", largeSize: reserveLargeSize });\n var free = (size) => ({ type: \"free\", size });\n var moov = (muxer) => {\n return box(\"moov\", undefined, [\n mvhd(muxer.creationTime, muxer.trackDatas),\n ...muxer.trackDatas.map((x) => trak(x, muxer.creationTime)),\n muxer.isFragmented ? mvex(muxer.trackDatas) : null,\n udta(muxer)\n ]);\n };\n var mvhd = (creationTime, trackDatas) => {\n const duration = Math.max(0, ...trackDatas.map((trackData) => Math.max(0, intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(trackData.startTimestampOffset ?? 0, GLOBAL_TIMESCALE))));\n const nextTrackId = Math.max(0, ...trackDatas.map((x) => x.track.id)) + 1;\n const needsU64 = !isU32(creationTime) || !isU32(duration);\n const u32OrU64 = needsU64 ? u64 : u32;\n return fullBox(\"mvhd\", +needsU64, 0, [\n u32OrU64(creationTime),\n u32OrU64(creationTime),\n u32(GLOBAL_TIMESCALE),\n u32OrU64(duration),\n fixed_16_16(1),\n fixed_8_8(1),\n Array(10).fill(0),\n matrixToBytes(IDENTITY_MATRIX),\n Array(24).fill(0),\n u32(nextTrackId)\n ]);\n };\n var trak = (trackData, creationTime) => {\n const trackMetadata = getTrackMetadata(trackData);\n const needsEditList = trackData.startTimestampOffset !== null && trackData.startTimestampOffset !== 0;\n return box(\"trak\", undefined, [\n tkhd(trackData, creationTime),\n needsEditList ? edts(trackData) : null,\n mdia(trackData, creationTime),\n trackMetadata.name !== undefined ? box(\"udta\", undefined, [\n box(\"name\", [\n ...textEncoder.encode(trackMetadata.name)\n ])\n ]) : null\n ]);\n };\n var tkhd = (trackData, creationTime) => {\n const durationInGlobalTimescale = Math.max(0, intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(trackData.startTimestampOffset ?? 0, GLOBAL_TIMESCALE));\n const needsU64 = !isU32(creationTime) || !isU32(durationInGlobalTimescale);\n const u32OrU64 = needsU64 ? u64 : u32;\n let matrix;\n if (trackData.type === \"video\" && trackData.track.metadata.transformationMatrix) {\n matrix = trackData.track.metadata.transformationMatrix;\n } else if (trackData.type === \"video\") {\n const { rotation, flip } = trackData.track.metadata;\n const linear = multiplyMatrices(rotationMatrix(rotation ?? 0), scaleMatrix(flip ? -1 : 1, 1));\n matrix = centeredTransformationMatrix(linear, trackData.info.width, trackData.info.height);\n } else {\n matrix = IDENTITY_MATRIX;\n }\n let flags = 2;\n if (trackData.track.metadata.disposition?.default !== false) {\n flags |= 1;\n }\n const alternateGroup = trackData.type === \"video\" ? 0 : trackData.type === \"audio\" ? 1 : trackData.type === \"subtitle\" ? 2 : assertNever(trackData);\n return fullBox(\"tkhd\", +needsU64, flags, [\n u32OrU64(creationTime),\n u32OrU64(creationTime),\n u32(trackData.track.id),\n u32(0),\n u32OrU64(durationInGlobalTimescale),\n Array(8).fill(0),\n u16(0),\n u16(alternateGroup),\n fixed_8_8(trackData.type === \"audio\" ? 1 : 0),\n u16(0),\n matrixToBytes(matrix),\n fixed_16_16(trackData.type === \"video\" ? trackData.info.width : 0),\n fixed_16_16(trackData.type === \"video\" ? trackData.info.height : 0)\n ]);\n };\n var edts = (trackData) => {\n const offset = trackData.startTimestampOffset;\n assert(offset !== null);\n if (offset > 0) {\n const startOffset = intoTimescale(offset, GLOBAL_TIMESCALE);\n const mediaDuration = intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE);\n const needs64Bits = !isU32(startOffset) || !isU32(mediaDuration);\n const u32OrU64 = needs64Bits ? u64 : u32;\n const i32OrI64 = needs64Bits ? i64 : i32;\n return box(\"edts\", undefined, [\n fullBox(\"elst\", needs64Bits ? 1 : 0, 0, [\n u32(2),\n u32OrU64(startOffset),\n i32OrI64(-1),\n fixed_16_16(1),\n u32OrU64(mediaDuration),\n i32OrI64(0),\n fixed_16_16(1)\n ])\n ]);\n } else {\n const mediaTime = intoTimescale(-offset, trackData.timescale);\n const mediaDuration = Math.max(0, intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(offset, GLOBAL_TIMESCALE));\n const needs64Bits = !isI32(mediaTime) || !isU32(mediaDuration);\n const u32OrU64 = needs64Bits ? u64 : u32;\n const i32OrI64 = needs64Bits ? i64 : i32;\n return box(\"edts\", undefined, [\n fullBox(\"elst\", needs64Bits ? 1 : 0, 0, [\n u32(1),\n u32OrU64(mediaDuration),\n i32OrI64(mediaTime),\n fixed_16_16(1)\n ])\n ]);\n }\n };\n var mdia = (trackData, creationTime) => box(\"mdia\", undefined, [\n mdhd(trackData, creationTime),\n hdlr(true, TRACK_TYPE_TO_COMPONENT_SUBTYPE[trackData.type], TRACK_TYPE_TO_HANDLER_NAME[trackData.type]),\n minf(trackData)\n ]);\n var mdhd = (trackData, creationTime) => {\n const localDuration = intoTimescale(presentationSpan(trackData), trackData.timescale);\n const needsU64 = !isU32(creationTime) || !isU32(localDuration);\n const u32OrU64 = needsU64 ? u64 : u32;\n return fullBox(\"mdhd\", +needsU64, 0, [\n u32OrU64(creationTime),\n u32OrU64(creationTime),\n u32(trackData.timescale),\n u32OrU64(localDuration),\n u16(getLanguageCodeInt(trackData.track.metadata.languageCode ?? UNDETERMINED_LANGUAGE)),\n u16(0)\n ]);\n };\n var TRACK_TYPE_TO_COMPONENT_SUBTYPE = {\n video: \"vide\",\n audio: \"soun\",\n subtitle: \"text\"\n };\n var TRACK_TYPE_TO_HANDLER_NAME = {\n video: \"MediabunnyVideoHandler\",\n audio: \"MediabunnySoundHandler\",\n subtitle: \"MediabunnyTextHandler\"\n };\n var hdlr = (hasComponentType, handlerType, name, manufacturer = \"\\x00\\x00\\x00\\x00\") => fullBox(\"hdlr\", 0, 0, [\n hasComponentType ? ascii(\"mhlr\") : u32(0),\n ascii(handlerType),\n ascii(manufacturer),\n u32(0),\n u32(0),\n ascii(name, true)\n ]);\n var minf = (trackData) => box(\"minf\", undefined, [\n TRACK_TYPE_TO_HEADER_BOX[trackData.type](),\n dinf(),\n stbl(trackData)\n ]);\n var vmhd = () => fullBox(\"vmhd\", 0, 1, [\n u16(0),\n u16(0),\n u16(0),\n u16(0)\n ]);\n var smhd = () => fullBox(\"smhd\", 0, 0, [\n u16(0),\n u16(0)\n ]);\n var nmhd = () => fullBox(\"nmhd\", 0, 0);\n var TRACK_TYPE_TO_HEADER_BOX = {\n video: vmhd,\n audio: smhd,\n subtitle: nmhd\n };\n var dinf = () => box(\"dinf\", undefined, [\n dref()\n ]);\n var dref = () => fullBox(\"dref\", 0, 0, [\n u32(1)\n ], [\n url()\n ]);\n var url = () => fullBox(\"url \", 0, 1);\n var stbl = (trackData) => {\n const needsCtts = trackData.compositionTimeOffsetTable.length > 1 || trackData.compositionTimeOffsetTable.some((x) => x.sampleCompositionTimeOffset !== 0);\n return box(\"stbl\", undefined, [\n stsd(trackData),\n stts(trackData),\n needsCtts ? ctts(trackData) : null,\n needsCtts ? cslg(trackData) : null,\n stsc(trackData),\n stsz(trackData),\n stco(trackData),\n stss(trackData)\n ]);\n };\n var stsd = (trackData) => {\n let sampleDescription;\n if (trackData.type === \"video\") {\n sampleDescription = videoSampleDescription(videoCodecToBoxName(trackData.track.source._codec, trackData.info.decoderConfig.codec), trackData);\n } else if (trackData.type === \"audio\") {\n const boxName = audioCodecToBoxName(trackData.track.source._codec, trackData.info.decoderConfig.codec, trackData.muxer.isQuickTime);\n assert(boxName);\n sampleDescription = soundSampleDescription(boxName, trackData);\n } else if (trackData.type === \"subtitle\") {\n sampleDescription = subtitleSampleDescription(SUBTITLE_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData);\n }\n assert(sampleDescription);\n return fullBox(\"stsd\", 0, 0, [\n u32(1)\n ], [\n sampleDescription\n ]);\n };\n var videoSampleDescription = (compressionType, trackData) => box(compressionType, [\n Array(6).fill(0),\n u16(1),\n u16(0),\n u16(0),\n Array(12).fill(0),\n u16(trackData.info.width),\n u16(trackData.info.height),\n u32(4718592),\n u32(4718592),\n u32(0),\n u16(1),\n u8(VERSIONED_NAME.length),\n ascii(VERSIONED_NAME),\n Array(31 - VERSIONED_NAME.length).fill(0),\n u16(trackData.info.hasAlphaChannel ? 32 : 24),\n i16(65535)\n ], [\n VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec]?.(trackData) ?? null,\n pasp(trackData),\n colorSpaceIsEmpty(trackData.info.decoderConfig.colorSpace) ? null : colr(trackData),\n btrt(trackData)\n ]);\n var btrt = (trackData) => {\n if (trackData.avgBitrate === 0 && trackData.maxBitrate === 0) {\n return null;\n }\n return box(\"btrt\", [\n u32(0),\n u32(trackData.maxBitrate),\n u32(trackData.avgBitrate)\n ]);\n };\n var pasp = (trackData) => {\n if (trackData.info.pixelAspectRatio.num === trackData.info.pixelAspectRatio.den) {\n return null;\n }\n return box(\"pasp\", [\n u32(trackData.info.pixelAspectRatio.num),\n u32(trackData.info.pixelAspectRatio.den)\n ]);\n };\n var colr = (trackData) => {\n const colorSpace = trackData.info.decoderConfig.colorSpace;\n return box(\"colr\", [\n ascii(trackData.muxer.isQuickTime ? \"nclc\" : \"nclx\"),\n u16(colorSpace?.primaries != null ? COLOR_PRIMARIES_MAP[colorSpace.primaries] : 2),\n u16(colorSpace?.transfer != null ? TRANSFER_CHARACTERISTICS_MAP[colorSpace.transfer] : 2),\n u16(colorSpace?.matrix != null ? MATRIX_COEFFICIENTS_MAP[colorSpace.matrix] : 2),\n trackData.muxer.isQuickTime ? [] : u8((colorSpace?.fullRange ? 1 : 0) << 7)\n ]);\n };\n var avcC = (trackData) => trackData.info.decoderConfig && box(\"avcC\", [\n ...toUint8Array(trackData.info.decoderConfig.description)\n ]);\n var hvcC = (trackData) => trackData.info.decoderConfig && box(\"hvcC\", [\n ...toUint8Array(trackData.info.decoderConfig.description)\n ]);\n var vpcC = (trackData) => {\n if (!trackData.info.decoderConfig) {\n return null;\n }\n const decoderConfig = trackData.info.decoderConfig;\n const parts = decoderConfig.codec.split(\".\");\n const profile = Number(parts[1]);\n const level = Number(parts[2]);\n const bitDepth = Number(parts[3]);\n const chromaSubsampling = parts[4] ? Number(parts[4]) : 1;\n const videoFullRangeFlag = parts[8] ? Number(parts[8]) : Number(decoderConfig.colorSpace?.fullRange ?? 0);\n const thirdByte = (bitDepth << 4) + (chromaSubsampling << 1) + videoFullRangeFlag;\n const colourPrimaries = parts[5] ? Number(parts[5]) : decoderConfig.colorSpace?.primaries ? COLOR_PRIMARIES_MAP[decoderConfig.colorSpace.primaries] : 1;\n const transferCharacteristics = parts[6] ? Number(parts[6]) : decoderConfig.colorSpace?.transfer ? TRANSFER_CHARACTERISTICS_MAP[decoderConfig.colorSpace.transfer] : 1;\n const matrixCoefficients = parts[7] ? Number(parts[7]) : decoderConfig.colorSpace?.matrix ? MATRIX_COEFFICIENTS_MAP[decoderConfig.colorSpace.matrix] : 1;\n return fullBox(\"vpcC\", 1, 0, [\n u8(profile),\n u8(level),\n u8(thirdByte),\n u8(colourPrimaries),\n u8(transferCharacteristics),\n u8(matrixCoefficients),\n u16(0)\n ]);\n };\n var av1C = (trackData) => {\n return box(\"av1C\", generateAv1CodecConfigurationFromCodecString(trackData.info.decoderConfig.codec));\n };\n var soundSampleDescription = (compressionType, trackData) => {\n let version = 0;\n let contents;\n let sampleSizeInBits = 16;\n const isPcmCodec = PCM_AUDIO_CODECS.includes(trackData.track.source._codec);\n if (isPcmCodec) {\n const codec = trackData.track.source._codec;\n const { sampleSize } = parsePcmCodec(codec);\n sampleSizeInBits = 8 * sampleSize;\n if (sampleSizeInBits > 16) {\n version = 1;\n }\n }\n if (trackData.muxer.isQuickTime) {\n version = 1;\n }\n if (version === 0) {\n contents = [\n Array(6).fill(0),\n u16(1),\n u16(version),\n u16(0),\n u32(0),\n u16(trackData.info.numberOfChannels),\n u16(sampleSizeInBits),\n u16(0),\n u16(0),\n u16(trackData.info.sampleRate < 2 ** 16 ? trackData.info.sampleRate : 0),\n u16(0)\n ];\n } else {\n const compressionId = isPcmCodec ? 0 : -2;\n contents = [\n Array(6).fill(0),\n u16(1),\n u16(version),\n u16(0),\n u32(0),\n u16(trackData.info.numberOfChannels),\n u16(Math.min(sampleSizeInBits, 16)),\n i16(compressionId),\n u16(0),\n u16(trackData.info.sampleRate < 2 ** 16 ? trackData.info.sampleRate : 0),\n u16(0),\n isPcmCodec ? [\n u32(1),\n u32(sampleSizeInBits / 8),\n u32(trackData.info.numberOfChannels * sampleSizeInBits / 8)\n ] : [\n u32(0),\n u32(0),\n u32(0)\n ],\n u32(2)\n ];\n }\n return box(compressionType, contents, [\n audioCodecToConfigurationBox(trackData.track.source._codec, trackData.muxer.isQuickTime)?.(trackData) ?? null,\n btrt(trackData)\n ]);\n };\n var esds = (trackData) => {\n let objectTypeIndication;\n switch (trackData.track.source._codec) {\n case \"aac\":\n {\n objectTypeIndication = 64;\n }\n ;\n break;\n case \"mp3\":\n {\n objectTypeIndication = 107;\n }\n ;\n break;\n case \"vorbis\":\n {\n objectTypeIndication = 221;\n }\n ;\n break;\n default:\n throw new Error(`Unhandled audio codec: ${trackData.track.source._codec}`);\n }\n let bytes = [\n ...u8(objectTypeIndication),\n ...u8(21),\n ...u24(0),\n ...u32(trackData.maxBitrate),\n ...u32(trackData.avgBitrate)\n ];\n if (trackData.info.decoderConfig.description) {\n const description = toUint8Array(trackData.info.decoderConfig.description);\n bytes = [\n ...bytes,\n ...u8(5),\n ...variableUnsignedInt(description.byteLength),\n ...description\n ];\n }\n bytes = [\n ...u16(1),\n ...u8(0),\n ...u8(4),\n ...variableUnsignedInt(bytes.length),\n ...bytes,\n ...u8(6),\n ...u8(1),\n ...u8(2)\n ];\n bytes = [\n ...u8(3),\n ...variableUnsignedInt(bytes.length),\n ...bytes\n ];\n return fullBox(\"esds\", 0, 0, bytes);\n };\n var wave = (trackData) => {\n return box(\"wave\", undefined, [\n frma(trackData),\n enda(trackData),\n box(\"\\x00\\x00\\x00\\x00\")\n ]);\n };\n var frma = (trackData) => {\n return box(\"frma\", [\n ascii(audioCodecToBoxName(trackData.track.source._codec, trackData.info.decoderConfig.codec, trackData.muxer.isQuickTime))\n ]);\n };\n var enda = (trackData) => {\n const { littleEndian } = parsePcmCodec(trackData.track.source._codec);\n return box(\"enda\", [\n u16(+littleEndian)\n ]);\n };\n var dOps = (trackData) => {\n let outputChannelCount = trackData.info.numberOfChannels;\n let preSkip = 3840;\n let inputSampleRate = trackData.info.sampleRate;\n let outputGain = 0;\n let channelMappingFamily = 0;\n let channelMappingTable = new Uint8Array(0);\n const description = trackData.info.decoderConfig?.description;\n if (description) {\n assert(description.byteLength >= 18);\n const bytes = toUint8Array(description);\n const header = parseOpusIdentificationHeader(bytes);\n outputChannelCount = header.outputChannelCount;\n preSkip = header.preSkip;\n inputSampleRate = header.inputSampleRate;\n outputGain = header.outputGain;\n channelMappingFamily = header.channelMappingFamily;\n if (header.channelMappingTable) {\n channelMappingTable = header.channelMappingTable;\n }\n }\n return box(\"dOps\", [\n u8(0),\n u8(outputChannelCount),\n u16(preSkip),\n u32(inputSampleRate),\n i16(outputGain),\n u8(channelMappingFamily),\n ...channelMappingTable\n ]);\n };\n var dfLa = (trackData) => {\n const description = trackData.info.decoderConfig?.description;\n assert(description);\n const bytes = toUint8Array(description);\n return fullBox(\"dfLa\", 0, 0, [\n ...bytes.subarray(4)\n ]);\n };\n var pcmC = (trackData) => {\n const { littleEndian, sampleSize } = parsePcmCodec(trackData.track.source._codec);\n const formatFlags = +littleEndian;\n return fullBox(\"pcmC\", 0, 0, [\n u8(formatFlags),\n u8(8 * sampleSize)\n ]);\n };\n var dac3 = (trackData) => {\n assert(trackData.info.primingPacket);\n const frameInfo = parseAc3SyncFrame(trackData.info.primingPacket.data);\n if (!frameInfo) {\n throw new Error(\"Couldn't extract AC-3 frame info from the audio packet. \" + \"Ensure the packets contain valid AC-3 sync frames (as specified in ETSI TS 102 366).\");\n }\n const bytes = new Uint8Array(3);\n const bitstream = new Bitstream(bytes);\n bitstream.writeBits(2, frameInfo.fscod);\n bitstream.writeBits(5, frameInfo.bsid);\n bitstream.writeBits(3, frameInfo.bsmod);\n bitstream.writeBits(3, frameInfo.acmod);\n bitstream.writeBits(1, frameInfo.lfeon);\n bitstream.writeBits(5, frameInfo.bitRateCode);\n bitstream.writeBits(5, 0);\n return box(\"dac3\", [...bytes]);\n };\n var dec3 = (trackData) => {\n assert(trackData.info.primingPacket);\n const frameInfo = parseEac3SyncFrame(trackData.info.primingPacket.data);\n if (!frameInfo) {\n throw new Error(\"Couldn't extract E-AC-3 frame info from the audio packet. \" + \"Ensure the packets contain valid E-AC-3 sync frames (as specified in ETSI TS 102 366).\");\n }\n let totalBits = 16;\n for (const sub of frameInfo.substreams) {\n totalBits += 23;\n if (sub.numDepSub > 0) {\n totalBits += 9;\n } else {\n totalBits += 1;\n }\n }\n const size = Math.ceil(totalBits / 8);\n const bytes = new Uint8Array(size);\n const bitstream = new Bitstream(bytes);\n bitstream.writeBits(13, frameInfo.dataRate);\n bitstream.writeBits(3, frameInfo.substreams.length - 1);\n for (const sub of frameInfo.substreams) {\n bitstream.writeBits(2, sub.fscod);\n bitstream.writeBits(5, sub.bsid);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(3, sub.bsmod);\n bitstream.writeBits(3, sub.acmod);\n bitstream.writeBits(1, sub.lfeon);\n bitstream.writeBits(3, 0);\n bitstream.writeBits(4, sub.numDepSub);\n if (sub.numDepSub > 0) {\n bitstream.writeBits(9, sub.chanLoc);\n } else {\n bitstream.writeBits(1, 0);\n }\n }\n return box(\"dec3\", [...bytes]);\n };\n var ddts = (trackData) => {\n assert(trackData.info.primingPacket);\n const frameInfo = parseDtsFrame(trackData.info.primingPacket.data);\n if (!frameInfo) {\n throw new Error(\"Couldn't extract DTS frame info from the audio packet. \" + \"Ensure the packets contain valid DTS frames as specified in ETSI TS 102 114.\");\n }\n return box(\"ddts\", [...buildDtsSpecificBox(frameInfo)]);\n };\n var subtitleSampleDescription = (compressionType, trackData) => box(compressionType, [\n Array(6).fill(0),\n u16(1)\n ], [\n SUBTITLE_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData),\n btrt(trackData)\n ]);\n var vttC = (trackData) => box(\"vttC\", [\n ...textEncoder.encode(trackData.info.config.description)\n ]);\n var stts = (trackData) => {\n return fullBox(\"stts\", 0, 0, [\n u32(trackData.timeToSampleTable.length),\n trackData.timeToSampleTable.map((x) => [\n u32(x.sampleCount),\n u32(x.sampleDelta)\n ])\n ]);\n };\n var stss = (trackData) => {\n if (trackData.samples.every((x) => x.type === \"key\"))\n return null;\n const keySamples = [...trackData.samples.entries()].filter(([, sample]) => sample.type === \"key\");\n return fullBox(\"stss\", 0, 0, [\n u32(keySamples.length),\n keySamples.map(([index]) => u32(index + 1))\n ]);\n };\n var stsc = (trackData) => {\n return fullBox(\"stsc\", 0, 0, [\n u32(trackData.compactlyCodedChunkTable.length),\n trackData.compactlyCodedChunkTable.map((x) => [\n u32(x.firstChunk),\n u32(x.samplesPerChunk),\n u32(1)\n ])\n ]);\n };\n var stsz = (trackData) => {\n if (trackData.type === \"audio\" && trackData.info.requiresPcmTransformation) {\n const { sampleSize } = parsePcmCodec(trackData.track.source._codec);\n return fullBox(\"stsz\", 0, 0, [\n u32(sampleSize * trackData.info.numberOfChannels),\n u32(trackData.samples.reduce((acc, x) => acc + intoTimescale(x.duration, trackData.timescale), 0))\n ]);\n }\n return fullBox(\"stsz\", 0, 0, [\n u32(0),\n u32(trackData.samples.length),\n trackData.samples.map((x) => u32(x.size))\n ]);\n };\n var stco = (trackData) => {\n if (trackData.finalizedChunks.length > 0 && last(trackData.finalizedChunks).offset >= 2 ** 32) {\n return fullBox(\"co64\", 0, 0, [\n u32(trackData.finalizedChunks.length),\n trackData.finalizedChunks.map((x) => u64(x.offset))\n ]);\n }\n return fullBox(\"stco\", 0, 0, [\n u32(trackData.finalizedChunks.length),\n trackData.finalizedChunks.map((x) => u32(x.offset))\n ]);\n };\n var ctts = (trackData) => {\n return fullBox(\"ctts\", 1, 0, [\n u32(trackData.compositionTimeOffsetTable.length),\n trackData.compositionTimeOffsetTable.map((x) => [\n u32(x.sampleCount),\n i32(x.sampleCompositionTimeOffset)\n ])\n ]);\n };\n var cslg = (trackData) => {\n let leastDecodeToDisplayDelta = Infinity;\n let greatestDecodeToDisplayDelta = -Infinity;\n let compositionStartTime = Infinity;\n let compositionEndTime = -Infinity;\n assert(trackData.compositionTimeOffsetTable.length > 0);\n assert(trackData.samples.length > 0);\n for (let i = 0;i < trackData.compositionTimeOffsetTable.length; i++) {\n const entry = trackData.compositionTimeOffsetTable[i];\n leastDecodeToDisplayDelta = Math.min(leastDecodeToDisplayDelta, entry.sampleCompositionTimeOffset);\n greatestDecodeToDisplayDelta = Math.max(greatestDecodeToDisplayDelta, entry.sampleCompositionTimeOffset);\n }\n for (let i = 0;i < trackData.samples.length; i++) {\n const sample = trackData.samples[i];\n compositionStartTime = Math.min(compositionStartTime, intoTimescale(sample.timestamp, trackData.timescale));\n compositionEndTime = Math.max(compositionEndTime, intoTimescale(sample.timestamp + sample.duration, trackData.timescale));\n }\n const compositionToDtsShift = Math.max(-leastDecodeToDisplayDelta, 0);\n if (compositionEndTime >= 2 ** 31) {\n return null;\n }\n return fullBox(\"cslg\", 0, 0, [\n i32(compositionToDtsShift),\n i32(leastDecodeToDisplayDelta),\n i32(greatestDecodeToDisplayDelta),\n i32(compositionStartTime),\n i32(compositionEndTime)\n ]);\n };\n var mvex = (trackDatas) => {\n return box(\"mvex\", undefined, trackDatas.map(trex));\n };\n var trex = (trackData) => {\n return fullBox(\"trex\", 0, 0, [\n u32(trackData.track.id),\n u32(1),\n u32(0),\n u32(0),\n u32(0)\n ]);\n };\n var moof = (sequenceNumber, trackDatas) => {\n return box(\"moof\", undefined, [\n mfhd(sequenceNumber),\n ...trackDatas.map(traf)\n ]);\n };\n var mfhd = (sequenceNumber) => {\n return fullBox(\"mfhd\", 0, 0, [\n u32(sequenceNumber)\n ]);\n };\n var fragmentSampleFlags = (sample) => {\n let byte1 = 0;\n let byte2 = 0;\n const byte3 = 0;\n const byte4 = 0;\n const sampleIsDifferenceSample = sample.type === \"delta\";\n byte2 |= +sampleIsDifferenceSample;\n if (sampleIsDifferenceSample) {\n byte1 |= 1;\n } else {\n byte1 |= 2;\n }\n return byte1 << 24 | byte2 << 16 | byte3 << 8 | byte4;\n };\n var traf = (trackData) => {\n return box(\"traf\", undefined, [\n tfhd(trackData),\n tfdt(trackData),\n trun(trackData)\n ]);\n };\n var tfhd = (trackData) => {\n assert(trackData.currentChunk);\n let tfFlags = 0;\n tfFlags |= 8;\n tfFlags |= 16;\n tfFlags |= 32;\n tfFlags |= 131072;\n const referenceSample = trackData.currentChunk.samples[1] ?? trackData.currentChunk.samples[0];\n const referenceSampleInfo = {\n duration: referenceSample.timescaleUnitsToNextSample,\n size: referenceSample.size,\n flags: fragmentSampleFlags(referenceSample)\n };\n return fullBox(\"tfhd\", 0, tfFlags, [\n u32(trackData.track.id),\n u32(referenceSampleInfo.duration),\n u32(referenceSampleInfo.size),\n u32(referenceSampleInfo.flags)\n ]);\n };\n var tfdt = (trackData) => {\n assert(trackData.currentChunk);\n return fullBox(\"tfdt\", 1, 0, [\n u64(intoTimescale(trackData.currentChunk.startTimestamp, trackData.timescale))\n ]);\n };\n var trun = (trackData) => {\n assert(trackData.currentChunk);\n const allSampleDurations = trackData.currentChunk.samples.map((x) => x.timescaleUnitsToNextSample);\n const allSampleSizes = trackData.currentChunk.samples.map((x) => x.size);\n const allSampleFlags = trackData.currentChunk.samples.map(fragmentSampleFlags);\n const allSampleCompositionTimeOffsets = trackData.currentChunk.samples.map((x) => intoTimescale(x.timestamp - x.decodeTimestamp, trackData.timescale));\n const uniqueSampleDurations = new Set(allSampleDurations);\n const uniqueSampleSizes = new Set(allSampleSizes);\n const uniqueSampleFlags = new Set(allSampleFlags);\n const uniqueSampleCompositionTimeOffsets = new Set(allSampleCompositionTimeOffsets);\n const firstSampleFlagsPresent = uniqueSampleFlags.size === 2 && allSampleFlags[0] !== allSampleFlags[1];\n const sampleDurationPresent = uniqueSampleDurations.size > 1;\n const sampleSizePresent = uniqueSampleSizes.size > 1;\n const sampleFlagsPresent = !firstSampleFlagsPresent && uniqueSampleFlags.size > 1;\n const sampleCompositionTimeOffsetsPresent = uniqueSampleCompositionTimeOffsets.size > 1 || [...uniqueSampleCompositionTimeOffsets].some((x) => x !== 0);\n let flags = 0;\n flags |= 1;\n flags |= 4 * +firstSampleFlagsPresent;\n flags |= 256 * +sampleDurationPresent;\n flags |= 512 * +sampleSizePresent;\n flags |= 1024 * +sampleFlagsPresent;\n flags |= 2048 * +sampleCompositionTimeOffsetsPresent;\n return fullBox(\"trun\", 1, flags, [\n u32(trackData.currentChunk.samples.length),\n u32(trackData.currentChunk.offset - trackData.currentChunk.moofOffset || 0),\n firstSampleFlagsPresent ? u32(allSampleFlags[0]) : [],\n trackData.currentChunk.samples.map((_, i) => [\n sampleDurationPresent ? u32(allSampleDurations[i]) : [],\n sampleSizePresent ? u32(allSampleSizes[i]) : [],\n sampleFlagsPresent ? u32(allSampleFlags[i]) : [],\n sampleCompositionTimeOffsetsPresent ? i32(allSampleCompositionTimeOffsets[i]) : []\n ])\n ]);\n };\n var mfra = (trackDatas) => {\n return box(\"mfra\", undefined, [\n ...trackDatas.map(tfra),\n mfro()\n ]);\n };\n var tfra = (trackData) => {\n const version = 1;\n return fullBox(\"tfra\", version, 0, [\n u32(trackData.track.id),\n u32(63),\n u32(trackData.finalizedChunks.length),\n trackData.finalizedChunks.map((chunk) => [\n u64(intoTimescale(chunk.samples[0].timestamp, trackData.timescale)),\n u64(chunk.moofOffset),\n u32(chunk.trafIndex + 1),\n u32(1),\n u32(1)\n ])\n ]);\n };\n var mfro = () => {\n return fullBox(\"mfro\", 0, 0, [\n u32(0)\n ]);\n };\n var vtte = () => box(\"vtte\");\n var vttc = (payload, timestamp, identifier, settings, sourceId) => box(\"vttc\", undefined, [\n sourceId !== null ? box(\"vsid\", [i32(sourceId)]) : null,\n identifier !== null ? box(\"iden\", [...textEncoder.encode(identifier)]) : null,\n timestamp !== null ? box(\"ctim\", [...textEncoder.encode(formatSubtitleTimestamp(timestamp))]) : null,\n settings !== null ? box(\"sttg\", [...textEncoder.encode(settings)]) : null,\n box(\"payl\", [...textEncoder.encode(payload)])\n ]);\n var vtta = (notes) => box(\"vtta\", [...textEncoder.encode(notes)]);\n var udta = (muxer) => {\n const boxes = [];\n const metadataFormat = muxer.format._options.metadataFormat ?? \"auto\";\n const metadataTags = muxer.output._metadataTags;\n if (metadataFormat === \"mdir\" || metadataFormat === \"auto\" && !muxer.isQuickTime) {\n const metaBox = metaMdir(metadataTags);\n if (metaBox)\n boxes.push(metaBox);\n } else if (metadataFormat === \"mdta\") {\n const metaBox = metaMdta(metadataTags);\n if (metaBox)\n boxes.push(metaBox);\n } else if (metadataFormat === \"udta\" || metadataFormat === \"auto\" && muxer.isQuickTime) {\n addQuickTimeMetadataTagBoxes(boxes, muxer.output._metadataTags);\n }\n if (boxes.length === 0) {\n return null;\n }\n return box(\"udta\", undefined, boxes);\n };\n var addQuickTimeMetadataTagBoxes = (boxes, tags) => {\n for (const { key, value } of keyValueIterator(tags)) {\n switch (key) {\n case \"title\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9nam\", value));\n }\n ;\n break;\n case \"description\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9des\", value));\n }\n ;\n break;\n case \"artist\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9ART\", value));\n }\n ;\n break;\n case \"album\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9alb\", value));\n }\n ;\n break;\n case \"albumArtist\":\n {\n boxes.push(metadataTagStringBoxShort(\"albr\", value));\n }\n ;\n break;\n case \"genre\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9gen\", value));\n }\n ;\n break;\n case \"date\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9day\", value.toISOString().slice(0, 10)));\n }\n ;\n break;\n case \"comment\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9cmt\", value));\n }\n ;\n break;\n case \"lyrics\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9lyr\", value));\n }\n ;\n break;\n case \"raw\":\n {}\n ;\n break;\n case \"discNumber\":\n case \"discsTotal\":\n case \"trackNumber\":\n case \"tracksTotal\":\n case \"beatsPerMinute\":\n case \"images\":\n {}\n ;\n break;\n default:\n assertNever(key);\n }\n }\n if (tags.raw) {\n for (const key in tags.raw) {\n const value = tags.raw[key];\n if (value == null || key.length !== 4 || boxes.some((x) => x.type === key)) {\n continue;\n }\n if (typeof value === \"string\") {\n boxes.push(metadataTagStringBoxShort(key, value));\n } else if (value instanceof Uint8Array) {\n boxes.push(box(key, Array.from(value)));\n }\n }\n }\n };\n var metadataTagStringBoxShort = (name, value) => {\n const encoded = textEncoder.encode(value);\n return box(name, [\n u16(encoded.length),\n u16(getLanguageCodeInt(\"und\")),\n Array.from(encoded)\n ]);\n };\n var DATA_BOX_MIME_TYPE_MAP = {\n \"image/jpeg\": 13,\n \"image/png\": 14,\n \"image/bmp\": 27\n };\n var generateMetadataPairs = (tags, isMdta) => {\n const pairs = [];\n for (const { key, value } of keyValueIterator(tags)) {\n switch (key) {\n case \"title\":\n {\n pairs.push({ key: isMdta ? \"title\" : \"\u00A9nam\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"description\":\n {\n pairs.push({ key: isMdta ? \"description\" : \"\u00A9des\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"artist\":\n {\n pairs.push({ key: isMdta ? \"artist\" : \"\u00A9ART\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"album\":\n {\n pairs.push({ key: isMdta ? \"album\" : \"\u00A9alb\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"albumArtist\":\n {\n pairs.push({ key: isMdta ? \"album_artist\" : \"aART\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"comment\":\n {\n pairs.push({ key: isMdta ? \"comment\" : \"\u00A9cmt\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"genre\":\n {\n pairs.push({ key: isMdta ? \"genre\" : \"\u00A9gen\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"beatsPerMinute\":\n {\n if (!isMdta) {\n pairs.push({ key: \"tmpo\", value: box(\"data\", [\n u32(21),\n u32(0),\n u16(value)\n ]) });\n }\n }\n ;\n break;\n case \"lyrics\":\n {\n pairs.push({ key: isMdta ? \"lyrics\" : \"\u00A9lyr\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"date\":\n {\n pairs.push({\n key: isMdta ? \"date\" : \"\u00A9day\",\n value: dataStringBoxLong(value.toISOString().slice(0, 10))\n });\n }\n ;\n break;\n case \"images\":\n {\n for (const image of value) {\n if (image.kind !== \"coverFront\") {\n continue;\n }\n pairs.push({ key: \"covr\", value: box(\"data\", [\n u32(DATA_BOX_MIME_TYPE_MAP[image.mimeType] ?? 0),\n u32(0),\n Array.from(image.data)\n ]) });\n }\n }\n ;\n break;\n case \"trackNumber\":\n {\n if (isMdta) {\n const string = tags.tracksTotal !== undefined ? `${value}/${tags.tracksTotal}` : value.toString();\n pairs.push({ key: \"track\", value: dataStringBoxLong(string) });\n } else {\n pairs.push({ key: \"trkn\", value: box(\"data\", [\n u32(0),\n u32(0),\n u16(0),\n u16(value),\n u16(tags.tracksTotal ?? 0),\n u16(0)\n ]) });\n }\n }\n ;\n break;\n case \"discNumber\":\n {\n if (!isMdta) {\n pairs.push({ key: \"disc\", value: box(\"data\", [\n u32(0),\n u32(0),\n u16(0),\n u16(value),\n u16(tags.discsTotal ?? 0),\n u16(0)\n ]) });\n }\n }\n ;\n break;\n case \"tracksTotal\":\n case \"discsTotal\":\n {}\n ;\n break;\n case \"raw\":\n {}\n ;\n break;\n default:\n assertNever(key);\n }\n }\n if (tags.raw) {\n for (const key in tags.raw) {\n const value = tags.raw[key];\n if (value == null || !isMdta && key.length !== 4 || pairs.some((x) => x.key === key)) {\n continue;\n }\n if (typeof value === \"string\") {\n pairs.push({ key, value: dataStringBoxLong(value) });\n } else if (value instanceof Uint8Array) {\n pairs.push({ key, value: box(\"data\", [\n u32(0),\n u32(0),\n Array.from(value)\n ]) });\n } else if (value instanceof RichImageData) {\n pairs.push({ key, value: box(\"data\", [\n u32(DATA_BOX_MIME_TYPE_MAP[value.mimeType] ?? 0),\n u32(0),\n Array.from(value.data)\n ]) });\n }\n }\n }\n return pairs;\n };\n var metaMdir = (tags) => {\n const pairs = generateMetadataPairs(tags, false);\n if (pairs.length === 0) {\n return null;\n }\n return fullBox(\"meta\", 0, 0, undefined, [\n hdlr(false, \"mdir\", \"\", \"appl\"),\n box(\"ilst\", undefined, pairs.map((pair) => box(pair.key, undefined, [pair.value])))\n ]);\n };\n var metaMdta = (tags) => {\n const pairs = generateMetadataPairs(tags, true);\n if (pairs.length === 0) {\n return null;\n }\n return box(\"meta\", undefined, [\n hdlr(false, \"mdta\", \"\"),\n fullBox(\"keys\", 0, 0, [\n u32(pairs.length)\n ], pairs.map((pair) => box(\"mdta\", [\n ...textEncoder.encode(pair.key)\n ]))),\n box(\"ilst\", undefined, pairs.map((pair, i) => {\n const boxName = String.fromCharCode(...u32(i + 1));\n return box(boxName, undefined, [pair.value]);\n }))\n ]);\n };\n var dataStringBoxLong = (value) => {\n return box(\"data\", [\n u32(1),\n u32(0),\n ...textEncoder.encode(value)\n ]);\n };\n var videoCodecToBoxName = (codec, fullCodecString) => {\n switch (codec) {\n case \"avc\":\n return fullCodecString.startsWith(\"avc3\") ? \"avc3\" : \"avc1\";\n case \"hevc\":\n return \"hvc1\";\n case \"vp8\":\n return \"vp08\";\n case \"vp9\":\n return \"vp09\";\n case \"av1\":\n return \"av01\";\n case \"prores\":\n return fullCodecString;\n }\n };\n var VIDEO_CODEC_TO_CONFIGURATION_BOX = {\n avc: avcC,\n hevc: hvcC,\n vp8: vpcC,\n vp9: vpcC,\n av1: av1C,\n prores: null\n };\n var audioCodecToBoxName = (codec, fullCodecString, isQuickTime) => {\n switch (codec) {\n case \"aac\":\n return \"mp4a\";\n case \"mp3\":\n return \"mp4a\";\n case \"opus\":\n return \"Opus\";\n case \"vorbis\":\n return \"mp4a\";\n case \"flac\":\n return \"fLaC\";\n case \"ulaw\":\n return \"ulaw\";\n case \"alaw\":\n return \"alaw\";\n case \"pcm-u8\":\n return \"raw \";\n case \"pcm-s8\":\n return \"sowt\";\n case \"ac3\":\n return \"ac-3\";\n case \"eac3\":\n return \"ec-3\";\n case \"dts\":\n return fullCodecString;\n }\n if (isQuickTime) {\n switch (codec) {\n case \"pcm-s16\":\n return \"sowt\";\n case \"pcm-s16be\":\n return \"twos\";\n case \"pcm-s24\":\n return \"in24\";\n case \"pcm-s24be\":\n return \"in24\";\n case \"pcm-s32\":\n return \"in32\";\n case \"pcm-s32be\":\n return \"in32\";\n case \"pcm-f32\":\n return \"fl32\";\n case \"pcm-f32be\":\n return \"fl32\";\n case \"pcm-f64\":\n return \"fl64\";\n case \"pcm-f64be\":\n return \"fl64\";\n }\n } else {\n switch (codec) {\n case \"pcm-s16\":\n return \"ipcm\";\n case \"pcm-s16be\":\n return \"ipcm\";\n case \"pcm-s24\":\n return \"ipcm\";\n case \"pcm-s24be\":\n return \"ipcm\";\n case \"pcm-s32\":\n return \"ipcm\";\n case \"pcm-s32be\":\n return \"ipcm\";\n case \"pcm-f32\":\n return \"fpcm\";\n case \"pcm-f32be\":\n return \"fpcm\";\n case \"pcm-f64\":\n return \"fpcm\";\n case \"pcm-f64be\":\n return \"fpcm\";\n }\n }\n };\n var audioCodecToConfigurationBox = (codec, isQuickTime) => {\n switch (codec) {\n case \"aac\":\n return esds;\n case \"mp3\":\n return esds;\n case \"opus\":\n return dOps;\n case \"vorbis\":\n return esds;\n case \"flac\":\n return dfLa;\n case \"ac3\":\n return dac3;\n case \"eac3\":\n return dec3;\n case \"dts\":\n return ddts;\n }\n if (isQuickTime) {\n switch (codec) {\n case \"pcm-s24\":\n return wave;\n case \"pcm-s24be\":\n return wave;\n case \"pcm-s32\":\n return wave;\n case \"pcm-s32be\":\n return wave;\n case \"pcm-f32\":\n return wave;\n case \"pcm-f32be\":\n return wave;\n case \"pcm-f64\":\n return wave;\n case \"pcm-f64be\":\n return wave;\n }\n } else {\n switch (codec) {\n case \"pcm-s16\":\n return pcmC;\n case \"pcm-s16be\":\n return pcmC;\n case \"pcm-s24\":\n return pcmC;\n case \"pcm-s24be\":\n return pcmC;\n case \"pcm-s32\":\n return pcmC;\n case \"pcm-s32be\":\n return pcmC;\n case \"pcm-f32\":\n return pcmC;\n case \"pcm-f32be\":\n return pcmC;\n case \"pcm-f64\":\n return pcmC;\n case \"pcm-f64be\":\n return pcmC;\n }\n }\n return null;\n };\n var SUBTITLE_CODEC_TO_BOX_NAME = {\n webvtt: \"wvtt\"\n };\n var SUBTITLE_CODEC_TO_CONFIGURATION_BOX = {\n webvtt: vttC\n };\n var getLanguageCodeInt = (code) => {\n assert(code.length === 3);\n let language = 0;\n for (let i = 0;i < 3; i++) {\n language <<= 5;\n language += code.charCodeAt(i) - 96;\n }\n return language;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/writer.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class Writer {\n constructor(target, isMonotonic) {\n this.finalized = false;\n this.started = false;\n this.pos = 0;\n this.trackedWrites = null;\n this.trackedStart = -1;\n this.trackedEnd = -1;\n if (target._writerAcquired) {\n throw new Error(\"Can't have multiple Writers for the same Target.\");\n }\n this.target = target;\n target._setMonotonicity(isMonotonic);\n target._writerAcquired = true;\n }\n start() {\n assert(!this.started);\n this.target._start();\n this.started = true;\n }\n write(data) {\n assert(this.started && !this.finalized);\n this.maybeTrackWrites(data);\n this.target._write(data, this.pos);\n this.pos += data.byteLength;\n }\n seek(newPos) {\n this.pos = newPos;\n }\n getPos() {\n return this.pos;\n }\n async flush() {\n assert(this.started && !this.finalized);\n return this.target._flush();\n }\n async finalize() {\n assert(this.started && !this.finalized);\n await this.target._finalize();\n this.finalized = true;\n }\n maybeTrackWrites(data) {\n if (!this.trackedWrites) {\n return;\n }\n let pos = this.getPos();\n if (pos < this.trackedStart) {\n if (pos + data.byteLength <= this.trackedStart) {\n return;\n }\n data = data.subarray(this.trackedStart - pos);\n pos = 0;\n }\n const neededSize = pos + data.byteLength - this.trackedStart;\n let newLength = this.trackedWrites.byteLength;\n while (newLength < neededSize) {\n newLength *= 2;\n }\n if (newLength !== this.trackedWrites.byteLength) {\n const copy = new Uint8Array(newLength);\n copy.set(this.trackedWrites, 0);\n this.trackedWrites = copy;\n }\n this.trackedWrites.set(data, pos - this.trackedStart);\n this.trackedEnd = Math.max(this.trackedEnd, pos + data.byteLength);\n }\n startTrackingWrites() {\n this.trackedWrites = new Uint8Array(2 ** 10);\n this.trackedStart = this.getPos();\n this.trackedEnd = this.trackedStart;\n }\n stopTrackingWrites() {\n if (!this.trackedWrites) {\n throw new Error(\"Internal error: Can't get tracked writes since nothing was tracked.\");\n }\n const slice = this.trackedWrites.subarray(0, this.trackedEnd - this.trackedStart);\n const result = {\n data: slice,\n start: this.trackedStart,\n end: this.trackedEnd\n };\n this.trackedWrites = null;\n return result;\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/target.js\n var nodeAlias = (() => ({}));\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n class Target extends EventEmitter {\n constructor() {\n super(...arguments);\n this._writerAcquired = false;\n this._monotonicity = null;\n this.onwrite = null;\n }\n _setMonotonicity(monotonicity) {\n if (this._monotonicity !== false) {\n this._monotonicity = monotonicity;\n }\n }\n _dispatchWrite(start, end) {\n this.onwrite?.(start, end);\n this._emit(\"write\", { start, end });\n }\n slice(offset) {\n if (!Number.isInteger(offset) || offset < 0) {\n throw new TypeError(\"offset must be a non-negative integer.\");\n }\n return new RangedTarget(this, offset);\n }\n }\n var ARRAY_BUFFER_INITIAL_SIZE = 2 ** 16;\n var ARRAY_BUFFER_MAX_SIZE = 2 ** 32;\n\n class BufferTarget extends Target {\n constructor(options = {}) {\n super();\n this.buffer = null;\n this._maxPos = 0;\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"BufferTarget options, when provided, must be an object.\");\n }\n if (options.onFinalize !== undefined && typeof options.onFinalize !== \"function\") {\n throw new TypeError(\"options.onFinalize, when provided, must be a function.\");\n }\n this._options = options;\n this._supportsResize = \"resize\" in new ArrayBuffer(0);\n if (this._supportsResize) {\n try {\n this._buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE, { maxByteLength: ARRAY_BUFFER_MAX_SIZE });\n } catch {\n this._buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE);\n this._supportsResize = false;\n }\n } else {\n this._buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE);\n }\n this._bytes = new Uint8Array(this._buffer);\n }\n _ensureSize(size) {\n let newLength = this._buffer.byteLength;\n while (newLength < size)\n newLength *= 2;\n if (newLength === this._buffer.byteLength)\n return;\n if (newLength > ARRAY_BUFFER_MAX_SIZE) {\n throw new Error(`ArrayBuffer exceeded maximum size of ${ARRAY_BUFFER_MAX_SIZE} bytes. Please consider using another` + ` target.`);\n }\n if (this._supportsResize) {\n this._buffer.resize(newLength);\n } else {\n const newBuffer = new ArrayBuffer(newLength);\n const newBytes = new Uint8Array(newBuffer);\n newBytes.set(this._bytes, 0);\n this._buffer = newBuffer;\n this._bytes = newBytes;\n }\n }\n _start() {}\n _write(data, pos) {\n this._ensureSize(pos + data.byteLength);\n this._bytes.set(data, pos);\n this._maxPos = Math.max(this._maxPos, pos + data.byteLength);\n this._dispatchWrite(pos, pos + data.byteLength);\n }\n async _flush() {}\n async _finalize() {\n this.buffer = this._buffer.slice(0, this._maxPos);\n if (this._options.onFinalize) {\n await this._options.onFinalize(this.buffer);\n }\n this._emit(\"finalized\");\n }\n async _close() {}\n _getSlice(start, end) {\n return this._bytes.slice(start, end);\n }\n }\n var DEFAULT_CHUNK_SIZE = 2 ** 24;\n var MAX_CHUNKS_AT_ONCE = 2;\n\n class StreamTarget extends Target {\n constructor(writable, options = {}) {\n super();\n this._sections = [];\n this._lastWriteEnd = 0;\n this._lastFlushEnd = 0;\n this._streamWriter = null;\n this._writeError = null;\n this._chunks = [];\n if (!(writable instanceof WritableStream)) {\n throw new TypeError(\"StreamTarget requires a WritableStream instance.\");\n }\n if (options != null && typeof options !== \"object\") {\n throw new TypeError(\"StreamTarget options, when provided, must be an object.\");\n }\n if (options.chunked !== undefined && typeof options.chunked !== \"boolean\") {\n throw new TypeError(\"options.chunked, when provided, must be a boolean.\");\n }\n if (options.chunkSize !== undefined && (!Number.isInteger(options.chunkSize) || options.chunkSize < 1024)) {\n throw new TypeError(\"options.chunkSize, when provided, must be an integer and not smaller than 1024.\");\n }\n this._writable = writable;\n this._options = options;\n this._chunked = options.chunked ?? false;\n this._chunkSize = options.chunkSize ?? DEFAULT_CHUNK_SIZE;\n }\n _start() {\n this._streamWriter = this._writable.getWriter();\n }\n _write(data, pos) {\n if (pos > this._lastWriteEnd) {\n const paddingBytesNeeded = pos - this._lastWriteEnd;\n this._write(new Uint8Array(paddingBytesNeeded), this._lastWriteEnd);\n }\n this._sections.push({\n data: data.slice(),\n start: pos\n });\n this._lastWriteEnd = Math.max(this._lastWriteEnd, pos + data.byteLength);\n this._dispatchWrite(pos, pos + data.byteLength);\n }\n async _flush() {\n if (this._writeError !== null) {\n throw this._writeError;\n }\n assert(this._streamWriter);\n if (this._sections.length === 0) {\n return;\n }\n const chunks = [];\n const sorted = [...this._sections].sort((a, b) => a.start - b.start);\n chunks.push({\n start: sorted[0].start,\n size: sorted[0].data.byteLength\n });\n for (let i = 1;i < sorted.length; i++) {\n const lastChunk = chunks[chunks.length - 1];\n const section = sorted[i];\n if (section.start <= lastChunk.start + lastChunk.size) {\n lastChunk.size = Math.max(lastChunk.size, section.start + section.data.byteLength - lastChunk.start);\n } else {\n chunks.push({\n start: section.start,\n size: section.data.byteLength\n });\n }\n }\n for (const chunk of chunks) {\n chunk.data = new Uint8Array(chunk.size);\n for (const section of this._sections) {\n if (chunk.start <= section.start && section.start < chunk.start + chunk.size) {\n chunk.data.set(section.data, section.start - chunk.start);\n }\n }\n if (this._streamWriter.desiredSize !== null && this._streamWriter.desiredSize <= 0) {\n await this._streamWriter.ready;\n }\n if (this._chunked) {\n this._writeDataIntoChunks(chunk.data, chunk.start);\n this._tryToFlushChunks();\n } else {\n if (this._monotonicity === true && chunk.start !== this._lastFlushEnd) {\n throw new Error(\"Internal error: Monotonicity violation.\");\n }\n this._streamWriter.write({\n type: \"write\",\n data: chunk.data,\n position: chunk.start\n }).catch((error) => {\n this._writeError ??= error;\n });\n this._lastFlushEnd = chunk.start + chunk.data.byteLength;\n }\n }\n this._sections.length = 0;\n }\n _writeDataIntoChunks(data, position) {\n let chunkIndex = this._chunks.findIndex((x) => x.start <= position && position < x.start + this._chunkSize);\n if (chunkIndex === -1)\n chunkIndex = this._createChunk(position);\n const chunk = this._chunks[chunkIndex];\n const relativePosition = position - chunk.start;\n const toWrite = data.subarray(0, Math.min(this._chunkSize - relativePosition, data.byteLength));\n chunk.data.set(toWrite, relativePosition);\n const section = {\n start: relativePosition,\n end: relativePosition + toWrite.byteLength\n };\n this._insertSectionIntoChunk(chunk, section);\n if (chunk.written[0].start === 0 && chunk.written[0].end === this._chunkSize) {\n chunk.shouldFlush = true;\n }\n if (this._chunks.length > MAX_CHUNKS_AT_ONCE) {\n for (let i = 0;i < this._chunks.length - 1; i++) {\n this._chunks[i].shouldFlush = true;\n }\n this._tryToFlushChunks();\n }\n if (toWrite.byteLength < data.byteLength) {\n this._writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength);\n }\n }\n _insertSectionIntoChunk(chunk, section) {\n let low = 0;\n let high = chunk.written.length - 1;\n let index = -1;\n while (low <= high) {\n const mid = Math.floor(low + (high - low + 1) / 2);\n if (chunk.written[mid].start <= section.start) {\n low = mid + 1;\n index = mid;\n } else {\n high = mid - 1;\n }\n }\n chunk.written.splice(index + 1, 0, section);\n if (index === -1 || chunk.written[index].end < section.start)\n index++;\n while (index < chunk.written.length - 1 && chunk.written[index].end >= chunk.written[index + 1].start) {\n chunk.written[index].end = Math.max(chunk.written[index].end, chunk.written[index + 1].end);\n chunk.written.splice(index + 1, 1);\n }\n }\n _createChunk(includesPosition) {\n const start = Math.floor(includesPosition / this._chunkSize) * this._chunkSize;\n const chunk = {\n start,\n data: new Uint8Array(this._chunkSize),\n written: [],\n shouldFlush: false\n };\n this._chunks.push(chunk);\n this._chunks.sort((a, b) => a.start - b.start);\n return this._chunks.indexOf(chunk);\n }\n _tryToFlushChunks(force = false) {\n assert(this._streamWriter);\n for (let i = 0;i < this._chunks.length; i++) {\n const chunk = this._chunks[i];\n if (!chunk.shouldFlush && !force)\n continue;\n for (const section of chunk.written) {\n const position = chunk.start + section.start;\n if (this._monotonicity === true && position !== this._lastFlushEnd) {\n throw new Error(\"Internal error: Monotonicity violation.\");\n }\n const isPartialView = section.start !== 0 || section.end !== chunk.data.byteLength;\n let data;\n if (isPartialView && isWebKit()) {\n data = chunk.data.slice(section.start, section.end);\n } else {\n data = chunk.data.subarray(section.start, section.end);\n }\n this._streamWriter.write({\n type: \"write\",\n data,\n position\n }).catch((error) => {\n this._writeError ??= error;\n });\n this._lastFlushEnd = chunk.start + section.end;\n }\n this._chunks.splice(i--, 1);\n }\n }\n async _finalize() {\n if (this._chunked) {\n this._tryToFlushChunks(true);\n }\n if (this._writeError !== null) {\n throw this._writeError;\n }\n assert(this._streamWriter);\n await this._streamWriter.ready;\n await this._streamWriter.close();\n this._emit(\"finalized\");\n }\n async _close() {\n return this._streamWriter?.close();\n }\n }\n class RangedTarget extends Target {\n constructor(baseTarget, offset) {\n super();\n this._baseTarget = baseTarget;\n this._offset = offset;\n }\n _start() {}\n _write(data, pos) {\n this._baseTarget._write(data, this._offset + pos);\n this._dispatchWrite(pos, pos + data.byteLength);\n }\n _flush() {\n return this._baseTarget._flush();\n }\n async _finalize() {\n this._emit(\"finalized\");\n }\n async _close() {}\n _setMonotonicity(monotonicity) {\n super._setMonotonicity(monotonicity);\n this._baseTarget._setMonotonicity(monotonicity);\n }\n }\n\n class PathedTarget {\n constructor(rootPath, getTarget) {\n this.rootPath = rootPath;\n this.getTarget = getTarget;\n if (typeof rootPath !== \"string\") {\n throw new TypeError(\"rootPath must be a string.\");\n }\n if (typeof getTarget !== \"function\") {\n throw new TypeError(\"getTarget must be a function.\");\n }\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-muxer.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var GLOBAL_TIMESCALE = 57600;\n var TIMESTAMP_OFFSET = 2082844800;\n var getTrackMetadata = (trackData) => {\n const metadata = {};\n const track = trackData.track;\n if (track.metadata.name !== undefined) {\n metadata.name = track.metadata.name;\n }\n return metadata;\n };\n var intoTimescale = (timeInSeconds, timescale, round = true) => {\n const value = timeInSeconds * timescale;\n return round ? Math.round(value) : value;\n };\n var presentationSpan = (trackData) => {\n if (trackData.samples.length === 0) {\n return 0;\n }\n let minTimestamp = Infinity;\n let maxEndTimestamp = -Infinity;\n for (let i = 0;i < trackData.samples.length; i++) {\n const sample = trackData.samples[i];\n if (sample.timestamp < minTimestamp) {\n minTimestamp = sample.timestamp;\n }\n if (sample.timestamp + sample.duration > maxEndTimestamp) {\n maxEndTimestamp = sample.timestamp + sample.duration;\n }\n }\n if (minTimestamp === Infinity) {\n return 0;\n }\n return maxEndTimestamp - minTimestamp;\n };\n\n class IsobmffMuxer extends Muxer {\n constructor(output, format) {\n super(output);\n this.writer = null;\n this.boxWriter = null;\n this.initWriter = null;\n this.initBoxWriter = null;\n this.auxTarget = new BufferTarget;\n this.auxWriter = new Writer(this.auxTarget, false);\n this.auxBoxWriter = new IsobmffBoxWriter(this.auxWriter);\n this.mdat = null;\n this.ftypSize = null;\n this.trackDatas = [];\n this.allTracksKnown = promiseWithResolvers();\n this.creationTime = Math.floor(Date.now() / 1000) + TIMESTAMP_OFFSET;\n this.finalizedChunks = [];\n this.wroteFragmentedHeader = false;\n this.nextFragmentNumber = 1;\n this.maxWrittenTimestamp = -Infinity;\n this.minWrittenTimestamp = Infinity;\n this.maxWrittenEndTimestamp = -Infinity;\n this.segmentHeaderSize = null;\n this.format = format;\n this.formatOptions = { ...format._options };\n this.isQuickTime = format instanceof MovOutputFormat;\n this.isCmaf = format instanceof CmafOutputFormat;\n this.minimumFragmentDuration = this.formatOptions.minimumFragmentDuration ?? (format instanceof CmafOutputFormat ? Infinity : 1);\n this.auxWriter.start();\n }\n async start() {\n const release = await this.mutex.acquire();\n if (!this.isCmaf) {\n this.writer = await this.output._getRootWriter((target) => this.formatOptions.fastStart !== undefined ? this.formatOptions.fastStart === \"fragmented\" : target instanceof BufferTarget);\n this.boxWriter = new IsobmffBoxWriter(this.writer);\n this.fastStart = this.formatOptions.fastStart ?? (this.writer.target instanceof BufferTarget ? \"in-memory\" : false);\n this.isFragmented = this.fastStart === \"fragmented\";\n } else {\n this.fastStart = \"fragmented\";\n this.isFragmented = true;\n }\n if (this.isCmaf) {\n if (!this.output._hasInitTarget()) {\n throw new Error(`CMAF outputs require the initTarget field in OutputOptions to be set; the init segment` + ` will be written to it.`);\n }\n const initTarget = await this.output._getInitTarget();\n const initWriter = new Writer(initTarget, true);\n initWriter.start();\n this.initWriter = initWriter;\n this.initBoxWriter = new IsobmffBoxWriter(initWriter);\n }\n const holdsAvc = this.output.tracks.some((x) => x.isVideoTrack() && x.source._codec === \"avc\");\n {\n const boxWriter = this.initBoxWriter ?? this.boxWriter;\n assert(boxWriter);\n if (this.formatOptions.onFtyp) {\n boxWriter.writer.startTrackingWrites();\n }\n boxWriter.writeBox(ftyp({\n isQuickTime: this.isQuickTime,\n holdsAvc,\n fragmented: this.isFragmented,\n cmaf: this.isCmaf\n }));\n if (this.formatOptions.onFtyp) {\n const { data, start } = boxWriter.writer.stopTrackingWrites();\n this.formatOptions.onFtyp(data, start);\n }\n this.ftypSize = boxWriter.writer.getPos();\n if (this.isCmaf) {\n await this.initWriter.flush();\n }\n }\n if (this.fastStart === \"in-memory\") {} else if (this.fastStart === \"reserve\") {\n for (const track of this.output.tracks) {\n if (track.metadata.maximumPacketCount === undefined) {\n throw new Error(\"All tracks must specify maximumPacketCount in their metadata when using\" + \" fastStart: 'reserve'.\");\n }\n }\n } else if (this.isFragmented) {} else {\n assert(this.writer);\n assert(this.boxWriter);\n if (this.formatOptions.onMdat) {\n this.writer.startTrackingWrites();\n }\n this.mdat = mdat(true);\n this.boxWriter.writeBox(this.mdat);\n }\n await this.writer?.flush();\n for (const track of this.output.tracks) {\n if (track.isVideoTrack() && track.metadata.decoderConfig) {\n this.getVideoTrackData(track, track.metadata.primingPacket ?? null, { decoderConfig: track.metadata.decoderConfig });\n } else if (track.isAudioTrack() && track.metadata.decoderConfig) {\n this.getAudioTrackData(track, track.metadata.primingPacket ?? null, { decoderConfig: track.metadata.decoderConfig });\n }\n }\n release();\n }\n allTracksAreKnown() {\n for (const track of this.output.tracks) {\n if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) {\n return false;\n }\n }\n return true;\n }\n async getMimeType() {\n await this.allTracksKnown.promise;\n const codecStrings = this.trackDatas.map((trackData) => {\n if (trackData.type === \"video\") {\n return trackData.info.decoderConfig.codec;\n } else if (trackData.type === \"audio\") {\n return trackData.info.decoderConfig.codec;\n } else {\n const map = {\n webvtt: \"wvtt\"\n };\n return map[trackData.track.source._codec];\n }\n });\n return buildIsobmffMimeType({\n isQuickTime: this.isQuickTime,\n hasVideo: this.trackDatas.some((x) => x.type === \"video\"),\n hasAudio: this.trackDatas.some((x) => x.type === \"audio\"),\n codecStrings\n });\n }\n getVideoTrackData(track, packet, meta) {\n const existingTrackData = this.trackDatas.find((x) => x.track === track);\n if (existingTrackData) {\n return existingTrackData;\n }\n validateVideoChunkMetadata(meta, track.source._codec);\n assert(meta);\n assert(meta.decoderConfig);\n const decoderConfig = { ...meta.decoderConfig };\n assert(decoderConfig.codedWidth !== undefined);\n assert(decoderConfig.codedHeight !== undefined);\n let requiresAnnexBTransformation = false;\n if (track.source._codec === \"avc\" && !decoderConfig.description) {\n if (!packet) {\n throw new Error(\"No AVC description provided; you must therefore provide a priming packet.\");\n }\n const decoderConfigurationRecord = extractAvcDecoderConfigurationRecord(packet.data);\n if (!decoderConfigurationRecord) {\n throw new Error(\"Couldn't extract an AVCDecoderConfigurationRecord from the AVC packet. Make sure the packets are\" + \" in Annex B format (as specified in ITU-T-REC-H.264) when not providing a description, or\" + \" provide a description (must be an AVCDecoderConfigurationRecord as specified in ISO 14496-15)\" + \" and ensure the packets are in AVCC format.\");\n }\n decoderConfig.description = serializeAvcDecoderConfigurationRecord(decoderConfigurationRecord);\n requiresAnnexBTransformation = true;\n } else if (track.source._codec === \"hevc\" && !decoderConfig.description) {\n if (!packet) {\n throw new Error(\"No HEVC description provided; you must therefore provide a priming packet.\");\n }\n const decoderConfigurationRecord = extractHevcDecoderConfigurationRecord(packet.data);\n if (!decoderConfigurationRecord) {\n throw new Error(\"Couldn't extract an HEVCDecoderConfigurationRecord from the HEVC packet. Make sure the packets\" + \" are in Annex B format (as specified in ITU-T-REC-H.265) when not providing a description, or\" + \" provide a description (must be an HEVCDecoderConfigurationRecord as specified in ISO 14496-15)\" + \" and ensure the packets are in HEVC format.\");\n }\n decoderConfig.description = serializeHevcDecoderConfigurationRecord(decoderConfigurationRecord);\n requiresAnnexBTransformation = true;\n }\n const timescale = computeRationalApproximation(1 / (track.metadata.frameRate ?? GLOBAL_TIMESCALE), 1e6).den;\n const displayAspectWidth = decoderConfig.displayAspectWidth;\n const displayAspectHeight = decoderConfig.displayAspectHeight;\n const pixelAspectRatio = displayAspectWidth === undefined || displayAspectHeight === undefined ? { num: 1, den: 1 } : simplifyRational({\n num: displayAspectWidth * decoderConfig.codedHeight,\n den: displayAspectHeight * decoderConfig.codedWidth\n });\n const hasAlphaChannel = decoderConfig.codec === \"ap4h\" || decoderConfig.codec === \"ap4x\";\n const newTrackData = {\n muxer: this,\n track,\n type: \"video\",\n info: {\n width: decoderConfig.codedWidth,\n height: decoderConfig.codedHeight,\n pixelAspectRatio,\n decoderConfig,\n requiresAnnexBTransformation,\n hasAlphaChannel\n },\n timescale,\n samples: [],\n sampleQueue: [],\n timestampProcessingQueue: [],\n timeToSampleTable: [],\n compositionTimeOffsetTable: [],\n lastTimescaleUnits: null,\n lastSample: null,\n startTimestampOffset: null,\n finalizedChunks: [],\n currentChunk: null,\n compactlyCodedChunkTable: [],\n closed: false,\n avgBitrate: track.source._nominalBitrate ?? track.metadata.averageBitrate ?? 0,\n maxBitrate: track.source._nominalBitrate ?? track.metadata.bitrate ?? 0\n };\n this.trackDatas.push(newTrackData);\n this.trackDatas.sort((a, b) => a.track.id - b.track.id);\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n return newTrackData;\n }\n getAudioTrackData(track, packet, meta) {\n const existingTrackData = this.trackDatas.find((x) => x.track === track);\n if (existingTrackData) {\n return existingTrackData;\n }\n validateAudioChunkMetadata(meta, track.source._codec);\n assert(meta);\n assert(meta.decoderConfig);\n const decoderConfig = { ...meta.decoderConfig };\n let requiresAdtsStripping = false;\n if (track.source._codec === \"aac\" && !decoderConfig.description) {\n if (!packet) {\n throw new Error(\"No AAC description provided; you must therefore provide a priming packet.\");\n }\n const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packet.data));\n if (!adtsFrame) {\n throw new Error(\"Couldn't parse ADTS header from the AAC packet. Make sure the packets are in ADTS format\" + \" (as specified in ISO 13818-7) when not providing a description, or provide a description\" + \" (must be an AudioSpecificConfig as specified in ISO 14496-3) and ensure the packets\" + \" are raw AAC data.\");\n }\n const sampleRate = aacFrequencyTable[adtsFrame.samplingFrequencyIndex];\n const numberOfChannels = aacChannelMap[adtsFrame.channelConfiguration];\n if (sampleRate === undefined || numberOfChannels === undefined) {\n throw new Error(\"Invalid ADTS frame header.\");\n }\n decoderConfig.description = buildAacAudioSpecificConfig({\n objectType: adtsFrame.objectType,\n outputSampleRate: sampleRate,\n outputNumberOfChannels: numberOfChannels\n });\n requiresAdtsStripping = true;\n }\n if (!packet) {\n if (track.source._codec === \"ac3\" || track.source._codec === \"eac3\") {\n throw new Error(\"AC-3/E-AC-3 require a priming packet.\");\n }\n if (track.source._codec === \"dts\") {\n throw new Error(\"DTS requires a priming packet.\");\n }\n }\n const newTrackData = {\n muxer: this,\n track,\n type: \"audio\",\n info: {\n numberOfChannels: meta.decoderConfig.numberOfChannels,\n sampleRate: meta.decoderConfig.sampleRate,\n decoderConfig,\n requiresPcmTransformation: !this.isFragmented && PCM_AUDIO_CODECS.includes(track.source._codec),\n expectedNextPcmPacketTimestamp: null,\n requiresAdtsStripping,\n primingPacket: packet\n },\n timescale: decoderConfig.sampleRate,\n samples: [],\n sampleQueue: [],\n timestampProcessingQueue: [],\n timeToSampleTable: [],\n compositionTimeOffsetTable: [],\n lastTimescaleUnits: null,\n lastSample: null,\n startTimestampOffset: null,\n finalizedChunks: [],\n currentChunk: null,\n compactlyCodedChunkTable: [],\n closed: false,\n avgBitrate: track.source._nominalBitrate ?? track.metadata.averageBitrate ?? 0,\n maxBitrate: track.source._nominalBitrate ?? track.metadata.bitrate ?? 0\n };\n this.trackDatas.push(newTrackData);\n this.trackDatas.sort((a, b) => a.track.id - b.track.id);\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n return newTrackData;\n }\n getSubtitleTrackData(track, meta) {\n const existingTrackData = this.trackDatas.find((x) => x.track === track);\n if (existingTrackData) {\n return existingTrackData;\n }\n validateSubtitleMetadata(meta);\n assert(meta);\n assert(meta.config);\n const newTrackData = {\n muxer: this,\n track,\n type: \"subtitle\",\n info: {\n config: meta.config\n },\n timescale: 1000,\n samples: [],\n sampleQueue: [],\n timestampProcessingQueue: [],\n timeToSampleTable: [],\n compositionTimeOffsetTable: [],\n lastTimescaleUnits: null,\n lastSample: null,\n startTimestampOffset: null,\n finalizedChunks: [],\n currentChunk: null,\n compactlyCodedChunkTable: [],\n closed: false,\n avgBitrate: track.source._nominalBitrate ?? track.metadata.averageBitrate ?? 0,\n maxBitrate: track.source._nominalBitrate ?? track.metadata.bitrate ?? 0,\n lastCueEndTimestamp: null,\n cueQueue: [],\n nextSourceId: 0,\n cueToSourceId: new WeakMap\n };\n this.trackDatas.push(newTrackData);\n this.trackDatas.sort((a, b) => a.track.id - b.track.id);\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n return newTrackData;\n }\n async addEncodedVideoPacket(track, packet, meta) {\n const release = await this.mutex.acquire();\n try {\n const trackData = this.getVideoTrackData(track, packet, meta);\n let packetData = packet.data;\n if (trackData.info.requiresAnnexBTransformation) {\n const nalUnits = [...iterateNalUnitsInAnnexB(packetData)].map((loc) => packetData.subarray(loc.offset, loc.offset + loc.length));\n if (nalUnits.length === 0) {\n throw new Error(\"Failed to transform packet data. Make sure all packets are provided in Annex B format, as\" + \" specified in ITU-T-REC-H.264 and ITU-T-REC-H.265.\");\n }\n packetData = concatNalUnitsInLengthPrefixed(nalUnits, 4);\n }\n this.validateTimestamp(trackData.track, packet.timestamp, packet.type === \"key\");\n const internalSample = this.createSampleForTrack(trackData, packetData, packet.timestamp, packet.duration, packet.type);\n await this.registerSample(trackData, internalSample);\n } finally {\n release();\n }\n }\n async addEncodedAudioPacket(track, packet, meta) {\n const release = await this.mutex.acquire();\n try {\n const trackData = this.getAudioTrackData(track, packet, meta);\n let packetData = packet.data;\n if (trackData.info.requiresAdtsStripping) {\n const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packetData));\n if (!adtsFrame) {\n throw new Error(\"Expected ADTS frame, didn't get one.\");\n }\n const headerLength = adtsFrame.crcCheck === null ? MIN_ADTS_FRAME_HEADER_SIZE : MAX_ADTS_FRAME_HEADER_SIZE;\n packetData = packetData.subarray(headerLength);\n }\n this.validateTimestamp(trackData.track, packet.timestamp, packet.type === \"key\");\n let timestamp = packet.timestamp;\n let duration = packet.duration;\n if (trackData.info.requiresPcmTransformation) {\n const pcmInfo = parsePcmCodec(trackData.info.decoderConfig.codec);\n const frameSize = pcmInfo.sampleSize * trackData.info.numberOfChannels;\n duration = packetData.byteLength / frameSize / trackData.info.sampleRate;\n if (trackData.info.expectedNextPcmPacketTimestamp !== null) {\n const diff = timestamp - trackData.info.expectedNextPcmPacketTimestamp;\n if (diff < 0.01) {\n timestamp = trackData.info.expectedNextPcmPacketTimestamp;\n } else {\n const paddedDuration = await this.padWithSilence(trackData, trackData.info.expectedNextPcmPacketTimestamp, diff);\n timestamp = trackData.info.expectedNextPcmPacketTimestamp + paddedDuration;\n }\n }\n trackData.info.expectedNextPcmPacketTimestamp = timestamp + duration;\n }\n const internalSample = this.createSampleForTrack(trackData, packetData, timestamp, duration, packet.type);\n await this.registerSample(trackData, internalSample);\n } finally {\n release();\n }\n }\n async padWithSilence(trackData, timestamp, duration) {\n const deltaInTimescale = intoTimescale(duration, trackData.timescale);\n duration = deltaInTimescale / trackData.timescale;\n if (deltaInTimescale > 0) {\n const { sampleSize, silentValue } = parsePcmCodec(trackData.info.decoderConfig.codec);\n const samplesNeeded = deltaInTimescale * trackData.info.numberOfChannels;\n const data = new Uint8Array(sampleSize * samplesNeeded).fill(silentValue);\n const paddingSample = this.createSampleForTrack(trackData, new Uint8Array(data.buffer), timestamp, duration, \"key\");\n await this.registerSample(trackData, paddingSample);\n }\n return duration;\n }\n async addSubtitleCue(track, cue, meta) {\n const release = await this.mutex.acquire();\n try {\n const trackData = this.getSubtitleTrackData(track, meta);\n this.validateTimestamp(trackData.track, cue.timestamp, true);\n if (track.source._codec === \"webvtt\") {\n trackData.cueQueue.push(cue);\n await this.processWebVTTCues(trackData, cue.timestamp);\n }\n } finally {\n release();\n }\n }\n async processWebVTTCues(trackData, until) {\n while (trackData.cueQueue.length > 0) {\n trackData.lastCueEndTimestamp ??= Math.min(0, trackData.cueQueue[0].timestamp);\n const timestamps = new Set([]);\n for (const cue of trackData.cueQueue) {\n assert(cue.timestamp <= until);\n assert(trackData.lastCueEndTimestamp <= cue.timestamp + cue.duration);\n timestamps.add(Math.max(cue.timestamp, trackData.lastCueEndTimestamp));\n timestamps.add(cue.timestamp + cue.duration);\n }\n const sortedTimestamps = [...timestamps].sort((a, b) => a - b);\n const sampleStart = sortedTimestamps[0];\n const sampleEnd = sortedTimestamps[1] ?? sampleStart;\n if (until < sampleEnd) {\n break;\n }\n if (trackData.lastCueEndTimestamp < sampleStart) {\n this.auxWriter.seek(0);\n const box = vtte();\n this.auxBoxWriter.writeBox(box);\n const body = this.auxTarget._getSlice(0, this.auxWriter.getPos());\n const sample = this.createSampleForTrack(trackData, body, trackData.lastCueEndTimestamp, sampleStart - trackData.lastCueEndTimestamp, \"key\");\n await this.registerSample(trackData, sample);\n trackData.lastCueEndTimestamp = sampleStart;\n }\n this.auxWriter.seek(0);\n for (let i = 0;i < trackData.cueQueue.length; i++) {\n const cue = trackData.cueQueue[i];\n if (cue.timestamp >= sampleEnd) {\n break;\n }\n inlineTimestampRegex.lastIndex = 0;\n const containsTimestamp = inlineTimestampRegex.test(cue.text);\n const endTimestamp = cue.timestamp + cue.duration;\n let sourceId = trackData.cueToSourceId.get(cue);\n if (sourceId === undefined && sampleEnd < endTimestamp) {\n sourceId = trackData.nextSourceId++;\n trackData.cueToSourceId.set(cue, sourceId);\n }\n if (cue.notes) {\n const box = vtta(cue.notes);\n this.auxBoxWriter.writeBox(box);\n }\n const box = vttc(cue.text, containsTimestamp ? sampleStart : null, cue.identifier ?? null, cue.settings ?? null, sourceId ?? null);\n this.auxBoxWriter.writeBox(box);\n if (endTimestamp === sampleEnd) {\n trackData.cueQueue.splice(i--, 1);\n }\n }\n const body = this.auxTarget._getSlice(0, this.auxWriter.getPos());\n const sample = this.createSampleForTrack(trackData, body, sampleStart, sampleEnd - sampleStart, \"key\");\n await this.registerSample(trackData, sample);\n trackData.lastCueEndTimestamp = sampleEnd;\n }\n }\n createSampleForTrack(trackData, data, timestamp, duration, type) {\n const sample = {\n timestamp,\n decodeTimestamp: timestamp,\n duration,\n data,\n size: data.byteLength,\n type,\n timescaleUnitsToNextSample: intoTimescale(duration, trackData.timescale)\n };\n return sample;\n }\n processTimestamps(trackData, nextSample) {\n if (trackData.timestampProcessingQueue.length === 0) {\n return;\n }\n if (trackData.type === \"audio\" && trackData.info.requiresPcmTransformation) {\n assert(!this.isFragmented);\n trackData.startTimestampOffset ??= trackData.timestampProcessingQueue[0].timestamp;\n let totalDuration = 0;\n for (let i = 0;i < trackData.timestampProcessingQueue.length; i++) {\n const sample = trackData.timestampProcessingQueue[i];\n const duration = intoTimescale(sample.duration, trackData.timescale);\n totalDuration += duration;\n }\n if (trackData.timeToSampleTable.length === 0) {\n trackData.timeToSampleTable.push({\n sampleCount: totalDuration,\n sampleDelta: 1\n });\n } else {\n const lastEntry = last(trackData.timeToSampleTable);\n lastEntry.sampleCount += totalDuration;\n }\n trackData.timestampProcessingQueue.length = 0;\n return;\n }\n const sortedTimestamps = trackData.timestampProcessingQueue.map((x) => x.timestamp).sort((a, b) => a - b);\n if (this.isFragmented) {\n trackData.startTimestampOffset ??= Math.min(sortedTimestamps[0], 0);\n } else {\n trackData.startTimestampOffset ??= sortedTimestamps[0];\n }\n for (let i = 0;i < trackData.timestampProcessingQueue.length; i++) {\n const sample = trackData.timestampProcessingQueue[i];\n sample.decodeTimestamp = sortedTimestamps[i];\n const sampleCompositionTimeOffset = intoTimescale(sample.timestamp - sample.decodeTimestamp, trackData.timescale);\n const durationInTimescale = intoTimescale(sample.duration, trackData.timescale);\n if (trackData.lastTimescaleUnits !== null) {\n assert(trackData.lastSample);\n const timescaleUnits = intoTimescale(sample.decodeTimestamp, trackData.timescale, false);\n const delta = Math.round(timescaleUnits - trackData.lastTimescaleUnits);\n assert(delta >= 0);\n trackData.lastTimescaleUnits += delta;\n trackData.lastSample.timescaleUnitsToNextSample = delta;\n if (!this.isFragmented) {\n let lastTableEntry = last(trackData.timeToSampleTable);\n assert(lastTableEntry);\n if (lastTableEntry.sampleCount === 1) {\n lastTableEntry.sampleDelta = delta;\n const entryBefore = trackData.timeToSampleTable[trackData.timeToSampleTable.length - 2];\n if (entryBefore && entryBefore.sampleDelta === delta) {\n entryBefore.sampleCount++;\n trackData.timeToSampleTable.pop();\n lastTableEntry = entryBefore;\n }\n } else if (lastTableEntry.sampleDelta !== delta) {\n lastTableEntry.sampleCount--;\n trackData.timeToSampleTable.push(lastTableEntry = {\n sampleCount: 1,\n sampleDelta: delta\n });\n }\n if (lastTableEntry.sampleDelta === durationInTimescale) {\n lastTableEntry.sampleCount++;\n } else {\n trackData.timeToSampleTable.push({\n sampleCount: 1,\n sampleDelta: durationInTimescale\n });\n }\n const lastCompositionTimeOffsetTableEntry = last(trackData.compositionTimeOffsetTable);\n assert(lastCompositionTimeOffsetTableEntry);\n if (lastCompositionTimeOffsetTableEntry.sampleCompositionTimeOffset === sampleCompositionTimeOffset) {\n lastCompositionTimeOffsetTableEntry.sampleCount++;\n } else {\n trackData.compositionTimeOffsetTable.push({\n sampleCount: 1,\n sampleCompositionTimeOffset\n });\n }\n }\n } else {\n trackData.lastTimescaleUnits = intoTimescale(sample.decodeTimestamp, trackData.timescale, false);\n if (!this.isFragmented) {\n trackData.timeToSampleTable.push({\n sampleCount: 1,\n sampleDelta: durationInTimescale\n });\n trackData.compositionTimeOffsetTable.push({\n sampleCount: 1,\n sampleCompositionTimeOffset\n });\n }\n }\n trackData.lastSample = sample;\n }\n trackData.timestampProcessingQueue.length = 0;\n assert(trackData.lastSample);\n assert(trackData.lastTimescaleUnits !== null);\n if (nextSample !== undefined && trackData.lastSample.timescaleUnitsToNextSample === 0) {\n assert(nextSample.type === \"key\");\n const timescaleUnits = intoTimescale(nextSample.timestamp, trackData.timescale, false);\n const delta = Math.round(timescaleUnits - trackData.lastTimescaleUnits);\n trackData.lastSample.timescaleUnitsToNextSample = delta;\n }\n }\n async registerSample(trackData, sample) {\n if (sample.type === \"key\") {\n this.processTimestamps(trackData, sample);\n }\n trackData.timestampProcessingQueue.push(sample);\n if (this.isFragmented) {\n trackData.sampleQueue.push(sample);\n await this.interleaveSamples();\n } else if (this.fastStart === \"reserve\") {\n await this.registerSampleFastStartReserve(trackData, sample);\n } else {\n await this.addSampleToTrack(trackData, sample);\n }\n }\n async addSampleToTrack(trackData, sample) {\n if (!this.isFragmented) {\n trackData.samples.push(sample);\n if (this.fastStart === \"reserve\") {\n const maximumPacketCount = trackData.track.metadata.maximumPacketCount;\n assert(maximumPacketCount !== undefined);\n if (trackData.samples.length > maximumPacketCount) {\n throw new Error(`Track #${trackData.track.id} has already reached the maximum packet count` + ` (${maximumPacketCount}). Either add less packets or increase the maximum packet count.`);\n }\n }\n }\n let beginNewChunk = false;\n if (!trackData.currentChunk) {\n beginNewChunk = true;\n } else {\n trackData.currentChunk.startTimestamp = Math.min(trackData.currentChunk.startTimestamp, sample.timestamp);\n const currentChunkDuration = sample.timestamp - trackData.currentChunk.startTimestamp;\n if (this.isFragmented) {\n const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => {\n if (trackData === otherTrackData) {\n return sample.type === \"key\";\n }\n const firstQueuedSample = otherTrackData.sampleQueue[0];\n if (firstQueuedSample) {\n return firstQueuedSample.type === \"key\";\n }\n return otherTrackData.closed;\n });\n if (currentChunkDuration >= this.minimumFragmentDuration && keyFrameQueuedEverywhere && sample.timestamp > this.maxWrittenTimestamp) {\n beginNewChunk = true;\n await this.finalizeFragment();\n }\n } else {\n beginNewChunk = currentChunkDuration >= 0.5;\n }\n }\n if (beginNewChunk) {\n if (trackData.currentChunk) {\n await this.finalizeCurrentChunk(trackData);\n }\n trackData.currentChunk = {\n startTimestamp: sample.timestamp,\n samples: [],\n offset: null,\n moofOffset: null,\n trafIndex: null\n };\n }\n assert(trackData.currentChunk);\n trackData.currentChunk.samples.push(sample);\n if (this.isFragmented) {\n this.maxWrittenTimestamp = Math.max(this.maxWrittenTimestamp, sample.timestamp);\n this.maxWrittenEndTimestamp = Math.max(this.maxWrittenEndTimestamp, sample.timestamp + sample.duration);\n this.minWrittenTimestamp = Math.min(this.minWrittenTimestamp, sample.timestamp);\n }\n }\n async finalizeCurrentChunk(trackData) {\n assert(!this.isFragmented);\n assert(this.writer);\n if (!trackData.currentChunk)\n return;\n trackData.finalizedChunks.push(trackData.currentChunk);\n this.finalizedChunks.push(trackData.currentChunk);\n let sampleCount = trackData.currentChunk.samples.length;\n if (trackData.type === \"audio\" && trackData.info.requiresPcmTransformation) {\n sampleCount = trackData.currentChunk.samples.reduce((acc, sample) => acc + intoTimescale(sample.duration, trackData.timescale), 0);\n }\n if (trackData.compactlyCodedChunkTable.length === 0 || last(trackData.compactlyCodedChunkTable).samplesPerChunk !== sampleCount) {\n trackData.compactlyCodedChunkTable.push({\n firstChunk: trackData.finalizedChunks.length,\n samplesPerChunk: sampleCount\n });\n }\n if (this.fastStart === \"in-memory\") {\n trackData.currentChunk.offset = 0;\n return;\n }\n trackData.currentChunk.offset = this.writer.getPos();\n for (const sample of trackData.currentChunk.samples) {\n assert(sample.data);\n this.writer.write(sample.data);\n sample.data = null;\n }\n await this.writer.flush();\n }\n async interleaveSamples(isFinalCall = false) {\n assert(this.isFragmented);\n if (!isFinalCall && !this.allTracksAreKnown()) {\n return;\n }\n outer:\n while (true) {\n let trackWithMinTimestamp = null;\n let minTimestamp = Infinity;\n for (const trackData of this.trackDatas) {\n if (!isFinalCall && trackData.sampleQueue.length === 0 && !trackData.closed) {\n break outer;\n }\n if (trackData.sampleQueue.length > 0 && trackData.sampleQueue[0].timestamp < minTimestamp) {\n trackWithMinTimestamp = trackData;\n minTimestamp = trackData.sampleQueue[0].timestamp;\n }\n }\n if (!trackWithMinTimestamp) {\n break;\n }\n const sample = trackWithMinTimestamp.sampleQueue.shift();\n await this.addSampleToTrack(trackWithMinTimestamp, sample);\n }\n }\n async finalizeFragment(flushWriter = !this.isCmaf) {\n assert(this.isFragmented);\n if (!this.wroteFragmentedHeader) {\n this.wroteFragmentedHeader = true;\n const boxWriter = this.initBoxWriter ?? this.boxWriter;\n assert(boxWriter);\n if (this.formatOptions.onMoov) {\n boxWriter.writer.startTrackingWrites();\n }\n this.ensureOneEnabledTrack();\n const movieBox = moov(this);\n boxWriter.writeBox(movieBox);\n if (this.formatOptions.onMoov) {\n const { data, start } = boxWriter.writer.stopTrackingWrites();\n this.formatOptions.onMoov(data, start);\n }\n if (this.isCmaf) {\n assert(this.initWriter);\n await this.initWriter.flush();\n await this.initWriter.finalize();\n this.writer = await this.output._getRootWriter(true);\n this.boxWriter = new IsobmffBoxWriter(this.writer);\n const stypSize = this.boxWriter.measureBox(styp());\n const sidxSize = this.boxWriter.measureBox(sidx(this, 0));\n this.segmentHeaderSize = stypSize + sidxSize;\n this.writer.seek(this.segmentHeaderSize);\n }\n }\n assert(this.writer);\n assert(this.boxWriter);\n const tracksInFragment = this.trackDatas.filter((x) => x.currentChunk);\n if (tracksInFragment.length === 0) {\n if (flushWriter) {\n await this.writer.flush();\n }\n return;\n }\n const fragmentNumber = this.nextFragmentNumber++;\n const moofBox = moof(fragmentNumber, tracksInFragment);\n const moofOffset = this.writer.getPos();\n const mdatStartPos = moofOffset + this.boxWriter.measureBox(moofBox);\n let currentPos = mdatStartPos + MIN_BOX_HEADER_SIZE;\n let fragmentStartTimestamp = Infinity;\n for (let i = 0;i < tracksInFragment.length; i++) {\n const trackData = tracksInFragment[i];\n assert(trackData.currentChunk);\n assert(trackData.startTimestampOffset !== null);\n trackData.currentChunk.offset = currentPos;\n trackData.currentChunk.moofOffset = moofOffset;\n trackData.currentChunk.trafIndex = i;\n trackData.currentChunk.startTimestamp -= trackData.startTimestampOffset;\n for (const sample of trackData.currentChunk.samples) {\n currentPos += sample.size;\n sample.timestamp -= trackData.startTimestampOffset;\n sample.decodeTimestamp -= trackData.startTimestampOffset;\n }\n fragmentStartTimestamp = Math.min(fragmentStartTimestamp, trackData.currentChunk.startTimestamp);\n }\n const mdatSize = currentPos - mdatStartPos;\n const needsLargeMdatSize = mdatSize >= 2 ** 32;\n if (needsLargeMdatSize) {\n for (const trackData of tracksInFragment) {\n trackData.currentChunk.offset += MAX_BOX_HEADER_SIZE - MIN_BOX_HEADER_SIZE;\n }\n }\n if (this.formatOptions.onMoof) {\n this.writer.startTrackingWrites();\n }\n const newMoofBox = moof(fragmentNumber, tracksInFragment);\n this.boxWriter.writeBox(newMoofBox);\n if (this.formatOptions.onMoof) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMoof(data, start, fragmentStartTimestamp);\n }\n assert(this.writer.getPos() === mdatStartPos);\n if (this.formatOptions.onMdat) {\n this.writer.startTrackingWrites();\n }\n const mdatBox = mdat(needsLargeMdatSize);\n mdatBox.size = mdatSize;\n this.boxWriter.writeBox(mdatBox);\n this.writer.seek(mdatStartPos + (needsLargeMdatSize ? MAX_BOX_HEADER_SIZE : MIN_BOX_HEADER_SIZE));\n for (const trackData of tracksInFragment) {\n for (const sample of trackData.currentChunk.samples) {\n this.writer.write(sample.data);\n sample.data = null;\n }\n }\n if (this.formatOptions.onMdat) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMdat(data, start);\n }\n for (const trackData of tracksInFragment) {\n trackData.finalizedChunks.push(trackData.currentChunk);\n this.finalizedChunks.push(trackData.currentChunk);\n trackData.currentChunk = null;\n }\n if (flushWriter) {\n await this.writer.flush();\n }\n }\n async registerSampleFastStartReserve(trackData, sample) {\n if (this.allTracksAreKnown()) {\n if (!this.mdat) {\n await this.createFastStartReserveMdat();\n }\n await this.addSampleToTrack(trackData, sample);\n } else {\n trackData.sampleQueue.push(sample);\n }\n }\n async createFastStartReserveMdat() {\n assert(this.writer);\n assert(this.boxWriter);\n this.ensureOneEnabledTrack();\n const moovBox = moov(this);\n const moovSize = this.boxWriter.measureBox(moovBox);\n const reservedSize = moovSize + this.computeSampleTableSizeUpperBound() + 4096;\n assert(this.ftypSize !== null);\n this.writer.seek(this.ftypSize + reservedSize);\n if (this.formatOptions.onMdat) {\n this.writer.startTrackingWrites();\n }\n this.mdat = mdat(true);\n this.boxWriter.writeBox(this.mdat);\n for (const trackData of this.trackDatas) {\n for (const sample of trackData.sampleQueue) {\n await this.addSampleToTrack(trackData, sample);\n }\n trackData.sampleQueue.length = 0;\n }\n }\n computeSampleTableSizeUpperBound() {\n assert(this.fastStart === \"reserve\");\n let upperBound = 0;\n for (const trackData of this.trackDatas) {\n const n = trackData.track.metadata.maximumPacketCount;\n assert(n !== undefined);\n upperBound += (4 + 4) * Math.ceil(2 / 3 * n);\n upperBound += 4 * n;\n upperBound += (4 + 4) * Math.ceil(2 / 3 * n);\n upperBound += (4 + 4 + 4) * Math.ceil(2 / 3 * n);\n upperBound += 4 * n;\n upperBound += 8 * n;\n }\n return upperBound;\n }\n async onTrackClose(track) {\n const release = await this.mutex.acquire();\n const trackData = this.trackDatas.find((x) => x.track === track);\n if (trackData) {\n trackData.closed = true;\n if (trackData.type === \"subtitle\" && track.source._codec === \"webvtt\") {\n await this.processWebVTTCues(trackData, Infinity);\n }\n this.processTimestamps(trackData);\n }\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n if (this.isFragmented) {\n await this.interleaveSamples();\n }\n release();\n }\n ensureOneEnabledTrack() {\n for (const type of [\"video\", \"audio\", \"subtitle\"]) {\n const tracks = this.trackDatas.filter((t) => t.type === type);\n if (tracks.length === 0) {\n continue;\n }\n const hasEnabled = tracks.some((t) => t.track.metadata.disposition?.default !== false);\n if (!hasEnabled) {\n const firstTrack = tracks[0];\n firstTrack.track.metadata.disposition = {\n ...firstTrack.track.metadata.disposition,\n default: true\n };\n }\n }\n }\n async forceFragmentFinalization() {\n assert(this.isFragmented);\n const release = await this.mutex.acquire();\n try {\n for (const trackData of this.trackDatas) {\n if (trackData.type === \"subtitle\" && trackData.track.source._codec === \"webvtt\") {\n await this.processWebVTTCues(trackData, Infinity);\n }\n this.processTimestamps(trackData);\n }\n await this.interleaveSamples(true);\n await this.finalizeFragment();\n } finally {\n release();\n }\n }\n async finalize() {\n const release = await this.mutex.acquire();\n this.allTracksKnown.resolve();\n this.ensureOneEnabledTrack();\n if (!this.mdat && this.fastStart === \"reserve\") {\n await this.createFastStartReserveMdat();\n }\n for (const trackData of this.trackDatas) {\n trackData.closed = true;\n if (trackData.type === \"subtitle\" && trackData.track.source._codec === \"webvtt\") {\n await this.processWebVTTCues(trackData, Infinity);\n }\n this.processTimestamps(trackData);\n }\n if (this.isFragmented) {\n await this.interleaveSamples(true);\n await this.finalizeFragment(false);\n } else {\n for (const trackData of this.trackDatas) {\n await this.finalizeCurrentChunk(trackData);\n const span = presentationSpan(trackData);\n if (span > 0) {\n let totalBytes = 0;\n for (const sample of trackData.samples) {\n totalBytes += sample.size;\n }\n trackData.avgBitrate = Math.round(8 * totalBytes / span);\n } else {\n trackData.avgBitrate = 0;\n }\n let windowStart = 0;\n let windowBytes = 0;\n let maxWindowBytes = 0;\n for (let i = 0;i < trackData.samples.length; i++) {\n const sample = trackData.samples[i];\n windowBytes += sample.size;\n while (sample.decodeTimestamp - trackData.samples[windowStart].decodeTimestamp >= 1) {\n windowBytes -= trackData.samples[windowStart].size;\n windowStart++;\n }\n maxWindowBytes = Math.max(maxWindowBytes, windowBytes);\n }\n trackData.maxBitrate = 8 * maxWindowBytes;\n if (trackData.startTimestampOffset !== null) {\n for (let i = 0;i < trackData.samples.length; i++) {\n const sample = trackData.samples[i];\n sample.timestamp -= trackData.startTimestampOffset;\n sample.decodeTimestamp -= trackData.startTimestampOffset;\n }\n }\n }\n }\n assert(this.writer);\n assert(this.boxWriter);\n if (this.fastStart === \"in-memory\") {\n this.mdat = mdat(false);\n let mdatSize;\n for (let i = 0;i < 2; i++) {\n const movieBox = moov(this);\n const movieBoxSize = this.boxWriter.measureBox(movieBox);\n mdatSize = this.boxWriter.measureBox(this.mdat);\n let currentChunkPos = this.writer.getPos() + movieBoxSize + mdatSize;\n for (const chunk of this.finalizedChunks) {\n chunk.offset = currentChunkPos;\n for (const { data } of chunk.samples) {\n assert(data);\n currentChunkPos += data.byteLength;\n mdatSize += data.byteLength;\n }\n }\n if (currentChunkPos < 2 ** 32)\n break;\n if (mdatSize >= 2 ** 32)\n this.mdat.largeSize = true;\n }\n if (this.formatOptions.onMoov) {\n this.writer.startTrackingWrites();\n }\n const movieBox = moov(this);\n this.boxWriter.writeBox(movieBox);\n if (this.formatOptions.onMoov) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMoov(data, start);\n }\n if (this.formatOptions.onMdat) {\n this.writer.startTrackingWrites();\n }\n this.mdat.size = mdatSize;\n this.boxWriter.writeBox(this.mdat);\n for (const chunk of this.finalizedChunks) {\n for (const sample of chunk.samples) {\n assert(sample.data);\n this.writer.write(sample.data);\n sample.data = null;\n }\n }\n if (this.formatOptions.onMdat) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMdat(data, start);\n }\n } else if (this.isFragmented) {\n if (this.isCmaf) {\n const contentSize = this.segmentHeaderSize !== null ? this.writer.getPos() - this.segmentHeaderSize : 0;\n this.writer.seek(0);\n this.boxWriter.writeBox(styp());\n this.boxWriter.writeBox(sidx(this, contentSize));\n } else {\n const startPos = this.writer.getPos();\n const mfraBox = mfra(this.trackDatas);\n this.boxWriter.writeBox(mfraBox);\n const mfraBoxSize = this.writer.getPos() - startPos;\n this.writer.seek(this.writer.getPos() - 4);\n this.boxWriter.writeU32(mfraBoxSize);\n }\n } else {\n assert(this.mdat);\n const mdatPos = this.boxWriter.offsets.get(this.mdat);\n assert(mdatPos !== undefined);\n const mdatSize = this.writer.getPos() - mdatPos;\n this.mdat.size = mdatSize;\n this.mdat.largeSize = mdatSize >= 2 ** 32;\n this.boxWriter.patchBox(this.mdat);\n if (this.formatOptions.onMdat) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMdat(data, start);\n }\n const movieBox = moov(this);\n if (this.fastStart === \"reserve\") {\n assert(this.ftypSize !== null);\n this.writer.seek(this.ftypSize);\n if (this.formatOptions.onMoov) {\n this.writer.startTrackingWrites();\n }\n this.boxWriter.writeBox(movieBox);\n const remainingSpace = this.boxWriter.offsets.get(this.mdat) - this.writer.getPos();\n this.boxWriter.writeBox(free(remainingSpace));\n } else {\n if (this.formatOptions.onMoov) {\n this.writer.startTrackingWrites();\n }\n this.boxWriter.writeBox(movieBox);\n }\n if (this.formatOptions.onMoov) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMoov(data, start);\n }\n }\n release();\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/resample.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class AudioResampler {\n constructor(options) {\n this.sourceSampleRate = null;\n this.sourceNumberOfChannels = null;\n this.startTime = null;\n this.bufferStartFrame = 0;\n this.maxWrittenFrame = null;\n this.targetSampleRate = options.targetSampleRate;\n this.targetNumberOfChannels = options.targetNumberOfChannels;\n this.onSample = options.onSample;\n this.bufferSizeInFrames = Math.floor(this.targetSampleRate * 5);\n this.bufferSizeInSamples = this.bufferSizeInFrames * this.targetNumberOfChannels;\n }\n doChannelMixerSetup() {\n assert(this.sourceNumberOfChannels !== null);\n const sourceNum = this.sourceNumberOfChannels;\n const targetNum = this.targetNumberOfChannels;\n if (sourceNum === 1 && targetNum === 2) {\n this.channelMixer = (sourceData, sourceFrameIndex) => {\n return sourceData[sourceFrameIndex * sourceNum];\n };\n } else if (sourceNum === 1 && targetNum === 4) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return sourceData[sourceFrameIndex * sourceNum] * +(targetChannelIndex < 2);\n };\n } else if (sourceNum === 1 && targetNum === 6) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return sourceData[sourceFrameIndex * sourceNum] * +(targetChannelIndex === 2);\n };\n } else if (sourceNum === 2 && targetNum === 1) {\n this.channelMixer = (sourceData, sourceFrameIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return 0.5 * (sourceData[baseIdx] + sourceData[baseIdx + 1]);\n };\n } else if (sourceNum === 2 && targetNum === 4) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return targetChannelIndex < 2 ? sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] : 0;\n };\n } else if (sourceNum === 2 && targetNum === 6) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return targetChannelIndex < 2 ? sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] : 0;\n };\n } else if (sourceNum === 4 && targetNum === 1) {\n this.channelMixer = (sourceData, sourceFrameIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return 0.25 * (sourceData[baseIdx] + sourceData[baseIdx + 1] + sourceData[baseIdx + 2] + sourceData[baseIdx + 3]);\n };\n } else if (sourceNum === 4 && targetNum === 2) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return 0.5 * (sourceData[baseIdx + targetChannelIndex] + sourceData[baseIdx + targetChannelIndex + 2]);\n };\n } else if (sourceNum === 4 && targetNum === 6) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n if (targetChannelIndex < 2)\n return sourceData[baseIdx + targetChannelIndex];\n if (targetChannelIndex === 2 || targetChannelIndex === 3)\n return 0;\n return sourceData[baseIdx + targetChannelIndex - 2];\n };\n } else if (sourceNum === 6 && targetNum === 1) {\n this.channelMixer = (sourceData, sourceFrameIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return Math.SQRT1_2 * (sourceData[baseIdx] + sourceData[baseIdx + 1]) + sourceData[baseIdx + 2] + 0.5 * (sourceData[baseIdx + 4] + sourceData[baseIdx + 5]);\n };\n } else if (sourceNum === 6 && targetNum === 2) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return sourceData[baseIdx + targetChannelIndex] + Math.SQRT1_2 * (sourceData[baseIdx + 2] + sourceData[baseIdx + targetChannelIndex + 4]);\n };\n } else if (sourceNum === 6 && targetNum === 4) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n if (targetChannelIndex < 2) {\n return sourceData[baseIdx + targetChannelIndex] + Math.SQRT1_2 * sourceData[baseIdx + 2];\n }\n return sourceData[baseIdx + targetChannelIndex + 2];\n };\n } else {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return targetChannelIndex < sourceNum ? sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] : 0;\n };\n }\n }\n ensureTempBufferSize(requiredSamples) {\n let length = this.tempSourceBuffer.length;\n while (length < requiredSamples) {\n length *= 2;\n }\n if (length !== this.tempSourceBuffer.length) {\n const newBuffer = new Float32Array(length);\n newBuffer.set(this.tempSourceBuffer);\n this.tempSourceBuffer = newBuffer;\n }\n }\n async add(audioSample) {\n if (this.sourceSampleRate === null) {\n this.sourceSampleRate = audioSample.sampleRate;\n this.sourceNumberOfChannels = audioSample.numberOfChannels;\n this.startTime = audioSample.timestamp;\n const overflowFrames = Math.ceil(this.targetSampleRate / this.sourceSampleRate);\n this.bufferCapacityInFrames = this.bufferSizeInFrames + overflowFrames;\n this.outputBuffer = new Float32Array(this.bufferCapacityInFrames * this.targetNumberOfChannels);\n this.tempSourceBuffer = new Float32Array(this.sourceSampleRate * this.sourceNumberOfChannels);\n this.doChannelMixerSetup();\n }\n assert(this.startTime !== null);\n const requiredSamples = audioSample.numberOfFrames * audioSample.numberOfChannels;\n this.ensureTempBufferSize(requiredSamples);\n const sourceDataSize = audioSample.allocationSize({ planeIndex: 0, format: \"f32\" });\n const sourceView = new Float32Array(this.tempSourceBuffer.buffer, 0, sourceDataSize / 4);\n audioSample.copyTo(sourceView, { planeIndex: 0, format: \"f32\" });\n const inputStartTime = audioSample.timestamp - this.startTime;\n const inputEndTime = inputStartTime + audioSample.duration;\n const outputStartFrame = Math.floor((inputStartTime - 1 / this.sourceSampleRate) * this.targetSampleRate) + 1;\n const outputEndFrame = Math.ceil(inputEndTime * this.targetSampleRate);\n for (let outputFrame = outputStartFrame;outputFrame < outputEndFrame; outputFrame++) {\n if (outputFrame < this.bufferStartFrame) {\n continue;\n }\n while (outputFrame >= this.bufferStartFrame + this.bufferCapacityInFrames) {\n await this.finalizeCurrentBuffer();\n this.bufferStartFrame += this.bufferSizeInFrames;\n }\n const bufferFrameIndex = outputFrame - this.bufferStartFrame;\n assert(bufferFrameIndex < this.bufferCapacityInFrames);\n const outputTime = outputFrame / this.targetSampleRate;\n const inputTime = outputTime - inputStartTime;\n const sourcePosition = inputTime * this.sourceSampleRate;\n const sourceLowerFrame = Math.floor(sourcePosition);\n const sourceUpperFrame = Math.ceil(sourcePosition);\n const fraction = sourcePosition - sourceLowerFrame;\n for (let targetChannel = 0;targetChannel < this.targetNumberOfChannels; targetChannel++) {\n let lowerSample = 0;\n let upperSample = 0;\n if (sourceLowerFrame >= 0 && sourceLowerFrame < audioSample.numberOfFrames) {\n lowerSample = this.channelMixer(sourceView, sourceLowerFrame, targetChannel);\n }\n if (sourceUpperFrame >= 0 && sourceUpperFrame < audioSample.numberOfFrames) {\n upperSample = this.channelMixer(sourceView, sourceUpperFrame, targetChannel);\n }\n const outputSample = lowerSample + fraction * (upperSample - lowerSample);\n const outputIndex = bufferFrameIndex * this.targetNumberOfChannels + targetChannel;\n this.outputBuffer[outputIndex] += outputSample;\n }\n if (this.maxWrittenFrame === null) {\n this.maxWrittenFrame = bufferFrameIndex;\n } else {\n this.maxWrittenFrame = Math.max(this.maxWrittenFrame, bufferFrameIndex);\n }\n }\n }\n async finalizeCurrentBuffer() {\n if (this.maxWrittenFrame === null) {\n return;\n }\n assert(this.startTime !== null);\n const samplesWritten = Math.min(this.maxWrittenFrame + 1, this.bufferSizeInFrames) * this.targetNumberOfChannels;\n const outputData = new Float32Array(samplesWritten);\n outputData.set(this.outputBuffer.subarray(0, samplesWritten));\n const audioSample = new AudioSample({\n format: \"f32\",\n sampleRate: this.targetSampleRate,\n numberOfChannels: this.targetNumberOfChannels,\n timestamp: this.startTime + this.bufferStartFrame / this.targetSampleRate,\n data: outputData\n });\n await this.onSample(audioSample);\n this.outputBuffer.copyWithin(0, this.bufferSizeInSamples);\n this.outputBuffer.fill(0, this.outputBuffer.length - this.bufferSizeInSamples);\n this.maxWrittenFrame = this.maxWrittenFrame >= this.bufferSizeInFrames ? this.maxWrittenFrame - this.bufferSizeInFrames : null;\n }\n async finalize() {\n while (this.maxWrittenFrame !== null) {\n await this.finalizeCurrentBuffer();\n this.bufferStartFrame += this.bufferSizeInFrames;\n }\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/media-source.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var __addDisposableResource2 = function(env, value, async) {\n if (value !== null && value !== undefined) {\n if (typeof value !== \"object\" && typeof value !== \"function\")\n throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose)\n throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === undefined) {\n if (!Symbol.dispose)\n throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async)\n inner = dispose;\n }\n if (typeof dispose !== \"function\")\n throw new TypeError(\"Object not disposable.\");\n if (inner)\n dispose = function() {\n try {\n inner.call(this);\n } catch (e) {\n return Promise.reject(e);\n }\n };\n env.stack.push({ value, dispose, async });\n } else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n };\n var __disposeResources2 = function(SuppressedError2) {\n return function(env) {\n function fail(e) {\n env.error = env.hasError ? new SuppressedError2(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1)\n return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async)\n return s |= 2, Promise.resolve(result).then(next, function(e) {\n fail(e);\n return next();\n });\n } else\n s |= 1;\n } catch (e) {\n fail(e);\n }\n }\n if (s === 1)\n return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError)\n throw env.error;\n }\n return next();\n };\n }(typeof SuppressedError === \"function\" ? SuppressedError : function(error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n });\n\n class MediaSource {\n constructor() {\n this._connectedTrack = null;\n this._closingPromise = null;\n this._closed = false;\n this._nominalBitrate = null;\n }\n _ensureValidAdd() {\n if (!this._connectedTrack) {\n throw new Error(\"Source is not connected to an output track.\");\n }\n if (this._connectedTrack.output.state === \"canceled\") {\n throw new Error(\"Output has been canceled.\");\n }\n if (this._connectedTrack.output.state === \"finalizing\" || this._connectedTrack.output.state === \"finalized\") {\n throw new Error(\"Output has been finalized.\");\n }\n if (this._connectedTrack.output.state === \"pending\") {\n throw new Error(\"Output has not started.\");\n }\n if (this._closed) {\n throw new Error(\"Source is closed.\");\n }\n }\n async _start() {}\n async _flushAndClose(forceClose) {}\n close() {\n if (this._closingPromise) {\n return;\n }\n const connectedTrack = this._connectedTrack;\n if (!connectedTrack) {\n throw new Error(\"Cannot call close without connecting the source to an output track.\");\n }\n if (connectedTrack.output.state === \"pending\") {\n throw new Error(\"Cannot call close before output has been started.\");\n }\n this._closingPromise = (async () => {\n await this._flushAndClose(false);\n this._closed = true;\n if (connectedTrack.output.state === \"finalizing\" || connectedTrack.output.state === \"finalized\") {\n return;\n }\n connectedTrack.output._muxer.onTrackClose(connectedTrack);\n })();\n }\n async _flushOrWaitForOngoingClose(forceClose) {\n return this._closingPromise ??= (async () => {\n await this._flushAndClose(forceClose);\n this._closed = true;\n })();\n }\n }\n\n class VideoSource extends MediaSource {\n constructor(codec) {\n super();\n if (!VIDEO_CODECS.includes(codec)) {\n throw new TypeError(`Invalid video codec '${codec}'. Must be one of: ${VIDEO_CODECS.join(\", \")}.`);\n }\n this._codec = codec;\n }\n }\n var maybeEnsureIsKeyPacket = (track, packet) => {\n if (track.metadata.hasOnlyKeyPackets && packet.type !== \"key\") {\n throw new Error(\"Cannot add non-key packets to a hasOnlyKeyPackets video track.\");\n }\n };\n class VideoEncoderWrapper {\n setError(error) {\n if (!this.errorSet) {\n this.error = error;\n this.errorSet = true;\n }\n }\n constructor(source, encodingConfig) {\n this.source = source;\n this.encodingConfig = encodingConfig;\n this.ensureEncoderPromise = null;\n this.encoderInitialized = false;\n this.encoder = null;\n this.encoderConfig = null;\n this.encodersReclaimed = false;\n this.muxer = null;\n this.lastMultipleOfKeyFrameInterval = -1;\n this.emittedEncoderPackets = 0;\n this.codedWidth = null;\n this.codedHeight = null;\n this.outputWidth = null;\n this.outputHeight = null;\n this.frameRateLastSample = null;\n this.frameRateLastTimestamp = null;\n this.frameRateLastEndTimestamp = null;\n this.preciseTimings = [];\n this.customEncoder = null;\n this.customEncoderCallSerializer = new CallSerializer;\n this.customEncoderQueueSize = 0;\n this.defaultEncodeOptions = {};\n this.alphaEncoder = null;\n this.splitter = null;\n this.splitterCreationFailed = false;\n this.alphaFrameQueue = [];\n this.error = null;\n this.errorSet = false;\n this.lastMuxerPromise = Promise.resolve();\n this.closed = false;\n }\n async add(videoSample, shouldClose, encodeOptions) {\n const originalSample = videoSample;\n try {\n this.checkForEncoderError();\n this.source._ensureValidAdd();\n const config = this.encodingConfig;\n const sizeChangeBehavior = config.sizeChangeBehavior ?? \"deny\";\n let isSizeChange = false;\n if (this.codedWidth !== null && this.codedHeight !== null) {\n if (videoSample.codedWidth !== this.codedWidth || videoSample.codedHeight !== this.codedHeight) {\n isSizeChange = true;\n if (sizeChangeBehavior === \"deny\") {\n throw new Error(`Video sample size must remain constant. Expected ${this.codedWidth}x${this.codedHeight},` + ` got ${videoSample.codedWidth}x${videoSample.codedHeight}. To allow the sample size to` + ` change over time, set \\`sizeChangeBehavior\\` to a value other than 'deny' in the` + ` encoding options.`);\n }\n }\n } else {\n this.codedWidth = videoSample.codedWidth;\n this.codedHeight = videoSample.codedHeight;\n }\n const hasTransformConfig = config.transform?.width !== undefined || config.transform?.height !== undefined || config.transform?.rotate !== undefined || config.transform?.flip !== undefined || config.transform?.crop !== undefined || config.transform?.force === true;\n const needsTransform = hasTransformConfig || isSizeChange && sizeChangeBehavior !== \"passThrough\";\n if (needsTransform) {\n let targetWidth = config.transform?.width;\n let targetHeight = config.transform?.height;\n let appliedFit = config.transform?.fit ?? \"fill\";\n if (isSizeChange && sizeChangeBehavior !== \"passThrough\") {\n assert(this.outputWidth);\n assert(this.outputHeight);\n assert(sizeChangeBehavior !== \"deny\");\n targetWidth = this.outputWidth;\n targetHeight = this.outputHeight;\n appliedFit = sizeChangeBehavior;\n }\n const transformed = await videoSample.transform({\n width: targetWidth,\n height: targetHeight,\n roundDimensionsTo: 2,\n crop: config.transform?.crop,\n rotate: config.transform?.rotate,\n flip: config.transform?.flip,\n fit: appliedFit,\n alpha: config.alpha\n });\n if (this.outputWidth === null || this.outputHeight === null) {\n this.outputWidth = transformed.displayWidth;\n this.outputHeight = transformed.displayHeight;\n }\n if (shouldClose) {\n videoSample.close();\n }\n videoSample = transformed;\n shouldClose = true;\n } else {\n if (this.outputWidth === null || this.outputHeight === null) {\n this.outputWidth = videoSample.codedWidth;\n this.outputHeight = videoSample.codedHeight;\n }\n }\n const frameRate = config.transform?.frameRate;\n if (frameRate !== undefined) {\n const originalEndTimestamp = videoSample.timestamp + videoSample.duration;\n const alignedTimestamp = floorToDivisor(videoSample.timestamp, frameRate);\n if (this.frameRateLastSample !== null) {\n if (alignedTimestamp <= this.frameRateLastTimestamp) {\n this.frameRateLastSample.close();\n this.frameRateLastSample = videoSample.clone();\n this.frameRateLastEndTimestamp = originalEndTimestamp;\n return;\n } else {\n await this.padFrameRate(alignedTimestamp, encodeOptions);\n }\n }\n if (videoSample === originalSample) {\n videoSample = videoSample.clone();\n shouldClose = true;\n }\n videoSample.setTimestamp(alignedTimestamp);\n videoSample.setDuration(1 / frameRate);\n this.frameRateLastSample?.close();\n this.frameRateLastSample = videoSample.clone();\n this.frameRateLastTimestamp = alignedTimestamp;\n this.frameRateLastEndTimestamp = originalEndTimestamp;\n }\n await this.processAndEncode(videoSample, encodeOptions);\n } finally {\n if (shouldClose) {\n videoSample.close();\n }\n }\n }\n async processAndEncode(videoSample, encodeOptions) {\n const config = this.encodingConfig;\n let samplesToEncode;\n if (config.transform?.process) {\n let processed = config.transform.process(videoSample);\n if (isThenable(processed)) {\n processed = await processed;\n }\n if (processed === null) {\n return;\n }\n if (!Array.isArray(processed)) {\n processed = [processed];\n }\n const mappedSamples = [];\n try {\n for (const x of processed) {\n if (x instanceof VideoSample) {\n mappedSamples.push(x);\n } else if (typeof VideoFrame !== \"undefined\" && x instanceof VideoFrame) {\n mappedSamples.push(new VideoSample(x));\n } else {\n mappedSamples.push(new VideoSample(x, {\n timestamp: videoSample.timestamp,\n duration: videoSample.duration\n }));\n }\n }\n } catch (error) {\n for (const sample of mappedSamples) {\n if (sample !== videoSample) {\n sample.close();\n }\n }\n for (const x of processed) {\n if (x instanceof VideoSample && x !== videoSample) {\n x.close();\n } else if (typeof VideoFrame !== \"undefined\" && x instanceof VideoFrame) {\n x.close();\n }\n }\n throw error;\n }\n samplesToEncode = mappedSamples;\n } else {\n samplesToEncode = [videoSample];\n }\n try {\n for (const sampleToEncode of samplesToEncode) {\n if (!this.encoderInitialized) {\n if (!this.ensureEncoderPromise) {\n this.ensureEncoder(sampleToEncode);\n }\n if (!this.encoderInitialized) {\n await this.ensureEncoderPromise;\n }\n }\n assert(this.encoderInitialized);\n if (this.closed) {\n break;\n }\n if (this.encodersReclaimed) {\n this.recreateWebCodecsEncoders();\n }\n const keyFrameInterval = this.encodingConfig.keyFrameInterval ?? 2;\n const multipleOfKeyFrameInterval = Math.floor(sampleToEncode.timestamp / keyFrameInterval);\n const mergedEncodeOptions = {\n ...this.defaultEncodeOptions,\n ...sampleToEncode.encodeOptions,\n ...encodeOptions\n };\n const finalEncodeOptions = {\n ...mergedEncodeOptions,\n keyFrame: mergedEncodeOptions.keyFrame !== undefined ? mergedEncodeOptions.keyFrame : keyFrameInterval === 0 || multipleOfKeyFrameInterval !== this.lastMultipleOfKeyFrameInterval\n };\n this.lastMultipleOfKeyFrameInterval = multipleOfKeyFrameInterval;\n this.encodingConfig.onEncodedSample?.(sampleToEncode);\n if (this.customEncoder) {\n this.customEncoderQueueSize++;\n const clonedSample = sampleToEncode.clone();\n const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample, finalEncodeOptions)).catch((error) => this.setError(error)).finally(() => {\n this.customEncoderQueueSize--;\n clonedSample.close();\n });\n if (this.customEncoderQueueSize >= 4) {\n await promise;\n }\n } else {\n assert(this.encoder);\n const videoFrame = sampleToEncode.toVideoFrame();\n const preciseTimingIndex = binarySearchLessOrEqual(this.preciseTimings, videoFrame.timestamp, (x) => x.microsecondTimestamp);\n const existingEntry = preciseTimingIndex !== -1 ? this.preciseTimings[preciseTimingIndex] : null;\n if (existingEntry && existingEntry.microsecondTimestamp === videoFrame.timestamp) {\n if (existingEntry.timestamp !== sampleToEncode.timestamp) {\n existingEntry.timestampIsValid = false;\n }\n if (existingEntry.duration !== sampleToEncode.duration) {\n existingEntry.durationIsValid = false;\n }\n } else {\n this.preciseTimings.splice(preciseTimingIndex + 1, 0, {\n microsecondTimestamp: videoFrame.timestamp,\n timestamp: sampleToEncode.timestamp,\n duration: sampleToEncode.duration,\n timestampIsValid: true,\n durationIsValid: true\n });\n if (this.preciseTimings.length > 128) {\n this.preciseTimings.shift();\n }\n }\n if (!this.alphaEncoder) {\n try {\n this.encoder.encode(videoFrame, finalEncodeOptions);\n } finally {\n videoFrame.close();\n }\n } else {\n const frameDefinitelyHasNoAlpha = !!videoFrame.format && !videoFrame.format.includes(\"A\");\n if (frameDefinitelyHasNoAlpha || this.splitterCreationFailed) {\n this.alphaFrameQueue.push(null);\n try {\n this.encoder.encode(videoFrame, finalEncodeOptions);\n } finally {\n videoFrame.close();\n }\n } else {\n if (!this.splitter) {\n this.splitter = new ColorAlphaSplitter;\n }\n const { colorFrame, alphaFrame } = await this.splitter.split(videoFrame);\n this.alphaFrameQueue.push(alphaFrame);\n try {\n this.encoder.encode(colorFrame, finalEncodeOptions);\n } finally {\n colorFrame.close();\n }\n }\n }\n if (this.encoder.encodeQueueSize >= 4) {\n await new Promise((resolve) => this.encoder.addEventListener(\"dequeue\", resolve, { once: true }));\n }\n }\n await this.lastMuxerPromise;\n }\n } finally {\n for (const sample of samplesToEncode) {\n if (sample !== videoSample) {\n sample.close();\n }\n }\n }\n }\n async padFrameRate(until, encodeOptions) {\n const frameRate = this.encodingConfig.transform.frameRate;\n assert(this.frameRateLastSample);\n const frameDifference = Math.round((until - this.frameRateLastTimestamp) * frameRate);\n for (let i = 1;i < frameDifference; i++) {\n const env_1 = { stack: [], error: undefined, hasError: false };\n try {\n const sample = __addDisposableResource2(env_1, this.frameRateLastSample.clone(), false);\n sample.setTimestamp(this.frameRateLastTimestamp + i / frameRate);\n sample.setDuration(1 / frameRate);\n await this.processAndEncode(sample, encodeOptions);\n } catch (e_1) {\n env_1.error = e_1;\n env_1.hasError = true;\n } finally {\n __disposeResources2(env_1);\n }\n }\n }\n ensureEncoder(videoSample) {\n this.ensureEncoderPromise = (async () => {\n const quality = resolveQuality(this.encodingConfig.quality, this.encodingConfig.bitrate);\n assert(quality !== undefined);\n const candidates = buildVideoEncoderConfigs({\n ...this.encodingConfig,\n quality,\n width: videoSample.codedWidth,\n height: videoSample.codedHeight,\n squarePixelWidth: videoSample.squarePixelWidth,\n squarePixelHeight: videoSample.squarePixelHeight,\n framerate: this.source._connectedTrack?.metadata.frameRate\n });\n let selected = null;\n let MatchingCustomEncoder;\n for (const candidate of candidates) {\n const candidateConfig = candidate.config;\n this.encodingConfig.onEncoderConfig?.(candidateConfig);\n MatchingCustomEncoder = customVideoEncoders.find((x) => x.supports(this.encodingConfig.codec, candidateConfig));\n if (MatchingCustomEncoder) {\n selected = candidate;\n break;\n }\n if (typeof VideoEncoder === \"undefined\") {\n continue;\n }\n candidateConfig.alpha = \"discard\";\n if (this.encodingConfig.alpha === \"keep\") {\n candidateConfig.latencyMode = \"quality\";\n }\n const hasOddDimension = candidateConfig.width % 2 === 1 || candidateConfig.height % 2 === 1;\n if (hasOddDimension && (this.encodingConfig.codec === \"avc\" || this.encodingConfig.codec === \"hevc\")) {\n throw new Error(`The dimensions ${candidateConfig.width}x${candidateConfig.height} are not supported for codec` + ` '${this.encodingConfig.codec}'; both width and height must be even numbers. Make sure to` + ` round your dimensions to the nearest even number.`);\n }\n try {\n const support = await VideoEncoder.isConfigSupported(candidateConfig);\n if (support.supported) {\n selected = candidate;\n break;\n }\n } catch {}\n }\n if (!selected) {\n if (typeof VideoEncoder === \"undefined\") {\n throw new Error(missingWebCodecsClassMessage(\"VideoEncoder\"));\n }\n const firstConfig = candidates[0].config;\n const rateControls = candidates.map(({ config, quantizer }) => quantizer !== null ? `quantizer ${quantizer}` : `${config.bitrate} bps`);\n throw new Error(`This specific encoder configuration (${firstConfig.codec}, ${rateControls.join(\" / \")},` + ` ${firstConfig.width}x${firstConfig.height}, hardware acceleration:` + ` ${firstConfig.hardwareAcceleration ?? \"no-preference\"}) is not supported in this environment.` + ` Consider using another codec or changing your video parameters.`);\n }\n const encoderConfig = selected.config;\n if (selected.quantizer !== null) {\n this.defaultEncodeOptions = buildQuantizerEncodeOptions(this.encodingConfig.codec, selected.quantizer);\n } else {\n this.source._nominalBitrate = encoderConfig.bitrate ?? null;\n }\n if (MatchingCustomEncoder) {\n this.customEncoder = new MatchingCustomEncoder;\n this.customEncoder.codec = this.encodingConfig.codec;\n this.customEncoder.config = encoderConfig;\n this.customEncoder.onPacket = (packet, meta) => {\n if (!(packet instanceof EncodedPacket)) {\n throw new TypeError(\"The first argument passed to onPacket must be an EncodedPacket.\");\n }\n if (meta !== undefined && (!meta || typeof meta !== \"object\")) {\n throw new TypeError(\"The second argument passed to onPacket must be an object or undefined.\");\n }\n maybeEnsureIsKeyPacket(this.source._connectedTrack, packet);\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n this.lastMuxerPromise = this.muxer.addEncodedVideoPacket(this.source._connectedTrack, packet, meta).catch((error) => {\n this.setError(error);\n });\n };\n this.customEncoder.onError = (error) => {\n this.setError(error);\n };\n await this.customEncoder.init();\n } else {\n this.encoderConfig = encoderConfig;\n this.createWebCodecsEncoders();\n }\n assert(this.source._connectedTrack);\n this.muxer = this.source._connectedTrack.output._muxer;\n this.encoderInitialized = true;\n })();\n }\n createWebCodecsEncoders() {\n const encoderConfig = this.encoderConfig;\n assert(encoderConfig);\n const colorChunkQueue = [];\n const nullAlphaChunkQueue = [];\n let encodedAlphaChunkCount = 0;\n let alphaEncoderQueue = 0;\n const handleError = (error, stack) => {\n if (isCodecReclaimedError(error)) {\n this.encodersReclaimed = true;\n return;\n }\n error.stack = stack;\n this.setError(error);\n };\n const stack = new Error(\"Encoding error\").stack;\n this.encoder = new VideoEncoder({\n output: (chunk, meta) => {\n if (!this.alphaEncoder) {\n this.addPacket(chunk, null, meta);\n return;\n }\n const alphaFrame = this.alphaFrameQueue.shift();\n assert(alphaFrame !== undefined);\n if (alphaFrame) {\n this.alphaEncoder.encode(alphaFrame, {\n ...this.defaultEncodeOptions,\n keyFrame: chunk.type === \"key\"\n });\n alphaEncoderQueue++;\n alphaFrame.close();\n colorChunkQueue.push({ chunk, meta });\n } else {\n if (alphaEncoderQueue === 0) {\n this.addPacket(chunk, null, meta);\n } else {\n nullAlphaChunkQueue.push(encodedAlphaChunkCount + alphaEncoderQueue);\n colorChunkQueue.push({ chunk, meta });\n }\n }\n },\n error: (error) => handleError(error, stack)\n });\n this.encoder.configure(encoderConfig);\n if (this.encodingConfig.alpha === \"keep\") {\n const stack = new Error(\"Encoding error\").stack;\n this.alphaEncoder = new VideoEncoder({\n output: (chunk, meta) => {\n alphaEncoderQueue--;\n const colorChunk = colorChunkQueue.shift();\n assert(colorChunk !== undefined);\n this.addPacket(colorChunk.chunk, chunk, colorChunk.meta);\n encodedAlphaChunkCount++;\n while (nullAlphaChunkQueue.length > 0 && nullAlphaChunkQueue[0] === encodedAlphaChunkCount) {\n nullAlphaChunkQueue.shift();\n const colorChunk = colorChunkQueue.shift();\n assert(colorChunk !== undefined);\n this.addPacket(colorChunk.chunk, null, colorChunk.meta);\n }\n },\n error: (error) => handleError(error, stack)\n });\n this.alphaEncoder.configure(encoderConfig);\n }\n }\n addPacket(colorChunk, alphaChunk, meta) {\n const sideData = {};\n if (alphaChunk) {\n const alphaData = new Uint8Array(alphaChunk.byteLength);\n alphaChunk.copyTo(alphaData);\n sideData.alpha = alphaData;\n }\n let packet = EncodedPacket.fromEncodedChunk(colorChunk, sideData);\n const encoderConfig = this.encoderConfig;\n assert(encoderConfig);\n if (meta?.decoderConfig) {\n meta.decoderConfig.codec = extractVideoCodecString({\n width: encoderConfig.width,\n height: encoderConfig.height,\n codec: this.encodingConfig.codec,\n codecDescription: meta.decoderConfig.description ? toUint8Array(meta.decoderConfig.description) : null,\n colorSpace: meta.decoderConfig.colorSpace ?? null,\n avcType: encoderConfig.codec.includes(\"avc1.\") ? 1 : encoderConfig.codec.includes(\"avc3.\") ? 3 : null,\n avcCodecInfo: this.encodingConfig.codec === \"avc\" && !meta.decoderConfig.description ? extractAvcDecoderConfigurationRecord(packet.data) : null,\n hevcCodecInfo: this.encodingConfig.codec === \"hevc\" && !meta.decoderConfig.description ? extractHevcDecoderConfigurationRecord(packet.data) : null,\n vp9CodecInfo: this.encodingConfig.codec === \"vp9\" ? extractVp9CodecInfoFromPacket(packet.data) : null,\n av1CodecInfo: this.encodingConfig.codec === \"av1\" ? extractAv1CodecInfoFromPacket(packet.data) : null,\n proresFormat: null\n });\n }\n const preciseTimingIndex = binarySearchLessOrEqual(this.preciseTimings, colorChunk.timestamp, (x) => x.microsecondTimestamp);\n const entry = preciseTimingIndex !== -1 ? this.preciseTimings[preciseTimingIndex] : null;\n let actualType = null;\n if (this.emittedEncoderPackets === 0 && packet.type === \"delta\" && meta?.decoderConfig) {\n actualType = determineVideoPacketType(this.encodingConfig.codec, meta.decoderConfig, packet.data);\n }\n if (entry && entry.microsecondTimestamp === colorChunk.timestamp || actualType !== null) {\n packet = packet.clone({\n timestamp: entry?.timestampIsValid ? entry.timestamp : undefined,\n duration: entry?.durationIsValid ? entry.duration : undefined,\n type: actualType ?? undefined\n });\n }\n maybeEnsureIsKeyPacket(this.source._connectedTrack, packet);\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n this.lastMuxerPromise = this.muxer.addEncodedVideoPacket(this.source._connectedTrack, packet, meta).catch((error) => {\n this.setError(error);\n });\n this.emittedEncoderPackets++;\n }\n recreateWebCodecsEncoders() {\n assert(this.encoder);\n if (this.encoder.state !== \"closed\") {\n this.encoder.close();\n }\n if (this.alphaEncoder && this.alphaEncoder.state !== \"closed\") {\n this.alphaEncoder.close();\n }\n this.alphaFrameQueue.forEach((x) => x?.close());\n this.alphaFrameQueue.length = 0;\n this.encodersReclaimed = false;\n this.lastMultipleOfKeyFrameInterval = -1;\n this.createWebCodecsEncoders();\n }\n async flushAndClose(forceClose) {\n try {\n if (!forceClose) {\n this.checkForEncoderError();\n if (this.frameRateLastSample) {\n const frameRate = this.encodingConfig.transform.frameRate;\n const alignedEnd = floorToDivisor(this.frameRateLastEndTimestamp, frameRate);\n await this.padFrameRate(alignedEnd);\n }\n }\n this.closed = true;\n if (!forceClose) {\n if (this.customEncoder) {\n this.customEncoderCallSerializer.call(() => this.customEncoder.flush());\n } else if (this.encoder && !this.encodersReclaimed) {\n await this.encoder.flush();\n await this.alphaEncoder?.flush();\n await wait(25);\n }\n }\n } finally {\n this.closed = true;\n this.frameRateLastSample?.close();\n this.frameRateLastSample = null;\n if (this.customEncoder) {\n await this.customEncoderCallSerializer.call(() => this.customEncoder.close()).catch((error) => this.setError(error));\n } else if (this.encoder) {\n if (this.encoder.state !== \"closed\") {\n this.encoder.close();\n }\n if (this.alphaEncoder && this.alphaEncoder.state !== \"closed\") {\n this.alphaEncoder.close();\n }\n this.alphaFrameQueue.forEach((x) => x?.close());\n this.alphaFrameQueue.length = 0;\n this.splitter?.close();\n }\n }\n if (!forceClose) {\n this.checkForEncoderError();\n }\n }\n getQueueSize() {\n if (this.customEncoder) {\n return this.customEncoderQueueSize;\n } else {\n return this.encoder?.encodeQueueSize ?? 0;\n }\n }\n checkForEncoderError() {\n if (this.errorSet) {\n throw this.error;\n }\n }\n }\n var splitterWorkerUrl = null;\n\n class ColorAlphaSplitter {\n constructor() {\n this.worker = null;\n this.pendingRequests = new Map;\n this.nextRequestId = 0;\n }\n split(sourceFrame) {\n if (!this.worker) {\n if (!splitterWorkerUrl) {\n const blob = new Blob([`(${colorAlphaSplitterWorkerCode.toString()})()`], { type: \"application/javascript\" });\n splitterWorkerUrl = URL.createObjectURL(blob);\n }\n this.worker = new Worker(splitterWorkerUrl);\n this.worker.addEventListener(\"message\", (event) => {\n const data = event.data;\n const pending = this.pendingRequests.get(data.id);\n if (!pending) {\n return;\n }\n this.pendingRequests.delete(data.id);\n if (\"error\" in data) {\n pending.reject(new Error(data.error));\n } else {\n pending.resolve({ colorFrame: data.colorFrame, alphaFrame: data.alphaFrame });\n }\n });\n this.worker.addEventListener(\"error\", (event) => {\n const error = new Error(event.message || \"Color/alpha splitter worker error.\");\n for (const pending of this.pendingRequests.values()) {\n pending.reject(error);\n }\n this.pendingRequests.clear();\n });\n }\n const id = this.nextRequestId++;\n const pending = promiseWithResolvers();\n this.pendingRequests.set(id, pending);\n this.worker.postMessage({ id, sourceFrame }, { transfer: [sourceFrame] });\n return pending.promise;\n }\n close() {\n this.worker?.terminate();\n this.worker = null;\n const error = new Error(\"Color/alpha splitter closed.\");\n for (const pending of this.pendingRequests.values()) {\n pending.reject(error);\n }\n this.pendingRequests.clear();\n }\n }\n var colorAlphaSplitterWorkerCode = () => {\n let cpuSourceBuffer = null;\n let chain = Promise.resolve();\n self.addEventListener(\"message\", (event) => {\n const { id, sourceFrame } = event.data;\n chain = chain.then(async () => {\n try {\n const { colorFrame, alphaFrame } = await split(sourceFrame);\n self.postMessage({ id, colorFrame, alphaFrame }, { transfer: [colorFrame, alphaFrame] });\n } catch (error) {\n self.postMessage({ id, error: error.message });\n } finally {\n sourceFrame.close();\n }\n });\n });\n const split = async (sourceFrame) => {\n const format = sourceFrame.format;\n if (!format) {\n throw new Error(\"CPU color/alpha splitting requires a known VideoFrame format.\");\n }\n const sourceSize = sourceFrame.allocationSize();\n if (!cpuSourceBuffer || cpuSourceBuffer.byteLength !== sourceSize) {\n cpuSourceBuffer = new Uint8Array(sourceSize);\n }\n await sourceFrame.copyTo(cpuSourceBuffer);\n if (format === \"RGBA\" || format === \"BGRA\") {\n return splitInterleavedRgba(cpuSourceBuffer, format, sourceFrame);\n } else if (format === \"I420A\" || format === \"I420AP10\" || format === \"I420AP12\" || format === \"I422A\" || format === \"I422AP10\" || format === \"I422AP12\" || format === \"I444A\" || format === \"I444AP10\" || format === \"I444AP12\") {\n return splitPlanarYuvA(cpuSourceBuffer, format, sourceFrame);\n }\n throw new Error(`CPU color/alpha splitting does not support format '${format}'.`);\n };\n const splitInterleavedRgba = (source, format, sourceFrame) => {\n const width = sourceFrame.visibleRect?.width ?? sourceFrame.codedWidth;\n const height = sourceFrame.visibleRect?.height ?? sourceFrame.codedHeight;\n const pixelCount = width * height;\n const chromaW = Math.ceil(width / 2);\n const chromaH = Math.ceil(height / 2);\n const alphaSize = pixelCount + chromaW * chromaH * 2;\n const alphaBuffer = new Uint8Array(alphaSize);\n for (let i = 0, j = 3;i < pixelCount; i++, j += 4) {\n alphaBuffer[i] = source[j];\n }\n alphaBuffer.fill(128, pixelCount);\n const colorFrame = new VideoFrame(source, {\n format: format === \"RGBA\" ? \"RGBX\" : \"BGRX\",\n codedWidth: width,\n codedHeight: height,\n timestamp: sourceFrame.timestamp,\n duration: sourceFrame.duration ?? undefined\n });\n const alphaInit = {\n format: \"I420\",\n codedWidth: width,\n codedHeight: height,\n timestamp: sourceFrame.timestamp,\n duration: sourceFrame.duration ?? undefined,\n colorSpace: {\n fullRange: true,\n matrix: \"bt709\",\n primaries: \"bt709\",\n transfer: \"bt709\"\n },\n transfer: [alphaBuffer.buffer]\n };\n const alphaFrame = new VideoFrame(alphaBuffer, alphaInit);\n return { colorFrame, alphaFrame };\n };\n const splitPlanarYuvA = (source, format, sourceFrame) => {\n const width = sourceFrame.visibleRect?.width ?? sourceFrame.codedWidth;\n const height = sourceFrame.visibleRect?.height ?? sourceFrame.codedHeight;\n const is10 = format.includes(\"P10\");\n const is12 = format.includes(\"P12\");\n const bytesPerSample = is10 || is12 ? 2 : 1;\n let chromaW;\n let chromaH;\n if (format.startsWith(\"I420\")) {\n chromaW = Math.ceil(width / 2);\n chromaH = Math.ceil(height / 2);\n } else if (format.startsWith(\"I422\")) {\n chromaW = Math.ceil(width / 2);\n chromaH = height;\n } else {\n chromaW = width;\n chromaH = height;\n }\n const ySamples = width * height;\n const uvSamples = chromaW * chromaH;\n const yBytes = ySamples * bytesPerSample;\n const uvBytes = uvSamples * bytesPerSample;\n const aBytes = ySamples * bytesPerSample;\n const colorBytes = yBytes + uvBytes * 2;\n const colorFormat = format.replace(\"A\", \"\");\n const alphaChromaW = Math.ceil(width / 2);\n const alphaChromaH = Math.ceil(height / 2);\n const alphaUvSamples = alphaChromaW * alphaChromaH;\n const alphaUvBytes = alphaUvSamples * bytesPerSample;\n const alphaSize = aBytes + 2 * alphaUvBytes;\n const alphaBuffer = new Uint8Array(alphaSize);\n const aPlaneStart = colorBytes;\n alphaBuffer.set(source.subarray(aPlaneStart, aPlaneStart + aBytes), 0);\n const uvOffset = aBytes;\n const neutralChroma = is10 ? 512 : is12 ? 2048 : 128;\n if (bytesPerSample === 1) {\n alphaBuffer.fill(neutralChroma, uvOffset);\n } else {\n const uvView = new Uint16Array(alphaBuffer.buffer, uvOffset, 2 * alphaUvSamples);\n uvView.fill(neutralChroma);\n }\n const alphaFormat = is10 ? \"I420P10\" : is12 ? \"I420P12\" : \"I420\";\n const colorFrame = new VideoFrame(source.subarray(0, colorBytes), {\n format: colorFormat,\n codedWidth: width,\n codedHeight: height,\n timestamp: sourceFrame.timestamp,\n duration: sourceFrame.duration ?? undefined\n });\n const alphaInit = {\n format: alphaFormat,\n codedWidth: width,\n codedHeight: height,\n timestamp: sourceFrame.timestamp,\n duration: sourceFrame.duration ?? undefined,\n colorSpace: {\n fullRange: true,\n matrix: \"bt709\",\n primaries: \"bt709\",\n transfer: \"bt709\"\n },\n transfer: [alphaBuffer.buffer]\n };\n const alphaFrame = new VideoFrame(alphaBuffer, alphaInit);\n return { colorFrame, alphaFrame };\n };\n };\n\n class VideoSampleSource extends VideoSource {\n constructor(encodingConfig) {\n validateVideoEncodingConfig(encodingConfig);\n super(encodingConfig.codec);\n this._encoder = new VideoEncoderWrapper(this, encodingConfig);\n }\n add(videoSample, encodeOptions) {\n if (!(videoSample instanceof VideoSample)) {\n throw new TypeError(\"videoSample must be a VideoSample.\");\n }\n return this._encoder.add(videoSample, false, encodeOptions);\n }\n _flushAndClose(forceClose) {\n return this._encoder.flushAndClose(forceClose);\n }\n }\n class AudioSource extends MediaSource {\n constructor(codec) {\n super();\n if (!AUDIO_CODECS.includes(codec)) {\n throw new TypeError(`Invalid audio codec '${codec}'. Must be one of: ${AUDIO_CODECS.join(\", \")}.`);\n }\n this._codec = codec;\n }\n }\n class AudioEncoderWrapper {\n setError(error) {\n if (!this.errorSet) {\n this.error = error;\n this.errorSet = true;\n }\n }\n constructor(source, encodingConfig) {\n this.source = source;\n this.encodingConfig = encodingConfig;\n this.ensureEncoderPromise = null;\n this.encoderInitialized = false;\n this.encoder = null;\n this.muxer = null;\n this.lastNumberOfChannels = null;\n this.lastSampleRate = null;\n this.isPcmEncoder = false;\n this.outputSampleSize = null;\n this.writeOutputValue = null;\n this.customEncoder = null;\n this.customEncoderCallSerializer = new CallSerializer;\n this.customEncoderQueueSize = 0;\n this.lastEndSampleIndex = null;\n this.resampler = null;\n this.error = null;\n this.errorSet = false;\n this.lastMuxerPromise = Promise.resolve();\n this.closed = false;\n }\n async add(audioSample, shouldClose) {\n try {\n this.checkForEncoderError();\n this.source._ensureValidAdd();\n if (this.lastNumberOfChannels !== null && this.lastSampleRate !== null) {\n if (audioSample.numberOfChannels !== this.lastNumberOfChannels || audioSample.sampleRate !== this.lastSampleRate) {\n throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at` + ` ${this.lastSampleRate} Hz, got ${audioSample.numberOfChannels} channels at` + ` ${audioSample.sampleRate} Hz.`);\n }\n } else {\n this.lastNumberOfChannels = audioSample.numberOfChannels;\n this.lastSampleRate = audioSample.sampleRate;\n }\n const config = this.encodingConfig;\n const needsResample = config.transform?.numberOfChannels !== undefined || config.transform?.sampleRate !== undefined;\n if (needsResample) {\n if (!this.resampler) {\n this.resampler = new AudioResampler({\n targetNumberOfChannels: config.transform.numberOfChannels ?? audioSample.numberOfChannels,\n targetSampleRate: config.transform.sampleRate ?? audioSample.sampleRate,\n onSample: async (sample) => {\n await this.processAndEncode(sample, true);\n }\n });\n }\n await this.resampler.add(audioSample);\n } else {\n await this.processAndEncode(audioSample, shouldClose);\n }\n } finally {\n if (shouldClose) {\n audioSample.close();\n }\n }\n }\n async processAndEncode(audioSample, shouldClose) {\n const config = this.encodingConfig;\n if (config.transform?.sampleFormat !== undefined && toInterleavedAudioFormat(audioSample.format) !== config.transform.sampleFormat) {\n const newSample = audioSampleToInterleavedFormat(audioSample, config.transform.sampleFormat);\n if (shouldClose) {\n audioSample.close();\n }\n audioSample = newSample;\n shouldClose = true;\n }\n if (config.transform?.process) {\n try {\n let processed = config.transform.process(audioSample);\n if (isThenable(processed)) {\n processed = await processed;\n }\n if (processed === null) {\n return;\n }\n if (!Array.isArray(processed)) {\n processed = [processed];\n }\n try {\n for (const sample of processed) {\n if (!(sample instanceof AudioSample)) {\n throw new TypeError(\"The audio process function must return an AudioSample, null, or an array of\" + \" AudioSamples.\");\n }\n }\n for (const sample of processed) {\n await this.encodeSample(sample, true);\n }\n } finally {\n for (const sample of processed) {\n if (sample instanceof AudioSample) {\n sample.close();\n }\n }\n }\n } finally {\n if (shouldClose) {\n audioSample.close();\n }\n }\n } else {\n await this.encodeSample(audioSample, shouldClose);\n }\n }\n async encodeSample(audioSample, shouldClose) {\n try {\n if (!this.encoderInitialized) {\n if (!this.ensureEncoderPromise) {\n this.ensureEncoder(audioSample);\n }\n if (!this.encoderInitialized) {\n await this.ensureEncoderPromise;\n }\n }\n assert(this.encoderInitialized);\n if (this.closed) {\n return;\n }\n {\n const startSampleIndex = Math.round(audioSample.timestamp * audioSample.sampleRate);\n const endSampleIndex = Math.round((audioSample.timestamp + audioSample.duration) * audioSample.sampleRate);\n if (this.lastEndSampleIndex === null) {\n this.lastEndSampleIndex = endSampleIndex;\n } else {\n const sampleDiff = startSampleIndex - this.lastEndSampleIndex;\n if (sampleDiff >= 64) {\n const fillSample = new AudioSample({\n data: new Float32Array(sampleDiff * audioSample.numberOfChannels),\n format: \"f32-planar\",\n sampleRate: audioSample.sampleRate,\n numberOfChannels: audioSample.numberOfChannels,\n numberOfFrames: sampleDiff,\n timestamp: this.lastEndSampleIndex / audioSample.sampleRate\n });\n await this.encodeSample(fillSample, true);\n }\n this.lastEndSampleIndex += audioSample.numberOfFrames;\n }\n }\n this.encodingConfig.onEncodedSample?.(audioSample);\n if (this.customEncoder) {\n this.customEncoderQueueSize++;\n const clonedSample = audioSample.clone();\n const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample)).catch((error) => this.setError(error)).finally(() => {\n this.customEncoderQueueSize--;\n clonedSample.close();\n });\n if (this.customEncoderQueueSize >= 4) {\n await promise;\n }\n await this.lastMuxerPromise;\n } else if (this.isPcmEncoder) {\n await this.doPcmEncoding(audioSample, shouldClose);\n } else {\n assert(this.encoder);\n const audioData = audioSample.toAudioData();\n this.encoder.encode(audioData);\n audioData.close();\n if (shouldClose) {\n audioSample.close();\n }\n if (this.encoder.encodeQueueSize >= 4) {\n await new Promise((resolve) => this.encoder.addEventListener(\"dequeue\", resolve, { once: true }));\n }\n await this.lastMuxerPromise;\n }\n } finally {\n if (shouldClose) {\n audioSample.close();\n }\n }\n }\n async doPcmEncoding(audioSample, shouldClose) {\n assert(this.outputSampleSize);\n assert(this.writeOutputValue);\n const { numberOfChannels, numberOfFrames, sampleRate, timestamp } = audioSample;\n const CHUNK_SIZE = 2048;\n const outputs = [];\n for (let frame = 0;frame < numberOfFrames; frame += CHUNK_SIZE) {\n const frameCount = Math.min(CHUNK_SIZE, audioSample.numberOfFrames - frame);\n const outputSize = frameCount * numberOfChannels * this.outputSampleSize;\n const outputBuffer = new ArrayBuffer(outputSize);\n const outputView = new DataView(outputBuffer);\n outputs.push({ frameCount, view: outputView });\n }\n const isS32 = audioSample.format === \"s32\" || audioSample.format === \"s32-planar\";\n const readFormat = isS32 ? \"s32-planar\" : \"f32-planar\";\n const scale = isS32 ? 1 / 2147483648 : 1;\n const allocationSize = audioSample.allocationSize({ planeIndex: 0, format: readFormat });\n const values = isS32 ? new Int32Array(allocationSize / 4) : new Float32Array(allocationSize / 4);\n for (let i = 0;i < numberOfChannels; i++) {\n audioSample.copyTo(values, { planeIndex: i, format: readFormat });\n for (let j = 0;j < outputs.length; j++) {\n const { frameCount, view } = outputs[j];\n for (let k = 0;k < frameCount; k++) {\n this.writeOutputValue(view, (k * numberOfChannels + i) * this.outputSampleSize, values[j * CHUNK_SIZE + k] * scale);\n }\n }\n }\n if (shouldClose) {\n audioSample.close();\n }\n const meta = {\n decoderConfig: {\n codec: this.encodingConfig.codec,\n numberOfChannels,\n sampleRate\n }\n };\n for (let i = 0;i < outputs.length; i++) {\n const { frameCount, view } = outputs[i];\n const outputBuffer = view.buffer;\n const startFrame = i * CHUNK_SIZE;\n const packet = new EncodedPacket(new Uint8Array(outputBuffer), \"key\", timestamp + startFrame / sampleRate, frameCount / sampleRate);\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n await this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta);\n }\n }\n ensureEncoder(audioSample) {\n this.ensureEncoderPromise = (async () => {\n const { numberOfChannels, sampleRate } = audioSample;\n const quality = resolveQuality(this.encodingConfig.quality, this.encodingConfig.bitrate);\n const encoderConfig = buildAudioEncoderConfig({\n numberOfChannels,\n sampleRate,\n ...this.encodingConfig,\n quality\n });\n this.encodingConfig.onEncoderConfig?.(encoderConfig);\n this.source._nominalBitrate = encoderConfig.bitrate ?? null;\n const MatchingCustomEncoder = customAudioEncoders.find((x) => x.supports(this.encodingConfig.codec, encoderConfig));\n if (MatchingCustomEncoder) {\n this.customEncoder = new MatchingCustomEncoder;\n this.customEncoder.codec = this.encodingConfig.codec;\n this.customEncoder.config = encoderConfig;\n this.customEncoder.onPacket = (packet, meta) => {\n if (!(packet instanceof EncodedPacket)) {\n throw new TypeError(\"The first argument passed to onPacket must be an EncodedPacket.\");\n }\n if (meta !== undefined && (!meta || typeof meta !== \"object\")) {\n throw new TypeError(\"The second argument passed to onPacket must be an object or undefined.\");\n }\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n this.lastMuxerPromise = this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta).catch((error) => {\n this.setError(error);\n });\n };\n this.customEncoder.onError = (error) => {\n this.setError(error);\n };\n await this.customEncoder.init();\n } else if (PCM_AUDIO_CODECS.includes(this.encodingConfig.codec)) {\n this.initPcmEncoder();\n } else {\n if (typeof AudioEncoder === \"undefined\") {\n throw new Error(missingWebCodecsClassMessage(\"AudioEncoder\"));\n }\n let supported;\n try {\n const support = await AudioEncoder.isConfigSupported(encoderConfig);\n supported = support.supported ?? false;\n } catch {\n supported = false;\n }\n if (!supported) {\n throw new Error(`This specific encoder configuration (${encoderConfig.codec}, ${encoderConfig.bitrate} bps,` + ` ${encoderConfig.numberOfChannels} channels, ${encoderConfig.sampleRate} Hz) is not` + ` supported in this environment. Consider using another codec or changing your` + ` audio parameters.`);\n }\n const stack = new Error(\"Encoding error\").stack;\n this.encoder = new AudioEncoder({\n output: (chunk, meta) => {\n if (this.encodingConfig.codec === \"aac\" && meta?.decoderConfig) {\n let needsDescriptionOverwrite = false;\n if (!meta.decoderConfig.description || meta.decoderConfig.description.byteLength < 2) {\n needsDescriptionOverwrite = true;\n } else {\n const audioSpecificConfig = parseAacAudioSpecificConfig(toUint8Array(meta.decoderConfig.description));\n needsDescriptionOverwrite = audioSpecificConfig.objectType === 0;\n }\n if (needsDescriptionOverwrite) {\n const objectType = Number(last(encoderConfig.codec.split(\".\")));\n meta.decoderConfig.description = buildAacAudioSpecificConfig({\n objectType,\n outputNumberOfChannels: meta.decoderConfig.numberOfChannels,\n outputSampleRate: meta.decoderConfig.sampleRate\n });\n }\n }\n let packet = EncodedPacket.fromEncodedChunk(chunk);\n packet = packet.clone({\n timestamp: roundToDivisor(packet.timestamp, encoderConfig.sampleRate),\n duration: chunk.duration != null ? roundToDivisor(packet.duration, encoderConfig.sampleRate) : undefined\n });\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n this.lastMuxerPromise = this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta).catch((error) => {\n this.setError(error);\n });\n },\n error: (error) => {\n error.stack = stack;\n this.setError(error);\n }\n });\n this.encoder.configure(encoderConfig);\n }\n assert(this.source._connectedTrack);\n this.muxer = this.source._connectedTrack.output._muxer;\n this.encoderInitialized = true;\n })();\n }\n initPcmEncoder() {\n this.isPcmEncoder = true;\n const codec = this.encodingConfig.codec;\n const { dataType, sampleSize, littleEndian } = parsePcmCodec(codec);\n this.outputSampleSize = sampleSize;\n switch (sampleSize) {\n case 1:\n {\n if (dataType === \"unsigned\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setUint8(byteOffset, clamp(Math.round(value * 128) + 128, 0, 255));\n } else if (dataType === \"signed\") {\n this.writeOutputValue = (view, byteOffset, value) => {\n view.setInt8(byteOffset, clamp(Math.round(value * 128), -128, 127));\n };\n } else if (dataType === \"ulaw\") {\n this.writeOutputValue = (view, byteOffset, value) => {\n const int16 = clamp(Math.round(value * 32768), -32768, 32767);\n view.setUint8(byteOffset, toUlaw(int16));\n };\n } else if (dataType === \"alaw\") {\n this.writeOutputValue = (view, byteOffset, value) => {\n const int16 = clamp(Math.round(value * 32768), -32768, 32767);\n view.setUint8(byteOffset, toAlaw(int16));\n };\n } else {\n assert(false);\n }\n }\n ;\n break;\n case 2:\n {\n if (dataType === \"unsigned\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setUint16(byteOffset, clamp(Math.round(value * 32768) + 32768, 0, 65535), littleEndian);\n } else if (dataType === \"signed\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setInt16(byteOffset, clamp(Math.round(value * 32768), -32768, 32767), littleEndian);\n } else {\n assert(false);\n }\n }\n ;\n break;\n case 3:\n {\n if (dataType === \"unsigned\") {\n this.writeOutputValue = (view, byteOffset, value) => setUint24(view, byteOffset, clamp(Math.round(value * 8388608) + 8388608, 0, 16777215), littleEndian);\n } else if (dataType === \"signed\") {\n this.writeOutputValue = (view, byteOffset, value) => setInt24(view, byteOffset, clamp(Math.round(value * 8388608), -8388608, 8388607), littleEndian);\n } else {\n assert(false);\n }\n }\n ;\n break;\n case 4:\n {\n if (dataType === \"unsigned\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setUint32(byteOffset, clamp(Math.round(value * 2147483648) + 2147483648, 0, 4294967295), littleEndian);\n } else if (dataType === \"signed\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setInt32(byteOffset, clamp(Math.round(value * 2147483648), -2147483648, 2147483647), littleEndian);\n } else if (dataType === \"float\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setFloat32(byteOffset, value, littleEndian);\n } else {\n assert(false);\n }\n }\n ;\n break;\n case 8:\n {\n if (dataType === \"float\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setFloat64(byteOffset, value, littleEndian);\n } else {\n assert(false);\n }\n }\n ;\n break;\n default:\n {\n assertNever(sampleSize);\n assert(false);\n }\n ;\n }\n }\n async flushAndClose(forceClose) {\n try {\n if (!forceClose) {\n this.checkForEncoderError();\n if (this.resampler) {\n await this.resampler.finalize();\n }\n }\n this.closed = true;\n if (!forceClose) {\n if (this.customEncoder) {\n this.customEncoderCallSerializer.call(() => this.customEncoder.flush());\n } else if (this.encoder) {\n await this.encoder.flush();\n }\n }\n } finally {\n this.closed = true;\n this.resampler = null;\n if (this.customEncoder) {\n await this.customEncoderCallSerializer.call(() => this.customEncoder.close()).catch((error) => this.setError(error));\n } else if (this.encoder && this.encoder.state !== \"closed\") {\n this.encoder.close();\n }\n }\n if (!forceClose) {\n this.checkForEncoderError();\n }\n }\n getQueueSize() {\n if (this.customEncoder) {\n return this.customEncoderQueueSize;\n } else if (this.isPcmEncoder) {\n return 0;\n } else {\n return this.encoder?.encodeQueueSize ?? 0;\n }\n }\n checkForEncoderError() {\n if (this.errorSet) {\n throw this.error;\n }\n }\n }\n\n class AudioSampleSource extends AudioSource {\n constructor(encodingConfig) {\n validateAudioEncodingConfig(encodingConfig);\n super(encodingConfig.codec);\n this._encoder = new AudioEncoderWrapper(this, encodingConfig);\n }\n add(audioSample) {\n if (!(audioSample instanceof AudioSample)) {\n throw new TypeError(\"audioSample must be an AudioSample.\");\n }\n return this._encoder.add(audioSample, false);\n }\n _flushAndClose(forceClose) {\n return this._encoder.flushAndClose(forceClose);\n }\n }\n class SubtitleSource extends MediaSource {\n constructor(codec) {\n super();\n if (!SUBTITLE_CODECS.includes(codec)) {\n throw new TypeError(`Invalid subtitle codec '${codec}'. Must be one of: ${SUBTITLE_CODECS.join(\", \")}.`);\n }\n this._codec = codec;\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/output-format.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class OutputFormat {\n get supportsVideoRotationMetadata() {\n return this.supportsVideoTransformationMetadata;\n }\n getSupportedVideoCodecs() {\n return this.getSupportedCodecs().filter((codec) => VIDEO_CODECS.includes(codec));\n }\n getSupportedAudioCodecs() {\n return this.getSupportedCodecs().filter((codec) => AUDIO_CODECS.includes(codec));\n }\n getSupportedSubtitleCodecs() {\n return this.getSupportedCodecs().filter((codec) => SUBTITLE_CODECS.includes(codec));\n }\n _codecUnsupportedHint(codec) {\n return \"\";\n }\n _isFragmentedIsobmff() {\n return false;\n }\n }\n\n class IsobmffOutputFormat extends OutputFormat {\n constructor(options = {}) {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (options.fastStart !== undefined && ![false, \"in-memory\", \"reserve\", \"fragmented\"].includes(options.fastStart)) {\n throw new TypeError(\"options.fastStart, when provided, must be false, 'in-memory', 'reserve', or 'fragmented'.\");\n }\n if (options.minimumFragmentDuration !== undefined && (!isNumber(options.minimumFragmentDuration) || options.minimumFragmentDuration < 0)) {\n throw new TypeError(\"options.minimumFragmentDuration, when provided, must be a non-negative number.\");\n }\n if (options.onFtyp !== undefined && typeof options.onFtyp !== \"function\") {\n throw new TypeError(\"options.onFtyp, when provided, must be a function.\");\n }\n if (options.onMoov !== undefined && typeof options.onMoov !== \"function\") {\n throw new TypeError(\"options.onMoov, when provided, must be a function.\");\n }\n if (options.onMdat !== undefined && typeof options.onMdat !== \"function\") {\n throw new TypeError(\"options.onMdat, when provided, must be a function.\");\n }\n if (options.onMoof !== undefined && typeof options.onMoof !== \"function\") {\n throw new TypeError(\"options.onMoof, when provided, must be a function.\");\n }\n if (options.metadataFormat !== undefined && ![\"mdir\", \"mdta\", \"udta\", \"auto\"].includes(options.metadataFormat)) {\n throw new TypeError(\"options.metadataFormat, when provided, must be either 'auto', 'mdir', 'mdta', or 'udta'.\");\n }\n super();\n this._options = options;\n }\n getSupportedTrackCounts() {\n const max = 2 ** 32 - 1;\n return {\n video: { min: 0, max },\n audio: { min: 0, max },\n subtitle: { min: 0, max },\n total: { min: 0, max }\n };\n }\n get supportsVideoTransformationMetadata() {\n return true;\n }\n get supportsTimestampedMediaData() {\n return true;\n }\n get negativeTimestampSupport() {\n return \"full\";\n }\n _createMuxer(output) {\n return new IsobmffMuxer(output, this);\n }\n _isFragmentedIsobmff() {\n return this._options.fastStart === \"fragmented\";\n }\n }\n\n class Mp4OutputFormat extends IsobmffOutputFormat {\n constructor(options) {\n super(options);\n }\n get _name() {\n return \"MP4\";\n }\n get fileExtension() {\n return \".mp4\";\n }\n get mimeType() {\n return \"video/mp4\";\n }\n getSupportedCodecs() {\n return [\n ...VIDEO_CODECS,\n ...NON_PCM_AUDIO_CODECS,\n \"pcm-s16\",\n \"pcm-s16be\",\n \"pcm-s24\",\n \"pcm-s24be\",\n \"pcm-s32\",\n \"pcm-s32be\",\n \"pcm-f32\",\n \"pcm-f32be\",\n \"pcm-f64\",\n \"pcm-f64be\",\n ...SUBTITLE_CODECS\n ];\n }\n _codecUnsupportedHint(codec) {\n if (new MovOutputFormat().getSupportedCodecs().includes(codec)) {\n return \" Switching to MOV will grant support for this codec.\";\n }\n return \"\";\n }\n }\n\n class CmafOutputFormat extends IsobmffOutputFormat {\n constructor(options) {\n super(options);\n }\n get _name() {\n return \"CMAF\";\n }\n get fileExtension() {\n return \".m4s\";\n }\n get mimeType() {\n return \"video/mp4\";\n }\n getSupportedCodecs() {\n return [\n ...VIDEO_CODECS,\n ...NON_PCM_AUDIO_CODECS,\n \"pcm-s16\",\n \"pcm-s16be\",\n \"pcm-s24\",\n \"pcm-s24be\",\n \"pcm-s32\",\n \"pcm-s32be\",\n \"pcm-f32\",\n \"pcm-f32be\",\n \"pcm-f64\",\n \"pcm-f64be\",\n ...SUBTITLE_CODECS\n ];\n }\n }\n\n class MovOutputFormat extends IsobmffOutputFormat {\n constructor(options) {\n super(options);\n }\n get _name() {\n return \"MOV\";\n }\n get fileExtension() {\n return \".mov\";\n }\n get mimeType() {\n return \"video/quicktime\";\n }\n getSupportedCodecs() {\n return [\n ...VIDEO_CODECS,\n ...AUDIO_CODECS\n ];\n }\n _codecUnsupportedHint(codec) {\n if (new Mp4OutputFormat().getSupportedCodecs().includes(codec)) {\n return \" Switching to MP4 will grant support for this codec.\";\n }\n return \"\";\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/output.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var ALL_TRACK_TYPES = [\"video\", \"audio\", \"subtitle\"];\n\n class OutputTrack {\n constructor(id, output, type, source, metadata) {\n this.id = id;\n this.output = output;\n this.type = type;\n this.source = source;\n this.metadata = metadata;\n }\n isVideoTrack() {\n return this.type === \"video\";\n }\n isAudioTrack() {\n return this.type === \"audio\";\n }\n isSubtitleTrack() {\n return this.type === \"subtitle\";\n }\n canBePairedWith(other) {\n if (!(other instanceof OutputTrack)) {\n throw new TypeError(\"other must be an OutputTrack.\");\n }\n if (this === other) {\n return false;\n }\n const thisGroups = toArray(this.metadata.group);\n const otherGroups = toArray(other.metadata.group);\n for (const aGroup of thisGroups) {\n const pairableInSameGroup = this.type !== other.type && otherGroups.some((bGroup) => aGroup === bGroup);\n if (pairableInSameGroup) {\n return true;\n }\n const pairableAcrossGroups = otherGroups.some((bGroup) => aGroup._pairedGroups.has(bGroup));\n if (pairableAcrossGroups) {\n return true;\n }\n }\n return false;\n }\n }\n\n class OutputVideoTrack extends OutputTrack {\n constructor(id, output, source, metadata) {\n super(id, output, \"video\", source, metadata);\n }\n }\n\n class OutputAudioTrack extends OutputTrack {\n constructor(id, output, source, metadata) {\n super(id, output, \"audio\", source, metadata);\n }\n }\n\n class OutputSubtitleTrack extends OutputTrack {\n constructor(id, output, source, metadata) {\n super(id, output, \"subtitle\", source, metadata);\n }\n }\n\n class OutputTrackGroup {\n constructor() {\n this._pairedGroups = new Set;\n }\n pairWith(other) {\n if (!(other instanceof OutputTrackGroup)) {\n throw new TypeError(\"other must be an OutputTrackGroup.\");\n }\n if (this === other) {\n throw new TypeError(\"Cannot pair a group with itself.\");\n }\n this._pairedGroups.add(other);\n other._pairedGroups.add(this);\n }\n }\n var validateBaseTrackMetadata = (metadata) => {\n if (!metadata || typeof metadata !== \"object\") {\n throw new TypeError(\"metadata must be an object.\");\n }\n if (metadata.languageCode !== undefined && !isIso639Dash2LanguageCode(metadata.languageCode)) {\n throw new TypeError(\"metadata.languageCode, when provided, must be a three-letter, ISO 639-2/T language code.\");\n }\n if (metadata.name !== undefined && typeof metadata.name !== \"string\") {\n throw new TypeError(\"metadata.name, when provided, must be a string.\");\n }\n if (metadata.disposition !== undefined) {\n validateTrackDisposition(metadata.disposition);\n }\n if (metadata.maximumPacketCount !== undefined && (!Number.isInteger(metadata.maximumPacketCount) || metadata.maximumPacketCount < 0)) {\n throw new TypeError(\"metadata.maximumPacketCount, when provided, must be a non-negative integer.\");\n }\n if (metadata.bitrate !== undefined && (!Number.isFinite(metadata.bitrate) || metadata.bitrate < 0)) {\n throw new TypeError(\"metadata.bitrate, when provided, must be a non-negative number.\");\n }\n if (metadata.averageBitrate !== undefined && (!Number.isFinite(metadata.averageBitrate) || metadata.averageBitrate < 0)) {\n throw new TypeError(\"metadata.averageBitrate, when provided, must be a non-negative number.\");\n }\n if (metadata.group !== undefined && !(metadata.group instanceof OutputTrackGroup) && (!Array.isArray(metadata.group) || metadata.group.some((group) => !(group instanceof OutputTrackGroup)))) {\n throw new TypeError(\"metadata.group, when provided, must be an OutputTrackGroup instance or an array of\" + \" OutputTrackGroup instances.\");\n }\n };\n\n class Output extends EventEmitter {\n get target() {\n const errorMessage = \"Output.target cannot be used when using PathedTarget with an async callback.\" + \" Use the 'target' event instead.\";\n if (this._rootTargetPromise) {\n throw new TypeError(errorMessage);\n }\n const rootTargetResult = this._getRootTarget();\n if (isThenable(rootTargetResult)) {\n throw new TypeError(errorMessage);\n }\n return rootTargetResult;\n }\n constructor(options) {\n super();\n this.state = \"pending\";\n this.defaultTrackGroup = new OutputTrackGroup;\n this.tracks = [];\n this._onFinalize = null;\n this._unfinalizedTargets = new Set;\n this._rootWriterPromise = null;\n this._startPromise = null;\n this._cancelPromise = null;\n this._finalizePromise = null;\n this._mutex = new AsyncMutex;\n this._metadataTags = {};\n this._rootTarget = null;\n this._rootTargetPromise = null;\n this._firstMediaStreamTimestamp = null;\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (!(options.format instanceof OutputFormat)) {\n throw new TypeError(\"options.format must be an OutputFormat.\");\n }\n if (!(options.target instanceof Target || options.target instanceof PathedTarget)) {\n throw new TypeError(\"options.target must be a Target or a PathedTarget.\");\n }\n if (options.target instanceof Target) {\n this._rememberTarget(options.target);\n }\n if (options.initTarget !== undefined && !(options.initTarget instanceof Target) && typeof options.initTarget !== \"function\") {\n throw new Error(\"options.initTarget, when provided, must be a Target or a function that returns or resolves to\" + \" a Target.\");\n }\n if (options.onFinalize !== undefined && typeof options.onFinalize !== \"function\") {\n throw new TypeError(\"options.onFinalize, when provided, must be a function.\");\n }\n this.format = options.format;\n this._target = options.target;\n this._onFinalize = options.onFinalize ?? null;\n this._initTarget = options.initTarget ?? null;\n if (this._initTarget instanceof Target) {\n this._rememberTarget(this._initTarget);\n }\n this._muxer = options.format._createMuxer(this);\n }\n _getTargetValidated(request) {\n assert(this._target instanceof PathedTarget);\n const result = this._target.getTarget(request);\n const handleResult = (result) => {\n if (!(result instanceof Target)) {\n throw new TypeError(\"getTarget must return a Target.\");\n }\n return result;\n };\n if (isThenable(result)) {\n return result.then(handleResult);\n } else {\n return handleResult(result);\n }\n }\n async _getTarget(request) {\n assert(this._target instanceof PathedTarget);\n const target = await this._getTargetValidated(request);\n this._emit(\"target\", { target, request, isRoot: request.isRoot });\n if (this.state === \"canceled\") {\n await target._close();\n } else {\n this._rememberTarget(target);\n }\n return target;\n }\n _rememberTarget(target) {\n this._unfinalizedTargets.add(target);\n target.on(\"finalized\", () => this._unfinalizedTargets.delete(target), { once: true });\n }\n async _getInitTarget() {\n assert(this._initTarget !== null);\n if (this._initTarget instanceof Target) {\n return this._initTarget;\n }\n const target = await this._initTarget();\n if (this.state === \"canceled\") {\n await target._close();\n } else {\n this._rememberTarget(target);\n }\n return target;\n }\n _hasInitTarget() {\n return this._initTarget !== null;\n }\n _getRootTarget() {\n if (this._rootTarget) {\n return this._rootTarget;\n }\n if (this._rootTargetPromise) {\n return this._rootTargetPromise;\n }\n if (this._target instanceof Target) {\n this._emit(\"target\", { target: this._target, request: null, isRoot: true });\n this._rootTarget = this._target;\n return this._target;\n }\n const request = {\n path: this._target.rootPath,\n isRoot: true,\n mimeType: this.format.mimeType\n };\n const result = this._getTargetValidated(request);\n const handleResult = (target) => {\n if (this.state === \"canceled\") {\n target._close();\n } else {\n this._rememberTarget(target);\n }\n this._emit(\"target\", { target, request, isRoot: true });\n this._rootTarget = target;\n return target;\n };\n if (isThenable(result)) {\n return this._rootTargetPromise = result.then(handleResult);\n } else {\n return handleResult(result);\n }\n }\n _getRootWriter(isMonotonic) {\n return this._rootWriterPromise ??= (async () => {\n const target = await this._getRootTarget();\n const writer = new Writer(target, typeof isMonotonic === \"boolean\" ? isMonotonic : isMonotonic(target));\n writer.start();\n return writer;\n })();\n }\n addVideoTrack(source, metadata = {}) {\n if (!(source instanceof VideoSource)) {\n throw new TypeError(\"source must be a VideoSource.\");\n }\n validateBaseTrackMetadata(metadata);\n if (metadata.rotation !== undefined && ![0, 90, 180, 270].includes(metadata.rotation)) {\n throw new TypeError(`Invalid video rotation: ${metadata.rotation}. Has to be 0, 90, 180 or 270.`);\n }\n if (metadata.flip !== undefined && typeof metadata.flip !== \"boolean\") {\n throw new TypeError(\"metadata.flip, when provided, must be a boolean.\");\n }\n if (metadata.transformationMatrix !== undefined && (!Array.isArray(metadata.transformationMatrix) || metadata.transformationMatrix.length !== 9 || !metadata.transformationMatrix.every((x) => Number.isFinite(x)))) {\n throw new TypeError(\"metadata.transformationMatrix, when provided, must be an array of 9 finite numbers.\");\n }\n if (metadata.frameRate !== undefined && (!Number.isFinite(metadata.frameRate) || metadata.frameRate <= 0)) {\n throw new TypeError(`Invalid video frame rate: ${metadata.frameRate}. Must be a positive number.`);\n }\n if (metadata.hasOnlyKeyPackets !== undefined && typeof metadata.hasOnlyKeyPackets !== \"boolean\") {\n throw new TypeError(\"metadata.hasOnlyKeyPackets, when provided, must be a boolean.\");\n }\n if (metadata.canBeTransparent !== undefined && typeof metadata.canBeTransparent !== \"boolean\") {\n throw new TypeError(\"metadata.canBeTransparent, when provided, must be a boolean.\");\n }\n if (metadata.decoderConfig !== undefined) {\n validateVideoChunkMetadata({ decoderConfig: metadata.decoderConfig }, source._codec);\n }\n if (metadata.primingPacket !== undefined) {\n if (!(metadata.primingPacket instanceof EncodedPacket)) {\n throw new TypeError(\"metadata.primingPacket, when provided, must be an EncodedPacket.\");\n }\n if (metadata.decoderConfig === undefined) {\n throw new TypeError(\"metadata.primingPacket can only be provided alongside metadata.decoderConfig.\");\n }\n }\n const metadataCopy = { ...metadata };\n metadataCopy.group ??= this.defaultTrackGroup;\n return this._addTrack(new OutputVideoTrack(this.tracks.length + 1, this, source, metadataCopy));\n }\n addAudioTrack(source, metadata = {}) {\n if (!(source instanceof AudioSource)) {\n throw new TypeError(\"source must be an AudioSource.\");\n }\n validateBaseTrackMetadata(metadata);\n if (metadata.decoderConfig !== undefined) {\n validateAudioChunkMetadata({ decoderConfig: metadata.decoderConfig }, source._codec);\n }\n if (metadata.primingPacket !== undefined) {\n if (!(metadata.primingPacket instanceof EncodedPacket)) {\n throw new TypeError(\"metadata.primingPacket, when provided, must be an EncodedPacket.\");\n }\n if (metadata.decoderConfig === undefined) {\n throw new TypeError(\"metadata.primingPacket can only be provided alongside metadata.decoderConfig.\");\n }\n }\n const metadataCopy = { ...metadata };\n metadataCopy.group ??= this.defaultTrackGroup;\n return this._addTrack(new OutputAudioTrack(this.tracks.length + 1, this, source, metadataCopy));\n }\n addSubtitleTrack(source, metadata = {}) {\n if (!(source instanceof SubtitleSource)) {\n throw new TypeError(\"source must be a SubtitleSource.\");\n }\n validateBaseTrackMetadata(metadata);\n const metadataCopy = { ...metadata };\n metadataCopy.group ??= this.defaultTrackGroup;\n return this._addTrack(new OutputSubtitleTrack(this.tracks.length + 1, this, source, metadataCopy));\n }\n setMetadataTags(tags) {\n validateMetadataTags(tags);\n if (this.state !== \"pending\") {\n throw new Error(\"Cannot set metadata tags after output has been started or canceled.\");\n }\n this._metadataTags = tags;\n }\n _addTrack(track) {\n if (this.state !== \"pending\") {\n throw new Error(\"Cannot add track after output has been started or canceled.\");\n }\n if (track.source._connectedTrack) {\n throw new Error(\"Source is already used for a track.\");\n }\n const supportedTrackCounts = this.format.getSupportedTrackCounts();\n const presentTracksOfThisType = this.tracks.reduce((count, t) => count + (t.type === track.type ? 1 : 0), 0);\n const maxCount = supportedTrackCounts[track.type].max;\n if (presentTracksOfThisType === maxCount) {\n throw new Error(maxCount === 0 ? `${this.format._name} does not support ${track.type} tracks.` : `${this.format._name} does not support more than ${maxCount} ${track.type} track` + `${maxCount === 1 ? \"\" : \"s\"}.`);\n }\n const maxTotalCount = supportedTrackCounts.total.max;\n if (this.tracks.length === maxTotalCount) {\n throw new Error(`${this.format._name} does not support more than ${maxTotalCount} tracks` + `${maxTotalCount === 1 ? \"\" : \"s\"} in total.`);\n }\n if (track.isVideoTrack()) {\n const supportedVideoCodecs = this.format.getSupportedVideoCodecs();\n if (supportedVideoCodecs.length === 0) {\n throw new Error(`${this.format._name} does not support video tracks.` + this.format._codecUnsupportedHint(track.source._codec));\n } else if (!supportedVideoCodecs.includes(track.source._codec)) {\n throw new Error(`Codec '${track.source._codec}' cannot be contained within ${this.format._name}. Supported` + ` video codecs are: ${supportedVideoCodecs.map((codec) => `'${codec}'`).join(\", \")}.` + this.format._codecUnsupportedHint(track.source._codec));\n }\n } else if (track.isAudioTrack()) {\n const supportedAudioCodecs = this.format.getSupportedAudioCodecs();\n if (supportedAudioCodecs.length === 0) {\n throw new Error(`${this.format._name} does not support audio tracks.` + this.format._codecUnsupportedHint(track.source._codec));\n } else if (!supportedAudioCodecs.includes(track.source._codec)) {\n throw new Error(`Codec '${track.source._codec}' cannot be contained within ${this.format._name}. Supported` + ` audio codecs are: ${supportedAudioCodecs.map((codec) => `'${codec}'`).join(\", \")}.` + this.format._codecUnsupportedHint(track.source._codec));\n }\n } else if (track.isSubtitleTrack()) {\n const supportedSubtitleCodecs = this.format.getSupportedSubtitleCodecs();\n if (supportedSubtitleCodecs.length === 0) {\n throw new Error(`${this.format._name} does not support subtitle tracks.` + this.format._codecUnsupportedHint(track.source._codec));\n } else if (!supportedSubtitleCodecs.includes(track.source._codec)) {\n throw new Error(`Codec '${track.source._codec}' cannot be contained within ${this.format._name}. Supported` + ` subtitle codecs are: ${supportedSubtitleCodecs.map((codec) => `'${codec}'`).join(\", \")}.` + this.format._codecUnsupportedHint(track.source._codec));\n }\n }\n this.tracks.push(track);\n track.source._connectedTrack = track;\n return track;\n }\n hasEnoughTracks() {\n const supportedTrackCounts = this.format.getSupportedTrackCounts();\n for (const trackType of ALL_TRACK_TYPES) {\n const presentTracksOfThisType = this.tracks.reduce((count, track) => count + (track.type === trackType ? 1 : 0), 0);\n const minCount = supportedTrackCounts[trackType].min;\n if (presentTracksOfThisType < minCount) {\n return false;\n }\n }\n const totalMinCount = supportedTrackCounts.total.min;\n if (this.tracks.length < totalMinCount) {\n return false;\n }\n return true;\n }\n async start() {\n const supportedTrackCounts = this.format.getSupportedTrackCounts();\n for (const trackType of ALL_TRACK_TYPES) {\n const presentTracksOfThisType = this.tracks.reduce((count, track) => count + (track.type === trackType ? 1 : 0), 0);\n const minCount = supportedTrackCounts[trackType].min;\n if (presentTracksOfThisType < minCount) {\n throw new Error(minCount === supportedTrackCounts[trackType].max ? `${this.format._name} requires exactly ${minCount} ${trackType}` + ` track${minCount === 1 ? \"\" : \"s\"}.` : `${this.format._name} requires at least ${minCount} ${trackType}` + ` track${minCount === 1 ? \"\" : \"s\"}.`);\n }\n }\n const totalMinCount = supportedTrackCounts.total.min;\n if (this.tracks.length < totalMinCount) {\n throw new Error(totalMinCount === supportedTrackCounts.total.max ? `${this.format._name} requires exactly ${totalMinCount} track` + `${totalMinCount === 1 ? \"\" : \"s\"}.` : `${this.format._name} requires at least ${totalMinCount} track` + `${totalMinCount === 1 ? \"\" : \"s\"}.`);\n }\n if (this.state === \"canceled\") {\n throw new Error(\"Output has been canceled.\");\n }\n if (this._startPromise) {\n Logging._warn(\"Output has already been started.\");\n return this._startPromise;\n }\n return this._startPromise = (async () => {\n this.state = \"started\";\n const releasePromise = this._mutex.acquire();\n try {\n await this._muxer.start();\n const promises = this.tracks.map((track) => track.source._start());\n await Promise.all(promises);\n } finally {\n (await releasePromise)();\n }\n })();\n }\n getMimeType() {\n return this._muxer.getMimeType();\n }\n async cancel() {\n if (this.state === \"canceled\") {\n return this._cancelPromise ?? undefined;\n } else if (this.state === \"finalizing\" || this.state === \"finalized\") {\n if (this.state === \"finalized\") {\n Logging._warn(\"Output has already been finalized.\");\n }\n return;\n }\n return this._cancelPromise = (async () => {\n this.state = \"canceled\";\n const release = await this._mutex.acquire();\n try {\n const promises = this.tracks.map((x) => x.source._flushOrWaitForOngoingClose(true));\n await Promise.all(promises);\n await Promise.all([...this._unfinalizedTargets].map((target) => target._close()));\n this._unfinalizedTargets.clear();\n } finally {\n release();\n }\n })();\n }\n async finalize() {\n if (this.state === \"pending\") {\n throw new Error(\"Cannot finalize before starting.\");\n }\n if (this.state === \"canceled\") {\n throw new Error(\"Cannot finalize after canceling.\");\n }\n if (this._finalizePromise) {\n Logging._warn(\"Output has already been finalized.\");\n return this._finalizePromise;\n }\n return this._finalizePromise = (async () => {\n this.state = \"finalizing\";\n const release = await this._mutex.acquire();\n try {\n const promises = this.tracks.map((x) => x.source._flushOrWaitForOngoingClose(false));\n await Promise.all(promises);\n await this._muxer.finalize();\n if (this._rootWriterPromise) {\n const rootWriter = await this._rootWriterPromise;\n if (!rootWriter.finalized) {\n await rootWriter.flush();\n await rootWriter.finalize();\n }\n }\n if (this._onFinalize) {\n await this._onFinalize();\n }\n this.state = \"finalized\";\n } catch (error) {\n this.state = \"canceled\";\n throw error;\n } finally {\n await Promise.all([...this._unfinalizedTargets].map((target) => target._close().catch(() => {})));\n this._unfinalizedTargets.clear();\n release();\n }\n })();\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/index.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var MEDIABUNNY_LOADED_SYMBOL = Symbol.for(\"mediabunny loaded\");\n if (globalThis[MEDIABUNNY_LOADED_SYMBOL]) {\n Logging._error(`[WARNING]\nMediabunny was loaded twice.` + \" This will likely cause Mediabunny not to work correctly.\" + \" Check if multiple dependencies are importing different versions of Mediabunny,\" + \" or if something is being bundled incorrectly.\");\n }\n globalThis[MEDIABUNNY_LOADED_SYMBOL] = true;\n\n // src/core/utils/error-handler.ts\n function extractErrorMessage(error) {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n }\n\n // src/core/utils/logger.ts\n function isDebugEnabled() {\n const globalAny = globalThis;\n if (globalAny.__VIDTREO_DEBUG__ === true || globalAny.__VIDTREO_DEV__ === true) {\n return true;\n }\n const envNode = typeof process !== \"undefined\" && process?.env ? \"development\" : undefined;\n if (envNode === \"development\" || envNode === \"test\") {\n return true;\n }\n if (typeof localStorage !== \"undefined\") {\n const flag = localStorage.getItem(\"VIDTREO_DEBUG\");\n if (flag === \"true\") {\n return true;\n }\n }\n return false;\n }\n var isDevelopment = isDebugEnabled();\n var ANSI_COLORS = {\n reset: \"\\x1B[0m\",\n bright: \"\\x1B[1m\",\n dim: \"\\x1B[2m\",\n red: \"\\x1B[31m\",\n green: \"\\x1B[32m\",\n yellow: \"\\x1B[33m\",\n blue: \"\\x1B[34m\",\n magenta: \"\\x1B[35m\",\n cyan: \"\\x1B[36m\",\n white: \"\\x1B[37m\",\n gray: \"\\x1B[90m\"\n };\n function formatMessage(level, message, options) {\n if (!isDevelopment) {\n return \"\";\n }\n const prefix = options?.prefix || `[${level.toUpperCase()}]`;\n const color = options?.color || getDefaultColor(level);\n const colorCode = ANSI_COLORS[color];\n const resetCode = ANSI_COLORS.reset;\n return `${colorCode}${prefix}${resetCode} ${message}`;\n }\n function getDefaultColor(level) {\n switch (level) {\n case \"error\":\n return \"red\";\n case \"warn\":\n return \"yellow\";\n case \"info\":\n return \"cyan\";\n case \"debug\":\n return \"gray\";\n default:\n return \"white\";\n }\n }\n function log(level, message, ...args) {\n if (!isDevelopment) {\n return;\n }\n const formatted = formatMessage(level, message);\n console[level](formatted, ...args);\n }\n var logger = {\n log: (message, ...args) => {\n log(\"log\", message, ...args);\n },\n info: (message, ...args) => {\n log(\"info\", message, ...args);\n },\n warn: (message, ...args) => {\n log(\"warn\", message, ...args);\n },\n error: (message, ...args) => {\n log(\"error\", message, ...args);\n },\n debug: (message, ...args) => {\n log(\"debug\", message, ...args);\n },\n group: (label, color = \"cyan\") => {\n if (!isDevelopment) {\n return;\n }\n const colorCode = ANSI_COLORS[color];\n const resetCode = ANSI_COLORS.reset;\n console.group(`${colorCode}${label}${resetCode}`);\n },\n groupEnd: () => {\n if (!isDevelopment) {\n return;\n }\n console.groupEnd();\n }\n };\n\n // src/core/utils/validation.ts\n function requireNonNull(value, message) {\n if (value === null || value === undefined) {\n throw new Error(message);\n }\n return value;\n }\n function requireDefined(value, message) {\n if (value === undefined) {\n throw new Error(message);\n }\n return value;\n }\n function requireInitialized(value, componentName) {\n if (value === null || value === undefined) {\n throw new Error(`${componentName} is not initialized`);\n }\n return value;\n }\n\n // src/core/processor/worker/audio-state.ts\n var MILLISECONDS_PER_SECOND = 1000;\n\n class AudioState {\n getNowMilliseconds;\n isPaused = false;\n isMuted = false;\n pausedDuration = 0;\n pauseStartedAt = null;\n lastAudioTimestamp = 0;\n isProcessingActive = false;\n constructor(dependencies) {\n this.getNowMilliseconds = dependencies.getNowMilliseconds;\n }\n reset() {\n this.isPaused = false;\n this.isMuted = false;\n this.pausedDuration = 0;\n this.pauseStartedAt = null;\n this.lastAudioTimestamp = 0;\n this.isProcessingActive = false;\n }\n setProcessingActive(isActive) {\n this.isProcessingActive = isActive;\n }\n isActive() {\n return this.isProcessingActive;\n }\n toggleMuted() {\n this.isMuted = !this.isMuted;\n return this.isMuted;\n }\n setMuted(isMuted) {\n this.isMuted = isMuted;\n }\n getIsMuted() {\n return this.isMuted;\n }\n getIsPaused() {\n return this.isPaused;\n }\n getPausedDuration() {\n return this.pausedDuration;\n }\n pause() {\n if (this.isPaused) {\n return false;\n }\n this.pauseStartedAt = this.getNowMilliseconds() / MILLISECONDS_PER_SECOND;\n this.isPaused = true;\n return true;\n }\n resume() {\n if (!this.isPaused) {\n return false;\n }\n const now = this.getNowMilliseconds() / MILLISECONDS_PER_SECOND;\n if (this.pauseStartedAt !== null) {\n this.pausedDuration += now - this.pauseStartedAt;\n }\n this.pauseStartedAt = null;\n this.isPaused = false;\n return true;\n }\n getAudioTimestamp(timestamp) {\n if (timestamp >= this.lastAudioTimestamp) {\n return timestamp;\n }\n return this.lastAudioTimestamp;\n }\n updateLastAudioTimestamp(timestamp, duration) {\n this.lastAudioTimestamp = timestamp + duration;\n }\n getLastAudioTimestamp() {\n return this.lastAudioTimestamp;\n }\n }\n\n // src/core/processor/worker/buffer-tracker.ts\n var BUFFER_UPDATE_INTERVAL_MILLISECONDS = 1000;\n var BYTES_PER_KILOBYTE = 1024;\n var FILE_SIZE_PRECISION_FACTOR = 100;\n var FILE_SIZE_UNITS = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n\n class BufferTracker {\n intervalId = null;\n lastEmittedSize = null;\n dependencies;\n constructor(dependencies) {\n this.dependencies = dependencies;\n }\n start() {\n if (this.intervalId !== null) {\n return;\n }\n this.intervalId = this.dependencies.setInterval(() => {\n const size = this.dependencies.getBufferSize();\n if (size === this.lastEmittedSize) {\n return;\n }\n this.lastEmittedSize = size;\n const formatted = formatFileSize(size);\n this.dependencies.onBufferUpdate(size, formatted);\n }, BUFFER_UPDATE_INTERVAL_MILLISECONDS);\n }\n stop() {\n if (this.intervalId === null) {\n return;\n }\n this.dependencies.clearInterval(this.intervalId);\n this.intervalId = null;\n this.lastEmittedSize = null;\n }\n }\n function formatFileSize(bytes) {\n if (bytes === 0) {\n return `0 ${FILE_SIZE_UNITS[0]}`;\n }\n const base = BYTES_PER_KILOBYTE;\n const index = Math.floor(Math.log(bytes) / Math.log(base));\n const size = Math.round(bytes / base ** index * FILE_SIZE_PRECISION_FACTOR) / FILE_SIZE_PRECISION_FACTOR;\n return `${size} ${FILE_SIZE_UNITS[index]}`;\n }\n\n // src/core/processor/worker/encoder-fallback.ts\n var HARDWARE_ACCELERATION_FALLBACK_CHAIN = [\"prefer-hardware\", \"prefer-software\", \"no-preference\"];\n async function resolveVideoHardwareAcceleration(codec, width, height, bitrate) {\n for (const hardwareAcceleration of HARDWARE_ACCELERATION_FALLBACK_CHAIN) {\n const isSupported = await canEncodeVideo(codec, {\n width,\n height,\n bitrate: Math.round(bitrate),\n hardwareAcceleration\n }).catch(() => false);\n if (isSupported) {\n return hardwareAcceleration;\n }\n }\n return \"no-preference\";\n }\n\n // src/core/processor/worker/encoder-warmup.ts\n var WARMUP_WIDTH = 640;\n var WARMUP_HEIGHT = 360;\n var WARMUP_BITRATE = 1e6;\n var WARMUP_FRAMERATE = 30;\n var I420_BYTES_PER_PIXEL = 1.5;\n function resolveWarmupCodecString(codec) {\n switch (codec) {\n case \"avc\":\n return \"avc1.42001f\";\n case \"hevc\":\n return \"hvc1.1.6.L93.B0\";\n case \"vp9\":\n return \"vp09.00.10.08\";\n case \"av1\":\n return \"av01.0.04M.08\";\n case \"vp8\":\n return \"vp8\";\n default:\n return null;\n }\n }\n function encodeWarmupFrame(encoder, config) {\n const frame = new VideoFrame(new Uint8Array(WARMUP_WIDTH * WARMUP_HEIGHT * I420_BYTES_PER_PIXEL), {\n format: \"I420\",\n codedWidth: WARMUP_WIDTH,\n codedHeight: WARMUP_HEIGHT,\n timestamp: 0\n });\n return Promise.resolve().then(() => {\n encoder.configure(config);\n encoder.encode(frame, { keyFrame: true });\n return encoder.flush();\n }).finally(() => {\n frame.close();\n });\n }\n async function warmUpVideoEncoder(codec, hardwareAcceleration) {\n if (typeof VideoEncoder === \"undefined\" || typeof VideoFrame === \"undefined\") {\n return;\n }\n const codecString = resolveWarmupCodecString(codec);\n if (codecString === null) {\n return;\n }\n const config = {\n codec: codecString,\n width: WARMUP_WIDTH,\n height: WARMUP_HEIGHT,\n bitrate: WARMUP_BITRATE,\n framerate: WARMUP_FRAMERATE,\n hardwareAcceleration,\n latencyMode: \"realtime\"\n };\n const support = await VideoEncoder.isConfigSupported(config).catch(() => null);\n if (support?.supported !== true) {\n return;\n }\n const encoder = new VideoEncoder({\n output: () => {\n return;\n },\n error: () => {\n return;\n }\n });\n await encodeWarmupFrame(encoder, config).finally(() => {\n if (encoder.state !== \"closed\") {\n encoder.close();\n }\n });\n }\n\n // src/core/processor/worker/rotation-utils.ts\n var ROTATION_DEGREES_0 = 0;\n var ROTATION_DEGREES_90 = 90;\n var ROTATION_DEGREES_180 = 180;\n var ROTATION_DEGREES_270 = 270;\n var ROTATION_DEGREES_360 = 360;\n function calculateFrameRotationDegrees(input) {\n if (!input.isMobileDevice) {\n return ROTATION_DEGREES_0;\n }\n const targetWidth = input.targetWidth;\n const targetHeight = input.targetHeight;\n if (typeof targetWidth !== \"number\") {\n return ROTATION_DEGREES_0;\n }\n if (typeof targetHeight !== \"number\") {\n return ROTATION_DEGREES_0;\n }\n const isTargetPortrait = targetHeight > targetWidth;\n const isFramePortrait = input.frameHeight > input.frameWidth;\n if (isTargetPortrait === isFramePortrait) {\n return ROTATION_DEGREES_0;\n }\n const settingsRotation = resolveRotationHint(input.settingsRotation);\n if (settingsRotation !== null) {\n return settingsRotation;\n }\n const orientationRotation = resolveRotationHint(input.orientationAngle);\n if (orientationRotation !== null) {\n return orientationRotation;\n }\n const windowRotation = resolveRotationHint(input.windowOrientation);\n if (windowRotation !== null) {\n return windowRotation;\n }\n return getFallbackRotationDegrees();\n }\n function resolveRotationHint(rotationHint) {\n const normalizedRotation = normalizeRotationDegrees(rotationHint);\n if (normalizedRotation === ROTATION_DEGREES_90) {\n return ROTATION_DEGREES_90;\n }\n if (normalizedRotation === ROTATION_DEGREES_270) {\n return ROTATION_DEGREES_270;\n }\n return null;\n }\n function normalizeRotationDegrees(rotationDegrees) {\n if (typeof rotationDegrees !== \"number\") {\n return null;\n }\n const normalizedValue = (rotationDegrees % ROTATION_DEGREES_360 + ROTATION_DEGREES_360) % ROTATION_DEGREES_360;\n const remainder = normalizedValue % ROTATION_DEGREES_90;\n if (remainder !== 0) {\n return null;\n }\n if (normalizedValue === ROTATION_DEGREES_0) {\n return ROTATION_DEGREES_0;\n }\n if (normalizedValue === ROTATION_DEGREES_90) {\n return ROTATION_DEGREES_90;\n }\n if (normalizedValue === ROTATION_DEGREES_180) {\n return ROTATION_DEGREES_180;\n }\n if (normalizedValue === ROTATION_DEGREES_270) {\n return ROTATION_DEGREES_270;\n }\n return null;\n }\n function getFallbackRotationDegrees() {\n return ROTATION_DEGREES_90;\n }\n\n // src/core/processor/worker/watermark-utils.ts\n function calculateWatermarkTargetSize(videoWidth, imageWidth, imageHeight) {\n const targetWidth = Math.round(videoWidth * 0.07);\n const scaleFactor = targetWidth / imageWidth;\n const targetHeight = Math.round(imageHeight * scaleFactor);\n return { width: targetWidth, height: targetHeight };\n }\n function getWatermarkPosition(options) {\n const { watermarkWidth, watermarkHeight, videoWidth, videoHeight, position } = options;\n const padding = 20;\n switch (position) {\n case \"top-left\":\n return { x: padding, y: padding };\n case \"top-right\":\n return { x: videoWidth - watermarkWidth - padding, y: padding };\n case \"bottom-left\":\n return { x: padding, y: videoHeight - watermarkHeight - padding };\n case \"bottom-right\":\n return {\n x: videoWidth - watermarkWidth - padding,\n y: videoHeight - watermarkHeight - padding\n };\n case \"center\":\n return {\n x: (videoWidth - watermarkWidth) / 2,\n y: (videoHeight - watermarkHeight) / 2\n };\n default:\n return {\n x: videoWidth - watermarkWidth - padding,\n y: videoHeight - watermarkHeight - padding\n };\n }\n }\n\n // src/core/processor/worker/frame-compositor.ts\n var DOUBLE_VALUE = 2;\n var DEFAULT_WATERMARK_OPACITY = 1;\n var DEFAULT_WATERMARK_BASE_WIDTH = 1280;\n var OVERLAY_BACKGROUND_OPACITY = 0.6;\n var OVERLAY_PADDING = 16;\n var OVERLAY_TEXT_COLOR = \"#ffffff\";\n var OVERLAY_FONT_SIZE = 16;\n var OVERLAY_FONT_FAMILY = \"Arial, sans-serif\";\n var OVERLAY_MIN_WIDTH = 200;\n var OVERLAY_MIN_HEIGHT = 50;\n var OVERLAY_COLOR_CHANNEL_VALUE = 20;\n var OVERLAY_BORDER_RADIUS = 50;\n var COMPOSITION_CONTEXT_ERROR_MESSAGE = \"Failed to get composition canvas context\";\n var RECORDER_WORKER_LOG_PREFIX = \"[RecorderWorker]\";\n var ROTATION_RADIANS_90 = Math.PI * ROTATION_DEGREES_90 / ROTATION_DEGREES_180;\n var ROTATION_RADIANS_270 = Math.PI * ROTATION_DEGREES_270 / ROTATION_DEGREES_180;\n\n class FrameCompositor {\n overlayCanvas = null;\n compositionCanvas = null;\n compositionContext = null;\n watermarkCanvas = null;\n watermarkCacheKey = null;\n watermarkPreparationKey = null;\n watermarkPreparationPromise = null;\n watermarkRequestId = 0;\n frameRotationDegrees = null;\n videoSettings = null;\n viewportMetadata = null;\n isMobileDevice = false;\n logger;\n fetchResource;\n createImageBitmap;\n sendDebugLog;\n constructor(dependencies) {\n this.logger = dependencies.logger;\n this.fetchResource = dependencies.fetchResource;\n this.createImageBitmap = dependencies.createImageBitmap;\n this.sendDebugLog = dependencies.sendDebugLog;\n }\n resetRecordingState() {\n this.overlayCanvas = null;\n this.compositionCanvas = null;\n this.compositionContext = null;\n this.frameRotationDegrees = null;\n this.videoSettings = null;\n this.viewportMetadata = null;\n this.isMobileDevice = false;\n }\n reset() {\n this.resetRecordingState();\n this.watermarkCanvas = null;\n this.watermarkCacheKey = null;\n this.watermarkPreparationKey = null;\n this.watermarkPreparationPromise = null;\n this.watermarkRequestId += 1;\n }\n setVideoSettings(settings) {\n this.videoSettings = settings;\n this.frameRotationDegrees = null;\n }\n setViewportMetadata(metadata) {\n this.viewportMetadata = metadata;\n this.frameRotationDegrees = null;\n }\n setIsMobileDevice(isMobileDevice) {\n this.isMobileDevice = isMobileDevice;\n this.frameRotationDegrees = null;\n }\n async prepareWatermark(config) {\n const watermarkConfig = config.watermark;\n if (!watermarkConfig) {\n return;\n }\n const cacheKey = this.buildWatermarkCacheKey(config);\n if (this.watermarkCanvas && this.watermarkCacheKey === cacheKey) {\n return;\n }\n if (this.watermarkPreparationPromise && this.watermarkPreparationKey === cacheKey) {\n await this.watermarkPreparationPromise;\n return;\n }\n this.watermarkCanvas = null;\n this.watermarkCacheKey = null;\n this.watermarkPreparationKey = cacheKey;\n this.watermarkRequestId += 1;\n const requestId = this.watermarkRequestId;\n const preparationPromise = this.loadWatermark(config, cacheKey, requestId);\n this.watermarkPreparationPromise = preparationPromise;\n await preparationPromise.finally(() => {\n if (this.watermarkPreparationPromise !== preparationPromise) {\n return;\n }\n this.watermarkPreparationPromise = null;\n this.watermarkPreparationKey = null;\n });\n }\n buildWatermarkCacheKey(config) {\n const watermarkConfig = requireDefined(config.watermark, \"Watermark config is required\");\n let opacity = DEFAULT_WATERMARK_OPACITY;\n if (typeof watermarkConfig.opacity === \"number\") {\n opacity = watermarkConfig.opacity;\n }\n let videoWidth = DEFAULT_WATERMARK_BASE_WIDTH;\n if (typeof config.width === \"number\") {\n videoWidth = config.width;\n }\n return [watermarkConfig.url, opacity, videoWidth].join(\"|\");\n }\n async loadWatermark(config, cacheKey, requestId) {\n const watermarkConfig = requireDefined(config.watermark, \"Watermark config is required\");\n const url = watermarkConfig.url;\n let opacity = DEFAULT_WATERMARK_OPACITY;\n if (typeof watermarkConfig.opacity === \"number\") {\n opacity = watermarkConfig.opacity;\n }\n const response = await this.fetchResource(url, { mode: \"cors\" }).catch((error) => {\n this.logWatermarkError(url, error);\n return null;\n });\n if (!response) {\n return;\n }\n if (!response.ok) {\n const httpError = new Error(`HTTP error! status: ${response.status}`);\n this.logWatermarkError(url, httpError);\n return;\n }\n const blob = await response.blob().catch((error) => {\n this.logWatermarkError(url, error);\n return null;\n });\n if (!blob) {\n return;\n }\n let isVectorImageFormat = false;\n if (url.toLowerCase().endsWith(\".svg\")) {\n isVectorImageFormat = true;\n }\n if (blob.type === \"image/svg+xml\") {\n isVectorImageFormat = true;\n }\n if (isVectorImageFormat) {\n this.logger.warn(`${RECORDER_WORKER_LOG_PREFIX} Loading SVG watermark. Note: Some environments may not support SVG in createImageBitmap inside workers. If the watermark doesn't appear, consider using a PNG or a Data URL.`);\n }\n const imageBitmap = await this.createImageBitmap(blob).catch((error) => {\n const errorMessage = extractErrorMessage(error);\n const bitmapError = new Error(`Failed to create ImageBitmap from blob (${blob.type}). Errors can happen with SVGs in workers or invalid formats: ${errorMessage}`);\n this.logWatermarkError(url, bitmapError);\n return null;\n });\n if (!imageBitmap) {\n return;\n }\n let videoWidth = DEFAULT_WATERMARK_BASE_WIDTH;\n if (typeof config.width === \"number\") {\n videoWidth = config.width;\n }\n const { width: targetWidth, height: targetHeight } = calculateWatermarkTargetSize(videoWidth, imageBitmap.width, imageBitmap.height);\n const scaleFactor = targetWidth / imageBitmap.width;\n const canvas = new OffscreenCanvas(targetWidth, targetHeight);\n const context = requireNonNull(canvas.getContext(\"2d\", { willReadFrequently: false }), \"Failed to get watermark canvas context\");\n context.globalAlpha = opacity;\n context.drawImage(imageBitmap, 0, 0, targetWidth, targetHeight);\n context.globalAlpha = 1;\n imageBitmap.close();\n if (this.watermarkRequestId !== requestId) {\n return;\n }\n if (this.watermarkPreparationKey !== cacheKey) {\n return;\n }\n this.watermarkCanvas = canvas;\n this.watermarkCacheKey = cacheKey;\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX} Watermark prepared with pre-applied opacity`, {\n width: canvas.width,\n height: canvas.height,\n opacity,\n scaleFactor\n });\n }\n composeFrame(parameters) {\n const compositionPlan = this.getCompositionPlan(parameters);\n if (!compositionPlan.needsComposition) {\n return { frameToProcess: parameters.videoFrame, imageBitmap: null };\n }\n const outputDimensions = this.getOutputDimensions(parameters.videoFrame, compositionPlan.rotationDegrees, parameters.config);\n if (!outputDimensions) {\n return { frameToProcess: parameters.videoFrame, imageBitmap: null };\n }\n const width = outputDimensions.width;\n const height = outputDimensions.height;\n let fit = null;\n const sourceDimensions = this.getFrameDimensions(parameters.videoFrame, compositionPlan.rotationDegrees);\n const dimensionsMismatch = sourceDimensions.width !== width || sourceDimensions.height !== height;\n if (compositionPlan.needsResizing || dimensionsMismatch) {\n const shouldCover = parameters.config.shouldFillCamera === true && parameters.isScreenCapture !== true;\n fit = shouldCover ? calculateCoverFit(sourceDimensions.width, sourceDimensions.height, width, height) : this.calculateContainFit(sourceDimensions.width, sourceDimensions.height, width, height);\n }\n const context = this.ensureCompositionCanvas(width, height);\n context.clearRect(0, 0, width, height);\n if (fit && (fit.drawX > 0 || fit.drawY > 0)) {\n context.fillStyle = \"#000000\";\n context.fillRect(0, 0, width, height);\n }\n this.drawVideoFrame({\n context,\n videoFrame: parameters.videoFrame,\n rotationDegrees: compositionPlan.rotationDegrees,\n width,\n height,\n fit\n });\n this.applyOverlayIfNeeded(context, width, compositionPlan.shouldApplyOverlay, parameters.overlayConfig);\n this.applyWatermarkIfNeeded({\n context,\n videoWidth: width,\n videoHeight: height,\n needsWatermark: compositionPlan.needsWatermark,\n config: parameters.config\n });\n return this.buildCompositionResult(parameters.videoFrame);\n }\n getCompositionPlan(parameters) {\n const rotationDegrees = this.getFrameRotationDegrees(parameters.videoFrame, parameters.config);\n const shouldRotateFrame = rotationDegrees !== ROTATION_DEGREES_0;\n let needsWatermark = false;\n if (parameters.config.watermark && this.watermarkCanvas) {\n needsWatermark = true;\n }\n const needsResizing = this.detectResizingNeed(parameters.videoFrame, rotationDegrees, parameters.config);\n let needsComposition = false;\n if (parameters.shouldApplyOverlay) {\n needsComposition = true;\n }\n if (needsWatermark) {\n needsComposition = true;\n }\n if (shouldRotateFrame) {\n needsComposition = true;\n }\n if (needsResizing) {\n needsComposition = true;\n }\n return {\n rotationDegrees,\n shouldApplyOverlay: parameters.shouldApplyOverlay,\n needsWatermark,\n needsResizing,\n needsComposition\n };\n }\n applyOverlayIfNeeded(context, videoWidth, shouldApplyOverlay, overlayConfig) {\n if (!(shouldApplyOverlay && overlayConfig)) {\n return;\n }\n if (!this.overlayCanvas) {\n this.overlayCanvas = this.createOverlayCanvas(overlayConfig.text);\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX} Overlay canvas created`, {\n overlayWidth: this.overlayCanvas.width,\n overlayHeight: this.overlayCanvas.height\n });\n }\n if (!this.overlayCanvas) {\n return;\n }\n const overlayPosition = this.getOverlayPosition(this.overlayCanvas.width, videoWidth);\n context.drawImage(this.overlayCanvas, overlayPosition.horizontal, overlayPosition.vertical);\n }\n applyWatermarkIfNeeded(options) {\n const { context, videoWidth, videoHeight, needsWatermark, config } = options;\n const watermarkCanvas = this.watermarkCanvas;\n if (!(needsWatermark && watermarkCanvas && config.watermark)) {\n return;\n }\n const watermarkPosition = getWatermarkPosition({\n watermarkWidth: watermarkCanvas.width,\n watermarkHeight: watermarkCanvas.height,\n videoWidth,\n videoHeight,\n position: config.watermark.position\n });\n context.drawImage(watermarkCanvas, watermarkPosition.x, watermarkPosition.y);\n }\n buildCompositionResult(videoFrame) {\n const compositionCanvas = requireNonNull(this.compositionCanvas, \"Composition canvas must exist after ensureCompositionCanvas\");\n const imageBitmap = compositionCanvas.transferToImageBitmap();\n let frameInitialization = {};\n if (typeof videoFrame.timestamp === \"number\") {\n frameInitialization = {\n ...frameInitialization,\n timestamp: videoFrame.timestamp\n };\n }\n if (typeof videoFrame.duration === \"number\") {\n frameInitialization = {\n ...frameInitialization,\n duration: videoFrame.duration\n };\n }\n const frameToProcess = new VideoFrame(imageBitmap, frameInitialization);\n return { frameToProcess, imageBitmap };\n }\n createOverlayCanvas(text) {\n requireDefined(text, \"Overlay text is required\");\n const canvas = new OffscreenCanvas(1, 1);\n const context = requireNonNull(canvas.getContext(\"2d\"), \"Failed to get OffscreenCanvas context\");\n context.font = `${OVERLAY_FONT_SIZE}px ${OVERLAY_FONT_FAMILY}`;\n const textMetrics = context.measureText(text);\n const textWidth = textMetrics.width;\n const textHeight = OVERLAY_FONT_SIZE;\n const overlayWidth = Math.max(OVERLAY_MIN_WIDTH, textWidth + OVERLAY_PADDING * DOUBLE_VALUE);\n const overlayHeight = Math.max(OVERLAY_MIN_HEIGHT, textHeight + OVERLAY_PADDING * DOUBLE_VALUE);\n canvas.width = overlayWidth;\n canvas.height = overlayHeight;\n const redValue = OVERLAY_COLOR_CHANNEL_VALUE;\n const greenValue = OVERLAY_COLOR_CHANNEL_VALUE;\n const blueValue = OVERLAY_COLOR_CHANNEL_VALUE;\n const borderRadius = OVERLAY_BORDER_RADIUS;\n context.fillStyle = `rgba(${redValue}, ${greenValue}, ${blueValue}, ${OVERLAY_BACKGROUND_OPACITY})`;\n context.beginPath();\n context.roundRect(0, 0, overlayWidth, overlayHeight, borderRadius);\n context.fill();\n context.fillStyle = OVERLAY_TEXT_COLOR;\n context.font = `${OVERLAY_FONT_SIZE}px ${OVERLAY_FONT_FAMILY}`;\n context.textBaseline = \"middle\";\n context.textAlign = \"center\";\n const textHorizontalPosition = overlayWidth / DOUBLE_VALUE;\n const textVerticalPosition = overlayHeight / DOUBLE_VALUE;\n context.fillText(text, textHorizontalPosition, textVerticalPosition);\n return canvas;\n }\n getOverlayPosition(overlayWidth, videoWidth) {\n return {\n horizontal: videoWidth - overlayWidth - OVERLAY_PADDING,\n vertical: OVERLAY_PADDING\n };\n }\n ensureCompositionCanvas(width, height) {\n if (!this.compositionCanvas) {\n this.compositionCanvas = new OffscreenCanvas(width, height);\n this.compositionContext = requireNonNull(this.compositionCanvas.getContext(\"2d\", { willReadFrequently: false }), COMPOSITION_CONTEXT_ERROR_MESSAGE);\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX} Composition canvas created`, {\n width,\n height\n });\n return this.compositionContext;\n }\n if (!this.compositionContext) {\n this.compositionContext = requireNonNull(this.compositionCanvas.getContext(\"2d\", { willReadFrequently: false }), COMPOSITION_CONTEXT_ERROR_MESSAGE);\n return this.compositionContext;\n }\n const widthChanged = this.compositionCanvas.width !== width;\n const heightChanged = this.compositionCanvas.height !== height;\n let shouldResize = false;\n if (widthChanged) {\n shouldResize = true;\n }\n if (heightChanged) {\n shouldResize = true;\n }\n if (shouldResize) {\n this.compositionCanvas = new OffscreenCanvas(width, height);\n this.compositionContext = requireNonNull(this.compositionCanvas.getContext(\"2d\", { willReadFrequently: false }), COMPOSITION_CONTEXT_ERROR_MESSAGE);\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX} Composition canvas resized`, {\n width,\n height\n });\n return this.compositionContext;\n }\n return this.compositionContext;\n }\n getFrameRotationDegrees(videoFrame, config) {\n if (this.frameRotationDegrees !== null) {\n return this.frameRotationDegrees;\n }\n const rotation = this.determineFrameRotationDegrees(videoFrame, config);\n this.frameRotationDegrees = rotation;\n const rotationLog = JSON.stringify({\n rotationDegrees: rotation,\n configWidth: config.width,\n configHeight: config.height,\n settingsWidth: this.videoSettings?.width,\n settingsHeight: this.videoSettings?.height,\n facingMode: this.videoSettings?.facingMode,\n frameDisplayWidth: videoFrame.displayWidth,\n frameDisplayHeight: videoFrame.displayHeight\n });\n this.sendDebugLog(`${RECORDER_WORKER_LOG_PREFIX} Rotation decision`, rotationLog);\n return rotation;\n }\n determineFrameRotationDegrees(videoFrame, config) {\n const configWidth = config.width;\n const configHeight = config.height;\n let facingMode;\n let settingsRotation;\n if (this.videoSettings) {\n facingMode = this.videoSettings.facingMode;\n settingsRotation = this.videoSettings.rotation;\n }\n let orientationAngle;\n let windowOrientation;\n if (this.viewportMetadata) {\n orientationAngle = this.viewportMetadata.orientationAngle;\n windowOrientation = this.viewportMetadata.windowOrientation;\n }\n return calculateFrameRotationDegrees({\n isMobileDevice: this.isMobileDevice,\n targetWidth: configWidth,\n targetHeight: configHeight,\n frameWidth: videoFrame.displayWidth,\n frameHeight: videoFrame.displayHeight,\n facingMode,\n settingsRotation,\n orientationAngle,\n windowOrientation\n });\n }\n getFrameDimensions(videoFrame, rotationDegrees) {\n let width = videoFrame.displayWidth;\n let height = videoFrame.displayHeight;\n let shouldSwapDimensions = false;\n if (rotationDegrees === ROTATION_DEGREES_90) {\n shouldSwapDimensions = true;\n }\n if (rotationDegrees === ROTATION_DEGREES_270) {\n shouldSwapDimensions = true;\n }\n if (shouldSwapDimensions) {\n width = videoFrame.displayHeight;\n height = videoFrame.displayWidth;\n }\n return { width, height };\n }\n drawVideoFrame(parameters) {\n const { context, videoFrame, rotationDegrees, width, height, fit } = parameters;\n const sourceWidth = videoFrame.displayWidth;\n const sourceHeight = videoFrame.displayHeight;\n if (sourceWidth <= 0 || sourceHeight <= 0) {\n return;\n }\n context.setTransform(1, 0, 0, 1, 0, 0);\n if (!fit) {\n if (rotationDegrees === ROTATION_DEGREES_90) {\n context.translate(width, 0);\n context.rotate(ROTATION_RADIANS_90);\n context.drawImage(videoFrame, 0, 0, sourceWidth, sourceHeight);\n context.setTransform(1, 0, 0, 1, 0, 0);\n return;\n }\n if (rotationDegrees === ROTATION_DEGREES_270) {\n context.translate(0, height);\n context.rotate(ROTATION_RADIANS_270);\n context.drawImage(videoFrame, 0, 0, sourceWidth, sourceHeight);\n context.setTransform(1, 0, 0, 1, 0, 0);\n return;\n }\n context.drawImage(videoFrame, 0, 0, sourceWidth, sourceHeight);\n return;\n }\n if (rotationDegrees === ROTATION_DEGREES_0) {\n context.drawImage(videoFrame, fit.drawX, fit.drawY, fit.drawWidth, fit.drawHeight);\n return;\n }\n const centerX = fit.drawX + fit.drawWidth / DOUBLE_VALUE;\n const centerY = fit.drawY + fit.drawHeight / DOUBLE_VALUE;\n context.translate(centerX, centerY);\n if (rotationDegrees === ROTATION_DEGREES_90) {\n context.rotate(ROTATION_RADIANS_90);\n } else if (rotationDegrees === ROTATION_DEGREES_270) {\n context.rotate(ROTATION_RADIANS_270);\n } else {\n context.rotate(Math.PI * rotationDegrees / ROTATION_DEGREES_180);\n }\n const isSwappedRotation = rotationDegrees === ROTATION_DEGREES_90 || rotationDegrees === ROTATION_DEGREES_270;\n const scaleX = isSwappedRotation ? fit.drawWidth / sourceHeight : fit.drawWidth / sourceWidth;\n const scaleY = isSwappedRotation ? fit.drawHeight / sourceWidth : fit.drawHeight / sourceHeight;\n context.scale(scaleX, scaleY);\n context.drawImage(videoFrame, -sourceWidth / DOUBLE_VALUE, -sourceHeight / DOUBLE_VALUE, sourceWidth, sourceHeight);\n context.setTransform(1, 0, 0, 1, 0, 0);\n }\n detectResizingNeed(videoFrame, rotationDegrees, config) {\n return detectResizingNeed(videoFrame, rotationDegrees, config);\n }\n getOutputDimensions(videoFrame, rotationDegrees, config) {\n return getOutputDimensions(videoFrame, rotationDegrees, config);\n }\n calculateContainFit(sourceWidth, sourceHeight, targetWidth, targetHeight) {\n return calculateContainFit(sourceWidth, sourceHeight, targetWidth, targetHeight);\n }\n logWatermarkError(url, error) {\n const errorMessage = extractErrorMessage(error);\n this.logger.error(`${RECORDER_WORKER_LOG_PREFIX} Failed to load watermark. This is often caused by CORS if the image is on another domain. Try using a Data URL (base64) or ensure the server has Access-Control-Allow-Origin: *.`, {\n url,\n error: errorMessage\n });\n }\n }\n function calculateContainFit(sourceWidth, sourceHeight, targetWidth, targetHeight) {\n if (sourceWidth <= 0 || sourceHeight <= 0 || targetWidth <= 0 || targetHeight <= 0) {\n return {\n drawX: 0,\n drawY: 0,\n drawWidth: targetWidth,\n drawHeight: targetHeight\n };\n }\n const sourceAspect = sourceWidth / sourceHeight;\n const targetAspect = targetWidth / targetHeight;\n let drawWidth;\n let drawHeight;\n if (sourceAspect > targetAspect) {\n drawWidth = targetWidth;\n drawHeight = targetWidth / sourceAspect;\n } else {\n drawHeight = targetHeight;\n drawWidth = targetHeight * sourceAspect;\n }\n const roundedDrawWidth = Math.round(drawWidth);\n const roundedDrawHeight = Math.round(drawHeight);\n const drawX = (targetWidth - roundedDrawWidth) / 2;\n const drawY = (targetHeight - roundedDrawHeight) / 2;\n return {\n drawX: Math.round(drawX),\n drawY: Math.round(drawY),\n drawWidth: roundedDrawWidth,\n drawHeight: roundedDrawHeight\n };\n }\n function detectResizingNeed(videoFrame, rotationDegrees, config) {\n if (typeof config.width !== \"number\" || typeof config.height !== \"number\") {\n return false;\n }\n if (config.width <= 0 || config.height <= 0) {\n return false;\n }\n let frameWidth = videoFrame.displayWidth;\n let frameHeight = videoFrame.displayHeight;\n if (rotationDegrees === 90 || rotationDegrees === 270) {\n frameWidth = videoFrame.displayHeight;\n frameHeight = videoFrame.displayWidth;\n }\n if (frameWidth === config.width && frameHeight === config.height) {\n return false;\n }\n const sourceAspect = frameWidth / frameHeight;\n const targetAspect = config.width / config.height;\n const aspectRatioTolerance = 0.02;\n if (Math.abs(sourceAspect - targetAspect) > aspectRatioTolerance) {\n return true;\n }\n const sourcePixels = frameWidth * frameHeight;\n const targetPixels = config.width * config.height;\n return sourcePixels > targetPixels;\n }\n function getOutputDimensions(videoFrame, rotationDegrees, config) {\n if (typeof config.width === \"number\" && config.width > 0 && typeof config.height === \"number\" && config.height > 0) {\n return { width: config.width, height: config.height };\n }\n let width = videoFrame.displayWidth;\n let height = videoFrame.displayHeight;\n if (rotationDegrees === 90 || rotationDegrees === 270) {\n width = videoFrame.displayHeight;\n height = videoFrame.displayWidth;\n }\n if (width <= 0 || height <= 0) {\n return null;\n }\n return { width, height };\n }\n function calculateCoverFit(sourceWidth, sourceHeight, targetWidth, targetHeight) {\n if (sourceWidth <= 0 || sourceHeight <= 0 || targetWidth <= 0 || targetHeight <= 0) {\n return {\n drawX: 0,\n drawY: 0,\n drawWidth: targetWidth,\n drawHeight: targetHeight\n };\n }\n const scale = Math.max(targetWidth / sourceWidth, targetHeight / sourceHeight);\n const drawWidth = Math.round(sourceWidth * scale);\n const drawHeight = Math.round(sourceHeight * scale);\n return {\n drawX: Math.round((targetWidth - drawWidth) / 2),\n drawY: Math.round((targetHeight - drawHeight) / 2),\n drawWidth,\n drawHeight\n };\n }\n\n // src/core/processor/worker/recording-integrity.ts\n var EXCESSIVE_FRAME_ERROR_RATIO_THRESHOLD = 0.5;\n var MINIMUM_FRAMES_FOR_RATIO_CHECK = 10;\n function validateRecordingIntegrity(params) {\n const { frameCount, totalFrameErrors } = params;\n if (frameCount === 0) {\n return {\n isValid: false,\n error: {\n code: \"recording.no-video-frames\",\n message: \"Recording completed with zero video frames \u2014 the video track would be empty\"\n }\n };\n }\n const totalAttempts = frameCount + totalFrameErrors;\n if (totalAttempts >= MINIMUM_FRAMES_FOR_RATIO_CHECK && totalFrameErrors / totalAttempts > EXCESSIVE_FRAME_ERROR_RATIO_THRESHOLD) {\n return {\n isValid: false,\n error: {\n code: \"recording.excessive-frame-errors\",\n message: `Recording completed with excessive frame errors: ${totalFrameErrors} failures out of ${totalAttempts} attempts`\n }\n };\n }\n return { isValid: true };\n }\n\n // src/core/processor/worker/stop-finalization.ts\n var STOP_PENDING_WRITES_TIMEOUT_MILLISECONDS = 500;\n var STOP_PENDING_WRITES_POLL_INTERVAL_MILLISECONDS = 10;\n var ERROR_STOP_PENDING_WRITES_TIMEOUT = \"stop.pending-writes-timeout\";\n function createDefaultNowMilliseconds() {\n return () => performance.now();\n }\n function createDefaultWaitMilliseconds() {\n return (milliseconds) => new Promise((resolve) => {\n globalThis.setTimeout(resolve, milliseconds);\n });\n }\n async function waitForPendingWritesToDrain(dependencies) {\n let getNowMilliseconds = dependencies.getNowMilliseconds;\n if (!getNowMilliseconds) {\n getNowMilliseconds = createDefaultNowMilliseconds();\n }\n let waitMilliseconds = dependencies.waitMilliseconds;\n if (!waitMilliseconds) {\n waitMilliseconds = createDefaultWaitMilliseconds();\n }\n let timeoutMilliseconds = dependencies.timeoutMilliseconds;\n if (timeoutMilliseconds === undefined) {\n timeoutMilliseconds = STOP_PENDING_WRITES_TIMEOUT_MILLISECONDS;\n }\n const startedAtMilliseconds = getNowMilliseconds();\n let pendingWriteCount = dependencies.getPendingWriteCount();\n while (pendingWriteCount > 0) {\n const elapsedMilliseconds = getNowMilliseconds() - startedAtMilliseconds;\n if (elapsedMilliseconds >= timeoutMilliseconds) {\n throw new Error(ERROR_STOP_PENDING_WRITES_TIMEOUT);\n }\n await waitMilliseconds(STOP_PENDING_WRITES_POLL_INTERVAL_MILLISECONDS);\n pendingWriteCount = dependencies.getPendingWriteCount();\n }\n }\n\n // src/core/processor/worker/stop-transition.ts\n async function runStopTransition(dependencies) {\n await dependencies.finalizeStopSequence().then(() => dependencies.completeStop()).catch((error) => {\n return dependencies.recoverStopFailure().then(() => {\n throw error;\n });\n }).finally(() => {\n dependencies.clearStoppingFlag();\n });\n }\n\n // src/core/processor/worker/timestamp-manager.ts\n var DEFAULT_FRAME_RATE = 30;\n var MILLISECONDS_PER_SECOND2 = 1000;\n var MICROSECONDS_PER_SECOND = 1e6;\n var MAX_LEAD_SECONDS = 0.05;\n var MAX_LAG_SECONDS = 0.1;\n var MAX_DRIFT_CORRECTION_SECONDS = MAX_LAG_SECONDS;\n var DRIFT_OFFSET_DECAY_FACTOR = 0.5;\n var DRIFT_LOG_FRAME_INTERVAL = 90;\n var RECORDER_WORKER_LOG_PREFIX2 = \"[RecorderWorker]\";\n\n class TimestampManager {\n frameRate = DEFAULT_FRAME_RATE;\n lastVideoTimestamp = 0;\n baseVideoTimestamp = null;\n frameCount = 0;\n forceNextKeyFrame = false;\n driftOffset = 0;\n logger;\n getNowMilliseconds;\n constructor(dependencies) {\n this.logger = dependencies.logger;\n this.getNowMilliseconds = dependencies.getNowMilliseconds;\n }\n reset(frameRate) {\n let resolvedFrameRate = DEFAULT_FRAME_RATE;\n if (typeof frameRate === \"number\" && frameRate > 0) {\n resolvedFrameRate = frameRate;\n }\n this.frameRate = resolvedFrameRate;\n this.lastVideoTimestamp = 0;\n this.baseVideoTimestamp = null;\n this.frameCount = 0;\n this.forceNextKeyFrame = false;\n this.driftOffset = 0;\n }\n setFrameRate(frameRate) {\n this.frameRate = frameRate;\n }\n getFrameRate() {\n return this.frameRate;\n }\n getFrameCount() {\n return this.frameCount;\n }\n getLastVideoTimestamp() {\n return this.lastVideoTimestamp;\n }\n getBaseVideoTimestamp() {\n return this.baseVideoTimestamp;\n }\n calculateVideoFrameTimestamp(parameters) {\n if (this.frameRate <= 0) {\n throw new Error(\"Frame rate must be greater than zero\");\n }\n let rawTimestamp = this.getNowMilliseconds() / MILLISECONDS_PER_SECOND2;\n const hasTimestamp = typeof parameters.videoFrame.timestamp === \"number\" && parameters.videoFrame.timestamp !== null;\n if (hasTimestamp) {\n rawTimestamp = parameters.videoFrame.timestamp / MICROSECONDS_PER_SECOND;\n }\n if (this.baseVideoTimestamp === null) {\n this.baseVideoTimestamp = rawTimestamp;\n const logData = {\n baseVideoTimestamp: this.baseVideoTimestamp,\n recordingStartTime: parameters.recordingStartTime,\n difference: this.baseVideoTimestamp - parameters.recordingStartTime,\n pendingUpdates: parameters.pendingVisibilityUpdatesCount\n };\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX2} baseVideoTimestamp set`, logData);\n parameters.processPendingVisibilityUpdates();\n }\n if (this.baseVideoTimestamp === null) {\n throw new Error(\"Base video timestamp must be set\");\n }\n if (this.frameCount === 0 && this.lastVideoTimestamp > 0) {\n const originalBase = this.baseVideoTimestamp;\n const offset = rawTimestamp - originalBase;\n this.baseVideoTimestamp = rawTimestamp - this.lastVideoTimestamp;\n const frameTimestamp = this.lastVideoTimestamp;\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX2} First frame after source switch`, {\n rawTimestamp,\n originalBase,\n offset,\n adjustedBaseVideoTimestamp: this.baseVideoTimestamp,\n continuationTimestamp: this.lastVideoTimestamp,\n frameTimestamp,\n isScreenCapture: parameters.isScreenCapture\n });\n return frameTimestamp;\n }\n const normalizedTimestamp = rawTimestamp - this.baseVideoTimestamp - parameters.pausedDuration;\n let previousTimestamp = 0;\n if (this.lastVideoTimestamp > 0) {\n previousTimestamp = this.lastVideoTimestamp;\n }\n let frameTimestamp = normalizedTimestamp;\n if (normalizedTimestamp < previousTimestamp) {\n frameTimestamp = previousTimestamp + 1 / this.frameRate;\n }\n if (frameTimestamp < 0) {\n this.logger.warn(`${RECORDER_WORKER_LOG_PREFIX2} Negative frame timestamp detected, clamping to zero`, { frameTimestamp, normalizedTimestamp, previousTimestamp });\n return 0;\n }\n if (this.lastVideoTimestamp === 0) {\n this.lastVideoTimestamp = frameTimestamp;\n }\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX2} Frame timestamp calculation`, {\n rawTimestamp,\n baseVideoTimestamp: this.baseVideoTimestamp,\n normalizedTimestamp,\n previousTimestamp,\n frameTimestamp,\n lastVideoTimestamp: this.lastVideoTimestamp,\n isScreenCapture: parameters.isScreenCapture,\n frameCount: this.frameCount\n });\n return frameTimestamp;\n }\n prepareFrameTiming(parameters) {\n const frameDuration = 1 / this.frameRate;\n let adjustedTimestamp = parameters.frameTimestamp + this.driftOffset;\n const monotonicTimestamp = this.lastVideoTimestamp + frameDuration;\n if (adjustedTimestamp < monotonicTimestamp) {\n adjustedTimestamp = monotonicTimestamp;\n }\n let finalTimestamp = adjustedTimestamp;\n if (finalTimestamp - parameters.lastAudioTimestamp > MAX_LEAD_SECONDS) {\n finalTimestamp = parameters.lastAudioTimestamp + MAX_LEAD_SECONDS;\n }\n if (parameters.lastAudioTimestamp - finalTimestamp > MAX_LAG_SECONDS) {\n finalTimestamp = parameters.lastAudioTimestamp - MAX_LAG_SECONDS;\n }\n const minimumTimestamp = this.lastVideoTimestamp + frameDuration;\n if (finalTimestamp < minimumTimestamp) {\n finalTimestamp = minimumTimestamp;\n }\n let isKeyFrame = false;\n if (this.forceNextKeyFrame) {\n isKeyFrame = true;\n }\n this.driftOffset *= DRIFT_OFFSET_DECAY_FACTOR;\n return {\n finalTimestamp,\n frameDuration,\n isKeyFrame\n };\n }\n commitFrame(parameters) {\n this.frameCount += 1;\n this.lastVideoTimestamp = parameters.finalTimestamp;\n if (parameters.isKeyFrame) {\n this.forceNextKeyFrame = false;\n }\n let shouldLogDrift = false;\n let audioVideoDrift = 0;\n if (this.frameCount % DRIFT_LOG_FRAME_INTERVAL === 0 && parameters.audioProcessingActive) {\n audioVideoDrift = parameters.lastAudioTimestamp - this.lastVideoTimestamp;\n shouldLogDrift = true;\n }\n return {\n shouldLogDrift,\n audioVideoDrift,\n frameCount: this.frameCount,\n lastVideoTimestamp: this.lastVideoTimestamp\n };\n }\n handleSourceSwitch(lastAudioTimestamp) {\n if (this.baseVideoTimestamp === null) {\n throw new Error(\"Base video timestamp must be set for source switch\");\n }\n const minFrameDuration = 1 / this.frameRate;\n const rawDrift = lastAudioTimestamp - this.lastVideoTimestamp;\n this.driftOffset = clampValue(rawDrift, -MAX_DRIFT_CORRECTION_SECONDS, MAX_DRIFT_CORRECTION_SECONDS);\n const continuationTimestamp = Math.max(lastAudioTimestamp, this.lastVideoTimestamp) + minFrameDuration;\n const previousVideoTimestamp = this.lastVideoTimestamp;\n this.lastVideoTimestamp = continuationTimestamp;\n this.frameCount = 0;\n this.forceNextKeyFrame = true;\n return {\n continuationTimestamp,\n previousVideoTimestamp,\n minFrameDuration,\n rawDrift,\n driftOffset: this.driftOffset\n };\n }\n }\n function clampValue(value, min, max) {\n return Math.max(min, Math.min(max, value));\n }\n\n // src/core/processor/worker/types.ts\n var WORKER_MESSAGE_TYPE_PROBE = \"probe\";\n var WORKER_MESSAGE_TYPE_WARMUP = \"warmup\";\n var WORKER_MESSAGE_TYPE_AUDIO_CHUNK = \"audioChunk\";\n var WORKER_RESPONSE_TYPE_PROBE_RESULT = \"probeResult\";\n var WORKER_AUDIO_SAMPLE_FORMAT_F32_PLANAR = \"f32-planar\";\n\n // src/core/processor/worker/video-track-state-error.ts\n var ERROR_RECORDING_VIDEO_TRACK_ENDED = \"recording.video-track-ended\";\n function createVideoTrackEndedError(details = {}) {\n const error = new Error(`Video track ended before recording could start [${ERROR_RECORDING_VIDEO_TRACK_ENDED}]`);\n error.code = ERROR_RECORDING_VIDEO_TRACK_ENDED;\n error.isRecoverable = true;\n error.details = details;\n return error;\n }\n function isVideoTrackEndedError(error) {\n if (!(error instanceof Error)) {\n return false;\n }\n const errorWithCode = error;\n return errorWithCode.code === ERROR_RECORDING_VIDEO_TRACK_ENDED;\n }\n function assertVideoTrackLive(videoTrack, details = {}) {\n if (videoTrack.readyState === \"live\") {\n return;\n }\n throw createVideoTrackEndedError({\n ...details,\n trackId: videoTrack.id,\n readyState: videoTrack.readyState\n });\n }\n\n // src/core/processor/worker/visibility-tracker.ts\n var MILLISECONDS_PER_SECOND3 = 1000;\n var OVERLAY_LOG_FRAME_INTERVAL = 90;\n var RECORDER_WORKER_LOG_PREFIX3 = \"[RecorderWorker]\";\n var INITIAL_INTERVAL_INDEX = 0;\n var INITIAL_LAST_OVERLAY_TIMESTAMP = -1;\n\n class VisibilityTracker {\n hiddenIntervals = [];\n currentHiddenIntervalStart = null;\n pendingVisibilityUpdates = [];\n recordingStartTime = 0;\n isScreenCapture = false;\n intervalCursor = INITIAL_INTERVAL_INDEX;\n lastOverlayTimestamp = INITIAL_LAST_OVERLAY_TIMESTAMP;\n logger;\n constructor(dependencies) {\n this.logger = dependencies.logger;\n }\n reset(recordingStartTime, isScreenCapture) {\n this.hiddenIntervals = [];\n this.currentHiddenIntervalStart = null;\n this.pendingVisibilityUpdates = [];\n this.recordingStartTime = recordingStartTime;\n this.isScreenCapture = isScreenCapture;\n this.intervalCursor = INITIAL_INTERVAL_INDEX;\n this.lastOverlayTimestamp = INITIAL_LAST_OVERLAY_TIMESTAMP;\n }\n setRecordingStartTime(recordingStartTime) {\n this.recordingStartTime = recordingStartTime;\n }\n setIsScreenCapture(isScreenCapture) {\n this.isScreenCapture = isScreenCapture;\n }\n getPendingUpdatesCount() {\n return this.pendingVisibilityUpdates.length;\n }\n shouldApplyOverlay(timestamp, frameCount) {\n if (this.isScreenCapture) {\n return false;\n }\n const shouldApplyCurrentInterval = this.currentHiddenIntervalStart !== null && timestamp >= this.currentHiddenIntervalStart;\n let shouldApply = false;\n if (shouldApplyCurrentInterval) {\n shouldApply = true;\n }\n if (!shouldApplyCurrentInterval) {\n shouldApply = this.shouldApplyCompletedIntervalOverlay(timestamp);\n }\n if (frameCount % OVERLAY_LOG_FRAME_INTERVAL === 0) {\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX3} Overlay check`, {\n timestamp,\n shouldApply,\n frameCount,\n intervalsCount: this.hiddenIntervals.length,\n intervalCursor: this.intervalCursor\n });\n }\n return shouldApply;\n }\n shouldApplyCompletedIntervalOverlay(timestamp) {\n if (this.hiddenIntervals.length === 0) {\n return false;\n }\n if (this.lastOverlayTimestamp !== INITIAL_LAST_OVERLAY_TIMESTAMP && timestamp < this.lastOverlayTimestamp) {\n this.intervalCursor = INITIAL_INTERVAL_INDEX;\n }\n this.lastOverlayTimestamp = timestamp;\n let activeCursor = this.intervalCursor;\n while (activeCursor < this.hiddenIntervals.length) {\n const interval = this.hiddenIntervals[activeCursor];\n if (timestamp < interval.start) {\n break;\n }\n if (timestamp <= interval.end) {\n this.intervalCursor = activeCursor;\n return true;\n }\n activeCursor += 1;\n }\n this.intervalCursor = activeCursor;\n return false;\n }\n handleUpdateVisibility(isHidden, timestamp, hasBaseVideoTimestamp, pausedDuration) {\n if (!hasBaseVideoTimestamp) {\n this.pendingVisibilityUpdates = [\n ...this.pendingVisibilityUpdates,\n { isHidden, timestamp }\n ];\n return;\n }\n this.processVisibilityUpdate(isHidden, timestamp, pausedDuration);\n }\n flushPendingUpdates(pausedDuration) {\n if (this.pendingVisibilityUpdates.length === 0) {\n return;\n }\n for (const update of this.pendingVisibilityUpdates) {\n this.processVisibilityUpdate(update.isHidden, update.timestamp, pausedDuration);\n }\n this.pendingVisibilityUpdates = [];\n }\n processVisibilityUpdate(isHidden, timestamp, pausedDuration) {\n const timestampSeconds = timestamp / MILLISECONDS_PER_SECOND3;\n const normalizedTimestamp = timestampSeconds - this.recordingStartTime - pausedDuration;\n if (isHidden && this.currentHiddenIntervalStart === null) {\n this.currentHiddenIntervalStart = Math.max(0, normalizedTimestamp);\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX3} Started hidden interval`, {\n start: this.currentHiddenIntervalStart\n });\n }\n if (isHidden) {\n return;\n }\n if (this.currentHiddenIntervalStart === null) {\n return;\n }\n const endTimestamp = Math.max(0, normalizedTimestamp);\n if (endTimestamp > this.currentHiddenIntervalStart) {\n const interval = {\n start: this.currentHiddenIntervalStart,\n end: endTimestamp\n };\n this.hiddenIntervals = [...this.hiddenIntervals, interval];\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX3} Completed hidden interval`, {\n interval,\n duration: endTimestamp - this.currentHiddenIntervalStart,\n totalIntervals: this.hiddenIntervals.length\n });\n } else {\n this.logger.warn(`${RECORDER_WORKER_LOG_PREFIX3} Invalid interval (end <= start), discarding`);\n }\n this.currentHiddenIntervalStart = null;\n }\n }\n\n // src/core/processor/worker/recorder-worker.ts\n var CHUNK_SIZE = 16 * 1024 * 1024;\n var DEFAULT_OUTPUT_FORMAT = \"mp4\";\n var MILLISECONDS_PER_SECOND4 = 1000;\n var ERROR_AUDIO_BITRATE_INVALID = \"Audio bitrate must be greater than zero\";\n var ERROR_AUDIO_SAMPLE_RATE_INVALID = \"Audio sample rate must be greater than zero\";\n var ERROR_AUDIO_CHANNELS_INVALID = \"Audio channels must be greater than zero\";\n var ERROR_AUDIO_FRAMES_INVALID = \"Audio frames must be greater than zero\";\n var STEREO_CHANNEL_COUNT = 2;\n var AUDIO_SAMPLE_AVERAGE_SCALE = 0.5;\n var STOP_PENDING_WRITES_TIMEOUT_MILLISECONDS2 = 500;\n var MP4_FAST_START_DISABLED = false;\n var VIDEO_LATENCY_MODE_REALTIME = \"realtime\";\n var VIDEO_CONTENT_HINT_MOTION = \"motion\";\n var VIDEO_HARDWARE_ACCELERATION_PREFERENCE = \"prefer-hardware\";\n var DEFAULT_VIDEO_WIDTH = 1280;\n var DEFAULT_VIDEO_HEIGHT = 720;\n var DEFAULT_VIDEO_BITRATE = 2500000;\n var CONSECUTIVE_FRAME_ERROR_THRESHOLD = 5;\n var EARLY_FRAME_THRESHOLD = 10;\n\n class RecorderWorker {\n output = null;\n videoSource = null;\n audioSource = null;\n videoProcessor = null;\n audioProcessor = null;\n config = null;\n videoProcessingActive = false;\n isStopping = false;\n isFinalized = false;\n bufferTracker;\n audioState;\n timestampManager;\n frameCompositor;\n overlayConfig = null;\n visibilityTracker;\n recordingStartTime = 0;\n isScreenCapture = false;\n totalSize = 0;\n expectedAudioChannels = null;\n expectedAudioSampleRate = null;\n pendingWriteCount = 0;\n resolvedHardwareAcceleration = VIDEO_HARDWARE_ACCELERATION_PREFERENCE;\n hwAccelCacheKey = null;\n hasWarmedVideoEncoder = false;\n consecutiveFrameErrors = 0;\n videoProcessingRunId = 0;\n totalFrameErrors = 0;\n totalFramesProcessed = 0;\n constructor() {\n this.bufferTracker = new BufferTracker({\n getBufferSize: () => this.totalSize,\n onBufferUpdate: (size, formatted) => {\n const response = {\n type: \"bufferUpdate\",\n size,\n formatted\n };\n self.postMessage(response);\n },\n setInterval: (handler, timeout) => self.setInterval(handler, timeout),\n clearInterval: (intervalId) => self.clearInterval(intervalId)\n });\n this.audioState = new AudioState({\n getNowMilliseconds: () => performance.now()\n });\n this.visibilityTracker = new VisibilityTracker({\n logger: {\n debug: (message, data) => logger.debug(message, data),\n warn: (message, data) => logger.warn(message, data)\n }\n });\n this.timestampManager = new TimestampManager({\n logger: {\n debug: (message, data) => logger.debug(message, data),\n warn: (message, data) => logger.warn(message, data)\n },\n getNowMilliseconds: () => performance.now()\n });\n this.frameCompositor = new FrameCompositor({\n logger: {\n debug: (message, data) => logger.debug(message, data),\n warn: (message, data) => logger.warn(message, data),\n error: (message, data) => logger.error(message, data)\n },\n fetchResource: (input, init) => fetch(input, init),\n createImageBitmap: (image) => createImageBitmap(image),\n sendDebugLog: (_message, _payload) => {\n return;\n }\n });\n self.addEventListener(\"message\", this.handleMessage);\n }\n shouldIgnoreMessage() {\n if (this.isStopping) {\n return true;\n }\n if (this.isFinalized) {\n return true;\n }\n return false;\n }\n handleAsyncOperation(operation, context) {\n operation.catch((error) => {\n logger.error(`[RecorderWorker] Error in ${context}:`, error);\n if (isVideoTrackEndedError(error)) {\n let metadata = {};\n if (error.details) {\n metadata = error.details;\n }\n this.sendFatalError({\n code: error.code,\n message: \"Video track ended before recording could start\",\n isRecoverable: true,\n metadata\n });\n return;\n }\n this.sendError(error);\n });\n }\n handleProbe() {\n const response = {\n type: WORKER_RESPONSE_TYPE_PROBE_RESULT,\n hasMediaStreamTrackProcessor: typeof MediaStreamTrackProcessor !== \"undefined\",\n hasVideoFrame: typeof VideoFrame !== \"undefined\",\n hasAudioData: typeof AudioData !== \"undefined\",\n hasOffscreenCanvas: typeof OffscreenCanvas !== \"undefined\",\n hasCreateImageBitmap: typeof createImageBitmap !== \"undefined\",\n hasReadableStream: typeof ReadableStream !== \"undefined\"\n };\n self.postMessage(response);\n }\n handleWarmup(config) {\n const hardwareAccelerationPromise = this.resolveHardwareAcceleration(config).then((result) => {\n this.resolvedHardwareAcceleration = result;\n return this.warmUpVideoEncoderOnce(config.codec, result);\n }).catch((error) => {\n logger.warn(\"[RecorderWorker] Warmup video encoder preparation failed\", { error: extractErrorMessage(error) });\n });\n const watermarkPromise = this.prepareWatermark(config).catch((error) => {\n logger.warn(\"[RecorderWorker] Warmup watermark preparation failed\", {\n error: extractErrorMessage(error)\n });\n });\n this.handleAsyncOperation(Promise.all([hardwareAccelerationPromise, watermarkPromise]).then(() => {\n return;\n }), \"handleWarmup\");\n }\n warmUpVideoEncoderOnce(codec, hardwareAcceleration) {\n if (this.hasWarmedVideoEncoder) {\n return Promise.resolve();\n }\n this.hasWarmedVideoEncoder = true;\n return warmUpVideoEncoder(codec, hardwareAcceleration);\n }\n handleMessage = (event) => {\n const message = event.data;\n logger.debug(\"[RecorderWorker] Received message:\", { type: message.type });\n switch (message.type) {\n case WORKER_MESSAGE_TYPE_PROBE:\n this.handleProbe();\n return;\n case WORKER_MESSAGE_TYPE_WARMUP:\n this.handleWarmup(message.config);\n return;\n case \"start\":\n this.handleStartMessage(message);\n return;\n case \"pause\":\n this.handlePause();\n return;\n case \"resume\":\n this.handleResume();\n return;\n case \"stop\":\n this.handleStopMessage();\n return;\n case \"toggleMute\":\n this.handleToggleMute();\n return;\n case WORKER_MESSAGE_TYPE_AUDIO_CHUNK:\n this.handleAudioChunk(message);\n return;\n case \"switchSource\":\n this.handleSwitchSourceMessage(message);\n return;\n case \"updateFps\":\n this.handleUpdateFps(message.fps);\n return;\n case \"updateVisibility\":\n this.visibilityTracker.handleUpdateVisibility(message.isHidden, message.timestamp, this.timestampManager.getBaseVideoTimestamp() !== null, this.audioState.getPausedDuration());\n return;\n case \"updateSourceType\":\n this.handleUpdateSourceType(message.isScreenCapture);\n return;\n case \"requestStats\":\n this.sendRecordingStats();\n return;\n default:\n this.sendError(new Error(`Unknown message type: ${message.type}`));\n }\n };\n handleStartMessage(message) {\n if (this.shouldIgnoreMessage()) {\n logger.debug(\"[RecorderWorker] start ignored (stopping/finalized)\");\n return;\n }\n let videoTrack = null;\n if (message.videoTrack) {\n videoTrack = message.videoTrack;\n }\n let videoStream = null;\n if (message.videoStream) {\n videoStream = message.videoStream;\n }\n this.handleAsyncOperation(this.handleStart(message, videoTrack, videoStream), \"handleStart\");\n }\n handleStopMessage() {\n if (this.shouldIgnoreMessage()) {\n logger.debug(\"[RecorderWorker] stop ignored (stopping/finalized)\");\n return;\n }\n this.handleAsyncOperation(this.handleStop(), \"handleStop\");\n }\n handleSwitchSourceMessage(message) {\n let videoTrack = null;\n if (message.videoTrack) {\n videoTrack = message.videoTrack;\n }\n let videoStream = null;\n if (message.videoStream) {\n videoStream = message.videoStream;\n }\n this.handleAsyncOperation(this.handleSwitchSource(videoTrack, videoStream), \"handleSwitchSource\");\n }\n validateConfig(config) {\n requireDefined(config, \"Transcode config is required\");\n if (config.width !== undefined && config.width <= 0) {\n throw new Error(\"Video width must be greater than zero\");\n }\n if (config.height !== undefined && config.height <= 0) {\n throw new Error(\"Video height must be greater than zero\");\n }\n if (config.fps !== undefined && config.fps <= 0) {\n throw new Error(\"Frame rate must be greater than zero\");\n }\n if (config.bitrate !== undefined && typeof config.bitrate === \"number\" && config.bitrate <= 0) {\n throw new Error(\"Bitrate must be greater than zero\");\n }\n if (config.keyFrameInterval <= 0) {\n throw new Error(\"Key frame interval must be greater than zero\");\n }\n }\n validateFormat(format) {\n if (format !== \"mp4\") {\n throw new Error(`Format ${format} is not yet supported in worker. Only MP4 is currently supported.`);\n }\n }\n initializeRecordingState(config) {\n this.config = config;\n this.timestampManager.reset(config.fps);\n this.audioState.reset();\n this.expectedAudioChannels = null;\n this.expectedAudioSampleRate = null;\n this.pendingWriteCount = 0;\n this.videoProcessingActive = false;\n this.frameCompositor.resetRecordingState();\n this.recordingStartTime = 0;\n this.visibilityTracker.reset(this.recordingStartTime, this.isScreenCapture);\n }\n setupOverlayConfig(overlayConfig) {\n let nextOverlayConfig = null;\n if (overlayConfig) {\n nextOverlayConfig = {\n enabled: overlayConfig.enabled,\n text: overlayConfig.text\n };\n }\n this.overlayConfig = nextOverlayConfig;\n let recordingStartTimeSeconds = performance.now() / MILLISECONDS_PER_SECOND4;\n if (overlayConfig && overlayConfig.recordingStartTime !== undefined) {\n recordingStartTimeSeconds = overlayConfig.recordingStartTime / MILLISECONDS_PER_SECOND4;\n }\n this.recordingStartTime = recordingStartTimeSeconds;\n this.visibilityTracker.setRecordingStartTime(this.recordingStartTime);\n const logData = {\n hasOverlayConfig: !!this.overlayConfig,\n overlayEnabled: this.overlayConfig?.enabled,\n overlayText: this.overlayConfig?.text,\n recordingStartTime: this.recordingStartTime\n };\n logger.debug(\"[RecorderWorker] Overlay config initialized\", logData);\n }\n createOutput() {\n const writable = new WritableStream({\n write: (chunk) => this.handleOutputChunkWrite(chunk)\n });\n this.output = new Output({\n format: new Mp4OutputFormat({\n fastStart: MP4_FAST_START_DISABLED\n }),\n target: new StreamTarget(writable, {\n chunked: true,\n chunkSize: CHUNK_SIZE\n })\n });\n }\n decrementPendingWriteCount() {\n this.pendingWriteCount -= 1;\n if (this.pendingWriteCount < 0) {\n this.pendingWriteCount = 0;\n }\n }\n handleOutputChunkWrite(chunk) {\n this.pendingWriteCount += 1;\n const writeOperation = Promise.resolve().then(() => {\n this.sendChunk(chunk.data, chunk.position);\n });\n return writeOperation.then(() => {\n this.decrementPendingWriteCount();\n }, (error) => {\n this.decrementPendingWriteCount();\n throw error;\n });\n }\n createVideoSource(config, hardwareAcceleration) {\n const fps = this.timestampManager.getFrameRate();\n const videoSourceOptions = {\n codec: config.codec,\n width: config.width,\n height: config.height,\n sizeChangeBehavior: \"contain\",\n alpha: \"discard\",\n bitrateMode: \"variable\",\n latencyMode: config.latencyMode || VIDEO_LATENCY_MODE_REALTIME,\n contentHint: VIDEO_CONTENT_HINT_MOTION,\n hardwareAcceleration,\n keyFrameInterval: config.keyFrameInterval,\n bitrate: this.deserializeBitrate(config.bitrate)\n };\n this.videoSource = new VideoSampleSource(videoSourceOptions);\n const output = requireNonNull(this.output, \"Output must be initialized before adding video track\");\n const trackOptions = {};\n if (fps !== undefined) {\n trackOptions.frameRate = fps;\n }\n output.addVideoTrack(this.videoSource, trackOptions);\n }\n async resolveHardwareAcceleration(config) {\n if (typeof VideoEncoder === \"undefined\") {\n return VIDEO_HARDWARE_ACCELERATION_PREFERENCE;\n }\n let width = DEFAULT_VIDEO_WIDTH;\n if (config.width !== undefined && config.width > 0) {\n width = config.width;\n }\n let height = DEFAULT_VIDEO_HEIGHT;\n if (config.height !== undefined && config.height > 0) {\n height = config.height;\n }\n let bitrate = DEFAULT_VIDEO_BITRATE;\n if (typeof config.bitrate === \"number\" && config.bitrate > 0) {\n bitrate = config.bitrate;\n }\n const cacheKey = `${config.codec}:${width}:${height}:${config.bitrate}`;\n if (this.hwAccelCacheKey === cacheKey) {\n return this.resolvedHardwareAcceleration;\n }\n const result = await resolveVideoHardwareAcceleration(config.codec, width, height, bitrate);\n this.hwAccelCacheKey = cacheKey;\n return result;\n }\n setupAudioSource(audioConfig, config) {\n if (!audioConfig) {\n return;\n }\n if (!config.audioBitrate) {\n return;\n }\n if (!config.audioCodec) {\n return;\n }\n if (config.audioBitrate <= 0) {\n throw new Error(ERROR_AUDIO_BITRATE_INVALID);\n }\n if (audioConfig.sampleRate <= 0) {\n throw new Error(ERROR_AUDIO_SAMPLE_RATE_INVALID);\n }\n if (audioConfig.numberOfChannels <= 0) {\n throw new Error(ERROR_AUDIO_CHANNELS_INVALID);\n }\n this.expectedAudioChannels = audioConfig.numberOfChannels;\n this.expectedAudioSampleRate = audioConfig.sampleRate;\n this.audioSource = new AudioSampleSource({\n codec: config.audioCodec,\n bitrate: config.audioBitrate,\n bitrateMode: \"variable\"\n });\n const output = requireNonNull(this.output, \"Output must be initialized before adding audio track\");\n output.addAudioTrack(this.audioSource);\n this.audioState.setProcessingActive(true);\n }\n setupAudioStream(audioStream, config) {\n if (!config.audioBitrate) {\n return;\n }\n if (!config.audioCodec) {\n return;\n }\n if (config.audioBitrate <= 0) {\n throw new Error(ERROR_AUDIO_BITRATE_INVALID);\n }\n this.expectedAudioChannels = null;\n this.expectedAudioSampleRate = null;\n this.audioSource = new AudioSampleSource({\n codec: config.audioCodec,\n bitrate: config.audioBitrate,\n bitrateMode: \"variable\"\n });\n const output = requireNonNull(this.output, \"Output must be initialized before adding audio track\");\n output.addAudioTrack(this.audioSource);\n this.audioProcessor = audioStream.getReader();\n this.audioState.setProcessingActive(true);\n this.processAudioData();\n }\n async handleStart(message, videoTrack, videoStream) {\n const audioConfig = message.audioConfig;\n let audioStream = null;\n if (message.audioStream) {\n audioStream = message.audioStream;\n }\n const config = message.config;\n const overlayConfig = message.overlayConfig;\n this.validateConfig(config);\n logger.debug(\"[RecorderWorker] handleStart called\", {\n hasVideoTrack: !!videoTrack,\n hasVideoStream: !!videoStream,\n hasAudioStream: !!audioStream,\n hasAudioConfig: !!audioConfig,\n config: {\n width: config.width,\n height: config.height,\n fps: config.fps,\n bitrate: config.bitrate\n },\n hasOverlayConfig: !!overlayConfig,\n overlayConfig\n });\n this.isStopping = false;\n this.isFinalized = false;\n if (this.output) {\n logger.debug(\"[RecorderWorker] Cleaning up existing output\");\n await this.cleanup();\n }\n this.initializeRecordingState(config);\n const resolvedVideoSettings = message.videoSettings;\n if (resolvedVideoSettings) {\n this.frameCompositor.setVideoSettings(resolvedVideoSettings);\n } else {\n this.frameCompositor.setVideoSettings(null);\n }\n const resolvedViewportMetadata = message.viewportMetadata;\n if (resolvedViewportMetadata) {\n this.frameCompositor.setViewportMetadata(resolvedViewportMetadata);\n } else {\n this.frameCompositor.setViewportMetadata(null);\n }\n this.frameCompositor.setIsMobileDevice(message.isMobileDevice === true);\n this.setupOverlayConfig(overlayConfig);\n let format = config.format;\n if (!format) {\n format = DEFAULT_OUTPUT_FORMAT;\n }\n this.validateFormat(format);\n const hwAccelPromise = this.resolveHardwareAcceleration(config);\n const watermarkPromise = this.prepareWatermark(config);\n this.createOutput();\n this.resolvedHardwareAcceleration = await hwAccelPromise;\n await watermarkPromise;\n this.sendEncoderAcceleration(this.resolvedHardwareAcceleration);\n this.createVideoSource(config, this.resolvedHardwareAcceleration);\n if (videoStream) {\n this.setupVideoProcessingFromStream(videoStream);\n }\n if (!videoStream && videoTrack) {\n this.setupVideoProcessing(videoTrack);\n }\n if (audioStream) {\n this.setupAudioStream(audioStream, config);\n } else {\n this.setupAudioSource(audioConfig, config);\n }\n const output = requireNonNull(this.output, \"Output must be initialized before starting\");\n await output.start();\n this.bufferTracker.start();\n this.sendReady();\n this.sendStateChange(\"recording\");\n }\n prepareWatermark(config) {\n if (!config.watermark) {\n return Promise.resolve();\n }\n return this.frameCompositor.prepareWatermark(config);\n }\n setupVideoProcessing(videoTrack) {\n if (!this.videoSource) {\n return;\n }\n if (typeof MediaStreamTrackProcessor === \"undefined\") {\n throw new Error(\"MediaStreamTrackProcessor is not available in worker\");\n }\n assertVideoTrackLive(videoTrack, { stage: \"worker-stream-create\" });\n const processor = new MediaStreamTrackProcessor({ track: videoTrack });\n this.videoProcessor = processor.readable.getReader();\n this.videoProcessingActive = true;\n this.processVideoFrames();\n }\n setupVideoProcessingFromStream(videoStream) {\n if (!this.videoSource) {\n return;\n }\n this.videoProcessor = videoStream.getReader();\n this.videoProcessingActive = true;\n this.processVideoFrames();\n }\n async handlePausedVideoFrame(videoProcessor) {\n const pausedResult = await videoProcessor.read();\n if (pausedResult.done) {\n return false;\n }\n if (pausedResult.value) {\n pausedResult.value.close();\n }\n return true;\n }\n async drainReader(reader) {\n while (true) {\n const result = await reader.read().catch(() => null);\n if (!result || result.done) {\n return;\n }\n result.value?.close();\n }\n }\n async processVideoFrame(videoFrame) {\n const videoSource = requireInitialized(this.videoSource, \"Video source\");\n const config = requireInitialized(this.config, \"Transcode config\");\n const pausedDuration = this.audioState.getPausedDuration();\n const frameTimestamp = this.timestampManager.calculateVideoFrameTimestamp({\n videoFrame,\n pausedDuration,\n recordingStartTime: this.recordingStartTime,\n pendingVisibilityUpdatesCount: this.visibilityTracker.getPendingUpdatesCount(),\n processPendingVisibilityUpdates: () => {\n this.visibilityTracker.flushPendingUpdates(pausedDuration);\n },\n isScreenCapture: this.isScreenCapture\n });\n const overlayConfig = this.overlayConfig;\n let shouldApplyOverlay = false;\n if (overlayConfig?.enabled && !this.isScreenCapture) {\n shouldApplyOverlay = this.visibilityTracker.shouldApplyOverlay(frameTimestamp, this.timestampManager.getFrameCount());\n }\n const compositionResult = this.frameCompositor.composeFrame({\n videoFrame,\n overlayConfig,\n shouldApplyOverlay,\n config,\n isScreenCapture: this.isScreenCapture\n });\n if (compositionResult.imageBitmap) {\n compositionResult.imageBitmap.close();\n }\n const lastAudioTimestamp = this.audioState.getLastAudioTimestamp();\n const frameTiming = this.timestampManager.prepareFrameTiming({\n frameTimestamp,\n lastAudioTimestamp\n });\n const sample = new VideoSample(compositionResult.frameToProcess, {\n timestamp: frameTiming.finalTimestamp,\n duration: frameTiming.frameDuration\n });\n let videoSampleOptions;\n if (frameTiming.isKeyFrame) {\n videoSampleOptions = { keyFrame: true };\n }\n const addError = await videoSource.add(sample, videoSampleOptions).then(() => null).catch((error) => {\n const errorMessage = extractErrorMessage(error);\n logger.warn(`[RecorderWorker] Failed to add video frame: ${errorMessage}`);\n this.handleVideoFrameAddError();\n return error;\n });\n sample.close();\n if (compositionResult.frameToProcess !== videoFrame) {\n compositionResult.frameToProcess.close();\n }\n if (!addError) {\n this.handleVideoFrameAddSuccess();\n const commitResult = this.timestampManager.commitFrame({\n finalTimestamp: frameTiming.finalTimestamp,\n isKeyFrame: frameTiming.isKeyFrame,\n lastAudioTimestamp,\n audioProcessingActive: this.audioState.isActive(),\n isScreenCapture: this.isScreenCapture\n });\n if (commitResult.shouldLogDrift) {\n logger.debug(\"[RecorderWorker] AV drift metrics\", {\n frameCount: commitResult.frameCount,\n lastAudioTimestamp,\n lastVideoTimestamp: commitResult.lastVideoTimestamp,\n audioVideoDrift: commitResult.audioVideoDrift,\n isScreenCapture: this.isScreenCapture\n });\n }\n }\n videoFrame.close();\n }\n async processVideoFrames() {\n if (!(this.videoProcessor && this.videoSource)) {\n return;\n }\n const ownedProcessor = this.videoProcessor;\n const currentRunId = ++this.videoProcessingRunId;\n while (this.videoProcessingActive && !this.isStopping && currentRunId === this.videoProcessingRunId) {\n if (this.audioState.getIsPaused()) {\n const shouldContinue = await this.handlePausedVideoFrame(ownedProcessor);\n if (!shouldContinue) {\n break;\n }\n continue;\n }\n const result = await ownedProcessor.read();\n if (result.done) {\n break;\n }\n const videoFrame = result.value;\n if (!videoFrame) {\n continue;\n }\n await this.processVideoFrame(videoFrame).catch((error) => {\n const errorMessage = extractErrorMessage(error);\n logger.error(\"[RecorderWorker] Error processing video frame\", errorMessage);\n videoFrame.close();\n });\n }\n if (ownedProcessor === this.videoProcessor) {\n await this.drainReader(ownedProcessor);\n }\n }\n handlePausedAudioData(audioData) {\n audioData.close();\n }\n createAudioBuffer(audioData) {\n const numberOfFrames = audioData.numberOfFrames;\n if (numberOfFrames <= 0) {\n throw new Error(ERROR_AUDIO_FRAMES_INVALID);\n }\n const numberOfChannels = audioData.numberOfChannels;\n if (numberOfChannels <= 0) {\n throw new Error(ERROR_AUDIO_CHANNELS_INVALID);\n }\n const audioBuffer = new Float32Array(numberOfFrames * numberOfChannels);\n let channelIndex = 0;\n while (channelIndex < numberOfChannels) {\n const startIndex = channelIndex * numberOfFrames;\n const endIndex = startIndex + numberOfFrames;\n const channelBuffer = audioBuffer.subarray(startIndex, endIndex);\n audioData.copyTo(channelBuffer, { planeIndex: channelIndex });\n channelIndex += 1;\n }\n return audioBuffer;\n }\n createAudioSample(audioBuffer, audioTimestamp, sampleRate, numberOfChannels) {\n if (sampleRate <= 0) {\n throw new Error(ERROR_AUDIO_SAMPLE_RATE_INVALID);\n }\n if (numberOfChannels <= 0) {\n throw new Error(ERROR_AUDIO_CHANNELS_INVALID);\n }\n let bufferToWrite = audioBuffer;\n if (this.audioState.getIsMuted()) {\n bufferToWrite = new Float32Array(audioBuffer.length);\n }\n return new AudioSample({\n data: bufferToWrite,\n format: WORKER_AUDIO_SAMPLE_FORMAT_F32_PLANAR,\n numberOfChannels,\n sampleRate,\n timestamp: audioTimestamp\n });\n }\n async processAudioSample(audioData, audioSample, audioTimestamp, duration) {\n const audioSource = requireInitialized(this.audioSource, \"Audio source\");\n await audioSource.add(audioSample).catch((error) => {\n const errorMessage = extractErrorMessage(error);\n logger.warn(`[RecorderWorker] Failed to add audio sample: ${errorMessage}`);\n });\n this.audioState.updateLastAudioTimestamp(audioTimestamp, duration);\n audioSample.close();\n audioData.close();\n }\n async processAudioData() {\n if (!(this.audioProcessor && this.audioSource)) {\n return;\n }\n while (this.audioState.isActive() && !this.isStopping) {\n const result = await this.audioProcessor.read();\n if (result.done) {\n this.audioState.setProcessingActive(false);\n break;\n }\n const audioData = result.value;\n if (this.shouldSkipAudioData(audioData)) {\n continue;\n }\n const audioFormat = this.getAudioDataFormat(audioData);\n if (!audioFormat) {\n continue;\n }\n const audioBuffer = this.createAudioBuffer(audioData);\n const normalized = this.normalizeAudioBufferForFormat(audioBuffer, audioFormat);\n const duration = audioFormat.numberOfFrames / audioFormat.sampleRate;\n const audioTimestamp = this.audioState.getLastAudioTimestamp();\n const audioSample = this.createAudioSample(normalized.buffer, audioTimestamp, audioFormat.sampleRate, normalized.numberOfChannels);\n await this.processAudioSample(audioData, audioSample, audioTimestamp, duration);\n }\n }\n handleAudioChunk(message) {\n this.handleAsyncOperation(this.processAudioChunk(message), \"handleAudioChunk\");\n }\n async processAudioChunk(message) {\n if (this.shouldIgnoreMessage()) {\n return;\n }\n if (!this.audioSource) {\n return;\n }\n if (!this.audioState.isActive()) {\n return;\n }\n if (this.audioState.getIsPaused()) {\n return;\n }\n if (message.frames <= 0) {\n throw new Error(ERROR_AUDIO_FRAMES_INVALID);\n }\n if (message.sampleRate <= 0) {\n throw new Error(ERROR_AUDIO_SAMPLE_RATE_INVALID);\n }\n if (message.numberOfChannels <= 0) {\n throw new Error(ERROR_AUDIO_CHANNELS_INVALID);\n }\n this.setExpectedAudioFormat(message.sampleRate, message.numberOfChannels);\n if (this.expectedAudioSampleRate !== null && message.sampleRate !== this.expectedAudioSampleRate) {\n this.sendError(new Error(\"Audio sample rate changed during recording\"));\n return;\n }\n if (this.expectedAudioChannels !== null && message.numberOfChannels !== this.expectedAudioChannels) {\n this.sendError(new Error(\"Audio channel count changed during recording\"));\n return;\n }\n let audioBuffer = message.data;\n const numberOfChannels = message.numberOfChannels;\n const expectedSamples = message.frames * numberOfChannels;\n if (audioBuffer.length < expectedSamples) {\n throw new Error(\"Audio buffer length is shorter than expected\");\n }\n const sampleRate = message.sampleRate;\n const duration = message.frames / sampleRate;\n const audioTimestamp = this.audioState.getAudioTimestamp(message.timestamp);\n if (this.audioState.getIsMuted()) {\n audioBuffer = new Float32Array(audioBuffer.length);\n }\n const audioSample = new AudioSample({\n data: audioBuffer,\n format: WORKER_AUDIO_SAMPLE_FORMAT_F32_PLANAR,\n numberOfChannels,\n sampleRate,\n timestamp: audioTimestamp\n });\n const audioSource = requireInitialized(this.audioSource, \"Audio source\");\n const addError = await audioSource.add(audioSample).then(() => null).catch((error) => {\n return new Error(`Failed to add audio sample: ${extractErrorMessage(error)}`);\n });\n if (addError) {\n this.sendError(addError);\n audioSample.close();\n return;\n }\n this.audioState.updateLastAudioTimestamp(audioTimestamp, duration);\n const lastAudioTimestamp = this.audioState.getLastAudioTimestamp();\n logger.debug(\"[RecorderWorker] Audio sample processed\", {\n lastAudioTimestamp,\n duration,\n sampleRate,\n numberOfFrames: message.frames\n });\n audioSample.close();\n }\n shouldSkipAudioData(audioData) {\n if (!audioData) {\n return true;\n }\n if (this.audioState.getIsPaused()) {\n this.handlePausedAudioData(audioData);\n return true;\n }\n return false;\n }\n getAudioDataFormat(audioData) {\n const sampleRate = audioData.sampleRate;\n if (sampleRate <= 0) {\n audioData.close();\n throw new Error(ERROR_AUDIO_SAMPLE_RATE_INVALID);\n }\n const numberOfFrames = audioData.numberOfFrames;\n const numberOfChannels = audioData.numberOfChannels;\n this.setExpectedAudioFormat(sampleRate, numberOfChannels);\n if (this.expectedAudioSampleRate !== null && sampleRate !== this.expectedAudioSampleRate) {\n logger.warn(\"[RecorderWorker] Audio sample rate changed\", {\n expectedSampleRate: this.expectedAudioSampleRate,\n receivedSampleRate: sampleRate\n });\n audioData.close();\n return null;\n }\n return {\n sampleRate,\n numberOfFrames,\n numberOfChannels\n };\n }\n normalizeAudioBufferForFormat(audioBuffer, audioFormat) {\n let bufferToWrite = audioBuffer;\n let channelsToWrite = audioFormat.numberOfChannels;\n if (this.expectedAudioChannels !== null) {\n const normalized = this.normalizeAudioBuffer(audioBuffer, audioFormat.numberOfFrames, audioFormat.numberOfChannels, this.expectedAudioChannels);\n bufferToWrite = normalized.buffer;\n channelsToWrite = normalized.numberOfChannels;\n }\n return {\n buffer: bufferToWrite,\n numberOfChannels: channelsToWrite\n };\n }\n handlePause() {\n if (!this.audioState.pause()) {\n return;\n }\n this.sendStateChange(\"paused\");\n }\n handleResume() {\n if (!this.audioState.resume()) {\n return;\n }\n this.sendStateChange(\"recording\");\n }\n handleStop() {\n if (this.isStopping) {\n logger.debug(\"[RecorderWorker] handleStop ignored (stopping/finalized)\");\n return Promise.resolve();\n }\n if (this.isFinalized) {\n logger.debug(\"[RecorderWorker] handleStop ignored (stopping/finalized)\");\n return Promise.resolve();\n }\n this.isStopping = true;\n this.isFinalized = true;\n this.videoProcessingActive = false;\n this.audioState.setProcessingActive(false);\n return runStopTransition({\n finalizeStopSequence: () => this.finalizeStopSequence(),\n completeStop: () => this.completeStop(),\n recoverStopFailure: () => {\n if (this.isFinalized) {\n this.resetStopStateAfterFailure();\n }\n return this.cleanup().catch((cleanupError) => {\n logger.error(\"[RecorderWorker] Stop failure cleanup failed\", {\n error: extractErrorMessage(cleanupError)\n });\n });\n },\n clearStoppingFlag: () => {\n this.isStopping = false;\n }\n });\n }\n async completeStop() {\n await this.cleanup();\n this.sendStateChange(\"stopped\");\n }\n async finalizeStopSequence() {\n if (this.videoProcessor) {\n await this.videoProcessor.cancel();\n this.videoProcessor = null;\n }\n if (this.audioProcessor) {\n await this.audioProcessor.cancel();\n this.audioProcessor = null;\n }\n if (this.output) {\n await this.output.finalize();\n }\n await waitForPendingWritesToDrain({\n getPendingWriteCount: () => this.pendingWriteCount,\n timeoutMilliseconds: STOP_PENDING_WRITES_TIMEOUT_MILLISECONDS2\n });\n this.sendRecordingStats();\n this.logEarlyFrameWarning();\n const integrity = validateRecordingIntegrity({\n frameCount: this.timestampManager.getFrameCount(),\n totalFrameErrors: this.totalFrameErrors\n });\n if (!integrity.isValid && integrity.error) {\n this.sendFatalError({\n code: integrity.error.code,\n message: integrity.error.message,\n isRecoverable: true,\n metadata: {\n frameCount: this.timestampManager.getFrameCount(),\n totalErrors: this.totalFrameErrors\n }\n });\n }\n }\n logEarlyFrameWarning() {\n const frameCount = this.timestampManager.getFrameCount();\n if (frameCount <= EARLY_FRAME_THRESHOLD && this.totalFrameErrors > 0) {\n logger.warn(\"[RecorderWorker] Recording finished with early frame errors\", {\n frameCount,\n totalFrameErrors: this.totalFrameErrors\n });\n }\n }\n handleVideoFrameAddError() {\n this.consecutiveFrameErrors += 1;\n this.totalFrameErrors += 1;\n if (this.consecutiveFrameErrors < CONSECUTIVE_FRAME_ERROR_THRESHOLD) {\n return;\n }\n this.videoProcessingActive = false;\n this.audioState.setProcessingActive(false);\n this.sendFatalError({\n code: \"recording.frame-loss\",\n message: `Recording aborted: ${CONSECUTIVE_FRAME_ERROR_THRESHOLD} consecutive video frame errors`,\n isRecoverable: true,\n metadata: {\n consecutiveErrors: this.consecutiveFrameErrors,\n totalErrors: this.totalFrameErrors,\n totalFrames: this.totalFramesProcessed\n }\n });\n }\n handleVideoFrameAddSuccess() {\n this.consecutiveFrameErrors = 0;\n this.totalFramesProcessed += 1;\n }\n resetStopStateAfterFailure() {\n this.isFinalized = false;\n this.videoProcessingActive = false;\n this.audioState.setProcessingActive(false);\n }\n handleToggleMute() {\n this.audioState.toggleMuted();\n }\n handleUpdateFps(fps) {\n if (fps <= 0) {\n throw new Error(\"Frame rate must be greater than zero\");\n }\n const previousFps = this.timestampManager.getFrameRate();\n logger.debug(\"[RecorderWorker] Updating FPS\", {\n fps,\n previousFps\n });\n this.timestampManager.setFrameRate(fps);\n if (this.config) {\n this.config.fps = fps;\n }\n }\n handleUpdateSourceType(isScreenCapture) {\n logger.debug(\"[RecorderWorker] Updating source type\", {\n isScreenCapture,\n previousIsScreenCapture: this.isScreenCapture\n });\n this.isScreenCapture = isScreenCapture;\n this.visibilityTracker.setIsScreenCapture(isScreenCapture);\n }\n async handleSwitchSource(videoTrack, videoStream) {\n if (!(videoTrack || videoStream)) {\n throw new Error(\"Video track or stream is required\");\n }\n const frameRate = this.timestampManager.getFrameRate();\n requireDefined(frameRate, \"Frame rate must be set\");\n if (frameRate <= 0) {\n throw new Error(\"Frame rate must be greater than zero\");\n }\n if (this.videoProcessor) {\n this.videoProcessingActive = false;\n const videoProcessor = this.videoProcessor;\n await videoProcessor.cancel();\n await this.drainReader(videoProcessor);\n this.videoProcessor = null;\n }\n const lastAudioTimestamp = this.audioState.getLastAudioTimestamp();\n const baseVideoTimestamp = this.timestampManager.getBaseVideoTimestamp();\n requireNonNull(baseVideoTimestamp, \"Base video timestamp must be set for source switch\");\n const switchResult = this.timestampManager.handleSourceSwitch(lastAudioTimestamp);\n logger.debug(\"[RecorderWorker] handleSwitchSource - preserving baseVideoTimestamp\", {\n continuationTimestamp: switchResult.continuationTimestamp,\n lastVideoTimestamp: this.timestampManager.getLastVideoTimestamp(),\n frameRate,\n isScreenCapture: this.isScreenCapture,\n baseVideoTimestamp,\n recordingStartTime: this.recordingStartTime,\n lastAudioTimestamp,\n previousVideoTimestamp: switchResult.previousVideoTimestamp,\n minFrameDuration: switchResult.minFrameDuration,\n rawDrift: switchResult.rawDrift,\n driftOffset: switchResult.driftOffset\n });\n if (videoStream) {\n this.setupVideoProcessingFromStream(videoStream);\n return;\n }\n if (videoTrack) {\n this.setupVideoProcessing(videoTrack);\n }\n }\n async cleanup() {\n this.bufferTracker.stop();\n this.videoProcessingActive = false;\n this.audioState.setProcessingActive(false);\n if (this.videoProcessor) {\n const videoProcessor = this.videoProcessor;\n await videoProcessor.cancel();\n await this.drainReader(videoProcessor);\n this.videoProcessor = null;\n }\n if (this.audioProcessor) {\n const audioProcessor = this.audioProcessor;\n await audioProcessor.cancel();\n await this.drainReader(audioProcessor);\n this.audioProcessor = null;\n }\n const videoSource = this.videoSource;\n if (videoSource && !this.isFinalized) {\n videoSource.close();\n }\n if (videoSource) {\n this.videoSource = null;\n }\n const audioSource = this.audioSource;\n if (audioSource && !this.isFinalized) {\n audioSource.close();\n }\n if (audioSource) {\n this.audioSource = null;\n }\n const output = this.output;\n if (output && !this.isFinalized) {\n await output.cancel().catch((error) => {\n logger.warn(\"[RecorderWorker] cancel failed (ignored, possibly finalized)\", error);\n });\n this.isFinalized = true;\n }\n if (output) {\n this.output = null;\n }\n this.timestampManager.reset(undefined);\n this.totalSize = 0;\n this.audioState.reset();\n this.frameCompositor.reset();\n this.overlayConfig = null;\n this.recordingStartTime = 0;\n this.isScreenCapture = false;\n this.expectedAudioChannels = null;\n this.expectedAudioSampleRate = null;\n this.pendingWriteCount = 0;\n this.consecutiveFrameErrors = 0;\n this.totalFrameErrors = 0;\n this.totalFramesProcessed = 0;\n this.visibilityTracker.reset(this.recordingStartTime, this.isScreenCapture);\n }\n setExpectedAudioFormat(sampleRate, numberOfChannels) {\n if (this.expectedAudioSampleRate === null) {\n this.expectedAudioSampleRate = sampleRate;\n }\n if (this.expectedAudioChannels === null) {\n this.expectedAudioChannels = numberOfChannels;\n }\n }\n normalizeAudioBuffer(audioBuffer, frames, actualChannels, expectedChannels) {\n if (actualChannels === expectedChannels) {\n return { buffer: audioBuffer, numberOfChannels: actualChannels };\n }\n if (actualChannels === 1 && expectedChannels === STEREO_CHANNEL_COUNT) {\n const expandedBuffer = new Float32Array(frames * STEREO_CHANNEL_COUNT);\n expandedBuffer.set(audioBuffer, 0);\n expandedBuffer.set(audioBuffer, frames);\n return {\n buffer: expandedBuffer,\n numberOfChannels: STEREO_CHANNEL_COUNT\n };\n }\n if (actualChannels === STEREO_CHANNEL_COUNT && expectedChannels === 1) {\n const mixedBuffer = new Float32Array(frames);\n let frameIndex = 0;\n while (frameIndex < frames) {\n const leftSample = audioBuffer[frameIndex];\n const rightSample = audioBuffer[frameIndex + frames];\n mixedBuffer[frameIndex] = (leftSample + rightSample) * AUDIO_SAMPLE_AVERAGE_SCALE;\n frameIndex += 1;\n }\n return { buffer: mixedBuffer, numberOfChannels: 1 };\n }\n logger.warn(\"[RecorderWorker] Audio channel mismatch\", {\n expectedChannels,\n receivedChannels: actualChannels\n });\n return { buffer: audioBuffer, numberOfChannels: actualChannels };\n }\n sendReady() {\n const response = { type: \"ready\" };\n self.postMessage(response);\n }\n sendError(error) {\n const errorMessage = extractErrorMessage(error);\n const response = {\n type: \"error\",\n error: errorMessage\n };\n self.postMessage(response);\n }\n sendChunk(data, position) {\n this.totalSize = Math.max(this.totalSize, position + data.length);\n const response = {\n type: \"chunk\",\n data,\n position\n };\n const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);\n self.postMessage(response, [buffer]);\n }\n sendStateChange(state) {\n const response = {\n type: \"stateChange\",\n state\n };\n self.postMessage(response);\n }\n sendEncoderAcceleration(acceleration) {\n const response = {\n type: \"encoderAcceleration\",\n acceleration\n };\n self.postMessage(response);\n }\n sendFatalError(payload) {\n self.postMessage({\n type: \"fatalError\",\n code: payload.code,\n message: payload.message,\n isRecoverable: payload.isRecoverable,\n metadata: payload.metadata\n });\n }\n sendRecordingStats() {\n self.postMessage({\n type: \"recordingStats\",\n videoFrameCount: this.timestampManager.getFrameCount(),\n totalFrameErrors: this.totalFrameErrors,\n totalFramesProcessed: this.totalFramesProcessed\n });\n }\n deserializeBitrate(bitrate) {\n if (typeof bitrate === \"number\") {\n return bitrate;\n }\n if (bitrate === \"low\") {\n return QUALITY_LOW;\n }\n if (bitrate === \"medium\") {\n return QUALITY_MEDIUM;\n }\n if (bitrate === \"high\") {\n return QUALITY_HIGH;\n }\n if (bitrate === \"very-high\") {\n return QUALITY_VERY_HIGH;\n }\n return QUALITY_HIGH;\n }\n }\n function shouldAutoInitializeRecorderWorker() {\n if (typeof self === \"undefined\") {\n return false;\n }\n if (!(\"importScripts\" in self)) {\n return false;\n }\n return typeof self.postMessage === \"function\";\n }\n function initializeRecorderWorker() {\n return new RecorderWorker;\n }\n if (shouldAutoInitializeRecorderWorker()) {\n initializeRecorderWorker();\n }\n})();\n";
|
|
2585
|
+
export declare const workerCode = "(() => {\n var __create = Object.create;\n var __getProtoOf = Object.getPrototypeOf;\n var __defProp = Object.defineProperty;\n var __getOwnPropNames = Object.getOwnPropertyNames;\n var __getOwnPropDesc = Object.getOwnPropertyDescriptor;\n var __hasOwnProp = Object.prototype.hasOwnProperty;\n function __accessProp(key) {\n return this[key];\n }\n var __toESMCache_node;\n var __toESMCache_esm;\n var __toESM = (mod, isNodeMode, target) => {\n var canCache = mod != null && typeof mod === \"object\";\n if (canCache) {\n var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;\n var cached = cache.get(mod);\n if (cached)\n return cached;\n }\n target = mod != null ? __create(__getProtoOf(mod)) : {};\n const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, \"default\") ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target;\n if (mod && typeof mod === \"object\" || typeof mod === \"function\") {\n for (let key of __getOwnPropNames(mod))\n if (!__hasOwnProp.call(to, key))\n __defProp(to, key, {\n get: __accessProp.bind(mod, key),\n enumerable: true\n });\n }\n if (canCache)\n cache.set(mod, to);\n return to;\n };\n var __toCommonJS = (from) => {\n var entry = (__moduleCache ??= new WeakMap).get(from), desc;\n if (entry)\n return entry;\n entry = __defProp({}, \"__esModule\", { value: true });\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (var key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(entry, key))\n __defProp(entry, key, {\n get: __accessProp.bind(from, key),\n enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable\n });\n }\n __moduleCache.set(from, entry);\n return entry;\n };\n var __moduleCache;\n var __returnValue = (v) => v;\n function __exportSetter(name, newValue) {\n this[name] = __returnValue.bind(null, newValue);\n }\n var __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, {\n get: all[name],\n enumerable: true,\n configurable: true,\n set: __exportSetter.bind(all, name)\n });\n };\n var __require = /* @__PURE__ */ ((x) => typeof require !== \"undefined\" ? require : typeof Proxy !== \"undefined\" ? new Proxy(x, {\n get: (a, b) => (typeof require !== \"undefined\" ? require : a)[b]\n }) : x)(function(x) {\n if (typeof require !== \"undefined\")\n return require.apply(this, arguments);\n throw Error('Dynamic require of \"' + x + '\" is not supported');\n });\n\n // src/core/processor/worker/recorder-worker.ts\n var exports_recorder_worker = {};\n __export(exports_recorder_worker, {\n RecorderWorker: () => RecorderWorker,\n initializeRecorderWorker: () => initializeRecorderWorker\n });\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/misc.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n function assert(x) {\n if (!x) {\n throw new Error(\"Assertion failed.\");\n }\n }\n var DEG_TO_RAD = Math.PI / 180;\n var RAD_TO_DEG = 180 / Math.PI;\n var normalizeRotation = (rotation) => {\n const mappedRotation = (rotation % 360 + 360) % 360;\n if (mappedRotation === 0 || mappedRotation === 90 || mappedRotation === 180 || mappedRotation === 270) {\n return mappedRotation;\n } else {\n throw new Error(`Invalid rotation ${rotation}.`);\n }\n };\n var IDENTITY_MATRIX = [1, 0, 0, 0, 1, 0, 0, 0, 1];\n var centeredTransformationMatrix = (linear, width, height) => {\n const [a, b, , c, d] = linear;\n const transformedWidth = Math.abs(a) * width + Math.abs(c) * height;\n const transformedHeight = Math.abs(b) * width + Math.abs(d) * height;\n return multiplyMatrices(multiplyMatrices(translationMatrix(-width / 2, -height / 2), linear), translationMatrix(transformedWidth / 2, transformedHeight / 2));\n };\n var extractRotationFromMatrix = (matrix) => {\n const [a, b] = matrix;\n const radians = Math.atan2(b, matrixIsFlipped(matrix) ? -a : a);\n return normalizeRotation(roundToMultiple(radians * RAD_TO_DEG, 90));\n };\n var matrixIsFlipped = (matrix) => {\n const [a, b, , c, d] = matrix;\n const det = a * d - b * c;\n return det < 0;\n };\n var rotationMatrix = (rotationInDegrees) => {\n const theta = rotationInDegrees * DEG_TO_RAD;\n const cosTheta = Math.round(Math.cos(theta));\n const sinTheta = Math.round(Math.sin(theta));\n return [\n cosTheta,\n sinTheta,\n 0,\n -sinTheta,\n cosTheta,\n 0,\n 0,\n 0,\n 1\n ];\n };\n var translationMatrix = (x, y) => {\n return [\n 1,\n 0,\n 0,\n 0,\n 1,\n 0,\n x,\n y,\n 1\n ];\n };\n var scaleMatrix = (x, y) => {\n return [\n x,\n 0,\n 0,\n 0,\n y,\n 0,\n 0,\n 0,\n 1\n ];\n };\n var multiplyMatrices = (a, b) => {\n const result = new Array(9);\n for (let i = 0;i < 3; i++) {\n for (let j = 0;j < 3; j++) {\n result[3 * i + j] = a[3 * i] * b[j] + a[3 * i + 1] * b[3 + j] + a[3 * i + 2] * b[6 + j];\n }\n }\n return result;\n };\n var composeRotationAndFlip = (rotation1, flip1, rotation2, flip2) => {\n return {\n rotation: normalizeRotation(rotation1 + (flip1 ? -rotation2 : rotation2)),\n flip: flip1 !== flip2\n };\n };\n var last = (arr) => {\n return arr && arr[arr.length - 1];\n };\n var isU32 = (value) => {\n return value >= 0 && value < 2 ** 32;\n };\n var isI32 = (value) => {\n return value >= -(2 ** 31) && value < 2 ** 31;\n };\n var readExpGolomb = (bitstream) => {\n let leadingZeroBits = 0;\n while (bitstream.readBits(1) === 0 && leadingZeroBits < 32) {\n leadingZeroBits++;\n }\n if (leadingZeroBits >= 32) {\n throw new Error(\"Invalid exponential-Golomb code.\");\n }\n const result = (1 << leadingZeroBits) - 1 + bitstream.readBits(leadingZeroBits);\n return result;\n };\n var readSignedExpGolomb = (bitstream) => {\n const codeNum = readExpGolomb(bitstream);\n return (codeNum & 1) === 0 ? -(codeNum >> 1) : codeNum + 1 >> 1;\n };\n var writeBits = (bytes, start, end, value) => {\n for (let i = start;i < end; i++) {\n const byteIndex = Math.floor(i / 8);\n let byte = bytes[byteIndex];\n const bitIndex = 7 - (i & 7);\n byte &= ~(1 << bitIndex);\n byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex;\n bytes[byteIndex] = byte;\n }\n };\n var toUint8Array = (source) => {\n if (source.constructor === Uint8Array) {\n return source;\n } else if (ArrayBuffer.isView(source)) {\n return new Uint8Array(source.buffer, source.byteOffset, source.byteLength);\n } else {\n return new Uint8Array(source);\n }\n };\n var toDataView = (source) => {\n if (source.constructor === DataView) {\n return source;\n } else if (ArrayBuffer.isView(source)) {\n return new DataView(source.buffer, source.byteOffset, source.byteLength);\n } else {\n return new DataView(source);\n }\n };\n var TextEncoder = typeof globalThis.TextEncoder !== \"undefined\" ? globalThis.TextEncoder : class TextEncoder {\n constructor() {\n this.encoding = \"utf-8\";\n }\n encode(input = \"\") {\n const bytes = new Uint8Array(3 * input.length);\n let n = 0;\n for (let i = 0;i < input.length; i++) {\n let c = input.charCodeAt(i);\n if (c < 128) {\n bytes[n++] = c;\n } else if (c < 2048) {\n bytes[n++] = 192 | c >> 6;\n bytes[n++] = 128 | c & 63;\n } else if (c < 55296 || c > 57343) {\n bytes[n++] = 224 | c >> 12;\n bytes[n++] = 128 | c >> 6 & 63;\n bytes[n++] = 128 | c & 63;\n } else {\n const next = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;\n if (c < 56320 && next >= 56320 && next <= 57343) {\n c = 65536 + (c - 55296 << 10) + (next - 56320);\n i++;\n bytes[n++] = 240 | c >> 18;\n bytes[n++] = 128 | c >> 12 & 63;\n bytes[n++] = 128 | c >> 6 & 63;\n bytes[n++] = 128 | c & 63;\n } else {\n bytes[n++] = 239;\n bytes[n++] = 191;\n bytes[n++] = 189;\n }\n }\n }\n return bytes.slice(0, n);\n }\n };\n var TextDecoder = typeof globalThis.TextDecoder !== \"undefined\" ? globalThis.TextDecoder : class TextDecoder {\n constructor(label = \"utf-8\") {\n const normalized = label.trim().toLowerCase();\n if (normalized === \"utf-8\" || normalized === \"utf8\" || normalized === \"unicode-1-1-utf-8\") {\n this.encoding = \"utf-8\";\n } else if (normalized === \"utf-16le\" || normalized === \"utf-16\") {\n this.encoding = \"utf-16le\";\n } else if (normalized === \"utf-16be\") {\n this.encoding = \"utf-16be\";\n } else {\n throw new RangeError(`The encoding label provided ('${label}') is invalid.`);\n }\n }\n decode(input) {\n const bytes = input ? toUint8Array(input) : new Uint8Array(0);\n const units = [];\n if (this.encoding === \"utf-8\") {\n let i = bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191 ? 3 : 0;\n while (i < bytes.length) {\n const lead = bytes[i];\n if (lead < 128) {\n units.push(lead);\n i++;\n continue;\n }\n let continuationCount;\n let codePoint;\n if (lead >= 194 && lead < 224) {\n continuationCount = 1;\n codePoint = lead & 31;\n } else if (lead >= 224 && lead < 240) {\n continuationCount = 2;\n codePoint = lead & 15;\n } else if (lead >= 240 && lead < 245) {\n continuationCount = 3;\n codePoint = lead & 7;\n } else {\n units.push(65533);\n i++;\n continue;\n }\n let lowerBound = lead === 224 ? 160 : lead === 240 ? 144 : 128;\n let upperBound = lead === 237 ? 159 : lead === 244 ? 143 : 191;\n let j = 1;\n for (;j <= continuationCount; j++) {\n const byte = i + j < bytes.length ? bytes[i + j] : 0;\n if (byte < lowerBound || byte > upperBound) {\n break;\n }\n codePoint = codePoint << 6 | byte & 63;\n lowerBound = 128;\n upperBound = 191;\n }\n i += j;\n if (j <= continuationCount) {\n units.push(65533);\n } else if (codePoint >= 65536) {\n codePoint -= 65536;\n units.push(55296 | codePoint >> 10, 56320 | codePoint & 1023);\n } else {\n units.push(codePoint);\n }\n }\n } else {\n const littleEndian = this.encoding === \"utf-16le\";\n const bomLow = littleEndian ? 255 : 254;\n const bomHigh = littleEndian ? 254 : 255;\n let i = bytes.length >= 2 && bytes[0] === bomLow && bytes[1] === bomHigh ? 2 : 0;\n while (i + 1 < bytes.length) {\n const unit = littleEndian ? bytes[i] | bytes[i + 1] << 8 : bytes[i] << 8 | bytes[i + 1];\n i += 2;\n if (unit >= 55296 && unit <= 56319) {\n const next = i + 1 < bytes.length ? littleEndian ? bytes[i] | bytes[i + 1] << 8 : bytes[i] << 8 | bytes[i + 1] : -1;\n if (next >= 56320 && next <= 57343) {\n units.push(unit, next);\n i += 2;\n } else {\n units.push(65533);\n }\n } else if (unit >= 56320 && unit <= 57343) {\n units.push(65533);\n } else {\n units.push(unit);\n }\n }\n if (i < bytes.length) {\n units.push(65533);\n }\n }\n let result = \"\";\n for (let i = 0;i < units.length; i += 8192) {\n result += String.fromCharCode(...units.slice(i, i + 8192));\n }\n return result;\n }\n };\n var textEncoder = /* @__PURE__ */ new TextEncoder;\n var COLOR_PRIMARIES_MAP = {\n bt709: 1,\n bt470bg: 5,\n smpte170m: 6,\n bt2020: 9,\n smpte432: 12\n };\n var TRANSFER_CHARACTERISTICS_MAP = {\n bt709: 1,\n smpte170m: 6,\n linear: 8,\n \"iec61966-2-1\": 13,\n pq: 16,\n hlg: 18\n };\n var MATRIX_COEFFICIENTS_MAP = {\n rgb: 0,\n bt709: 1,\n bt470bg: 5,\n smpte170m: 6,\n \"bt2020-ncl\": 9\n };\n var colorSpaceIsEmpty = (colorSpace) => {\n return !colorSpace || colorSpace.primaries == null && colorSpace.transfer == null && colorSpace.matrix == null && colorSpace.fullRange == null;\n };\n var isAllowSharedBufferSource = (x) => {\n return x instanceof ArrayBuffer || typeof SharedArrayBuffer !== \"undefined\" && x instanceof SharedArrayBuffer || ArrayBuffer.isView(x);\n };\n\n class AsyncMutex {\n constructor() {\n this.currentPromise = Promise.resolve();\n this.pending = 0;\n }\n async acquire() {\n let resolver;\n const nextPromise = new Promise((resolve) => {\n let resolved = false;\n resolver = () => {\n if (resolved) {\n return;\n }\n resolve();\n this.pending--;\n resolved = true;\n };\n });\n const currentPromiseAlias = this.currentPromise;\n this.currentPromise = nextPromise;\n this.pending++;\n await currentPromiseAlias;\n return resolver;\n }\n }\n var bytesToHexString = (bytes) => {\n return [...bytes].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n };\n var reverseBitsU32 = (x) => {\n x = x >> 1 & 1431655765 | (x & 1431655765) << 1;\n x = x >> 2 & 858993459 | (x & 858993459) << 2;\n x = x >> 4 & 252645135 | (x & 252645135) << 4;\n x = x >> 8 & 16711935 | (x & 16711935) << 8;\n x = x >> 16 & 65535 | (x & 65535) << 16;\n return x >>> 0;\n };\n var binarySearchLessOrEqual = (arr, key, valueGetter) => {\n let low = 0;\n let high = arr.length - 1;\n let ans = -1;\n while (low <= high) {\n const mid = low + (high - low + 1) / 2 | 0;\n const midVal = valueGetter(arr[mid]);\n if (midVal <= key) {\n ans = mid;\n low = mid + 1;\n } else {\n high = mid - 1;\n }\n }\n return ans;\n };\n var promiseWithResolvers = () => {\n let resolve;\n let reject;\n const promise = new Promise((res, rej) => {\n resolve = res;\n reject = rej;\n });\n return { promise, resolve, reject };\n };\n var assertNever = (x) => {\n throw new Error(`Unexpected value: ${x}`);\n };\n var getUint24 = (view, byteOffset, littleEndian) => {\n const byte1 = view.getUint8(byteOffset);\n const byte2 = view.getUint8(byteOffset + 1);\n const byte3 = view.getUint8(byteOffset + 2);\n if (littleEndian) {\n return byte1 | byte2 << 8 | byte3 << 16;\n } else {\n return byte1 << 16 | byte2 << 8 | byte3;\n }\n };\n var setUint24 = (view, byteOffset, value, littleEndian) => {\n value = value >>> 0;\n value = value & 16777215;\n if (littleEndian) {\n view.setUint8(byteOffset, value & 255);\n view.setUint8(byteOffset + 1, value >>> 8 & 255);\n view.setUint8(byteOffset + 2, value >>> 16 & 255);\n } else {\n view.setUint8(byteOffset, value >>> 16 & 255);\n view.setUint8(byteOffset + 1, value >>> 8 & 255);\n view.setUint8(byteOffset + 2, value & 255);\n }\n };\n var setInt24 = (view, byteOffset, value, littleEndian) => {\n value = clamp(value, -8388608, 8388607);\n if (value < 0) {\n value = value + 16777216 & 16777215;\n }\n setUint24(view, byteOffset, value, littleEndian);\n };\n var clamp = (value, min, max) => {\n return Math.max(min, Math.min(max, value));\n };\n var lerp = (from, to, t) => {\n return from + (to - from) * t;\n };\n var UNDETERMINED_LANGUAGE = \"und\";\n var roundToMultiple = (value, multiple) => {\n return Math.round(value / multiple) * multiple;\n };\n var roundToDivisor = (value, multiple) => {\n return Math.round(value * multiple) / multiple;\n };\n var floorToDivisor = (value, multiple) => {\n return Math.floor(value * multiple) / multiple;\n };\n var popcount = (value) => {\n let count = 0;\n while (value !== 0) {\n value &= value - 1;\n count++;\n }\n return count;\n };\n var ISO_639_2_REGEX = /^[a-z]{3}$/;\n var isIso639Dash2LanguageCode = (x) => {\n return ISO_639_2_REGEX.test(x);\n };\n var SECOND_TO_MICROSECOND_FACTOR = 1e6 * (1 + Number.EPSILON);\n var computeRationalApproximation = (x, maxDenominator) => {\n const sign = x < 0 ? -1 : 1;\n x = Math.abs(x);\n let prevNumerator = 0, prevDenominator = 1;\n let currNumerator = 1, currDenominator = 0;\n let remainder = x;\n while (true) {\n const integer = Math.floor(remainder);\n const nextNumerator = integer * currNumerator + prevNumerator;\n const nextDenominator = integer * currDenominator + prevDenominator;\n if (nextDenominator > maxDenominator) {\n return {\n num: sign * currNumerator,\n den: currDenominator\n };\n }\n prevNumerator = currNumerator;\n prevDenominator = currDenominator;\n currNumerator = nextNumerator;\n currDenominator = nextDenominator;\n remainder = 1 / (remainder - integer);\n if (!isFinite(remainder)) {\n break;\n }\n }\n return {\n num: sign * currNumerator,\n den: currDenominator\n };\n };\n\n class CallSerializer {\n constructor() {\n this.currentPromise = Promise.resolve();\n }\n call(fn) {\n return this.currentPromise = this.currentPromise.then(fn);\n }\n }\n var isWebKitCache = null;\n var isWebKit = () => {\n if (isWebKitCache !== null) {\n return isWebKitCache;\n }\n return isWebKitCache = !!(typeof navigator !== \"undefined\" && (navigator.vendor?.match(/apple/i) || /AppleWebKit/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) || /\\b(iPad|iPhone|iPod)\\b/.test(navigator.userAgent)));\n };\n var isFirefoxCache = null;\n var isFirefox = () => {\n if (isFirefoxCache !== null) {\n return isFirefoxCache;\n }\n return isFirefoxCache = typeof navigator !== \"undefined\" && navigator.userAgent?.includes(\"Firefox\");\n };\n var isChromiumCache = null;\n var isChromium = () => {\n if (isChromiumCache !== null) {\n return isChromiumCache;\n }\n return isChromiumCache = !!(typeof navigator !== \"undefined\" && (navigator.vendor?.includes(\"Google Inc\") || /Chrome/.test(navigator.userAgent)));\n };\n var missingWebCodecsClassMessage = (className) => {\n if (typeof globalThis.isSecureContext !== \"undefined\" && !globalThis.isSecureContext) {\n return `${className} is not available in this environment; this may be because this page is running in an` + ` insecure context. Try serving your page over HTTPS or use localhost.`;\n }\n return `${className} is not available in this environment.`;\n };\n var isCodecReclaimedError = (error) => {\n return error instanceof DOMException && error.name === \"QuotaExceededError\" && /reclaimed/i.test(error.message);\n };\n var NativePromiseConstructor = (async () => {})().constructor;\n var isThenable = (value) => {\n if (value instanceof NativePromiseConstructor || value instanceof Promise) {\n return true;\n }\n return typeof value?.then === \"function\";\n };\n var keyValueIterator = function* (object) {\n for (const key in object) {\n const value = object[key];\n if (value === undefined) {\n continue;\n }\n yield { key, value };\n }\n };\n var imageMimeTypeToExtension = (mimeType) => {\n switch (mimeType.toLowerCase()) {\n case \"image/jpeg\":\n case \"image/jpg\":\n return \".jpg\";\n case \"image/png\":\n return \".png\";\n case \"image/gif\":\n return \".gif\";\n case \"image/webp\":\n return \".webp\";\n case \"image/bmp\":\n return \".bmp\";\n case \"image/svg+xml\":\n return \".svg\";\n case \"image/tiff\":\n return \".tiff\";\n case \"image/avif\":\n return \".avif\";\n case \"image/x-icon\":\n case \"image/vnd.microsoft.icon\":\n return \".ico\";\n default:\n return null;\n }\n };\n var uint8ArraysAreEqual = (a, b) => {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0;i < a.length; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n };\n var polyfillSymbolDispose = () => {\n Symbol.dispose ??= Symbol(\"Symbol.dispose\");\n };\n var isNumber = (x) => {\n return typeof x === \"number\" && !Number.isNaN(x);\n };\n var arrayArgmin = (array, getValue) => {\n let minIndex = -1;\n let minValue = Infinity;\n for (let i = 0;i < array.length; i++) {\n const value = getValue(array[i]);\n if (value < minValue) {\n minValue = value;\n minIndex = i;\n }\n }\n return minIndex;\n };\n var simplifyRational = (rational) => {\n assert(Number.isInteger(rational.num));\n assert(Number.isInteger(rational.den));\n assert(rational.den !== 0);\n let a = Math.abs(rational.num);\n let b = Math.abs(rational.den);\n while (b !== 0) {\n const t = a % b;\n a = b;\n b = t;\n }\n const gcd = a || 1;\n return {\n num: rational.num / gcd,\n den: rational.den / gcd\n };\n };\n var validateRectangle = (rect, propertyPath) => {\n if (typeof rect !== \"object\" || !rect) {\n throw new TypeError(`${propertyPath} must be an object.`);\n }\n if (!Number.isInteger(rect.left) || rect.left < 0) {\n throw new TypeError(`${propertyPath}.left must be a non-negative integer.`);\n }\n if (!Number.isInteger(rect.top) || rect.top < 0) {\n throw new TypeError(`${propertyPath}.top must be a non-negative integer.`);\n }\n if (!Number.isInteger(rect.width) || rect.width < 0) {\n throw new TypeError(`${propertyPath}.width must be a non-negative integer.`);\n }\n if (!Number.isInteger(rect.height) || rect.height < 0) {\n throw new TypeError(`${propertyPath}.height must be a non-negative integer.`);\n }\n };\n var unthrottledTimeoutCallbacks = new Map;\n var unthrottledIntervalCallbacks = new Map;\n var wait = (ms) => {\n return new Promise((resolve) => setTimeout(resolve, ms));\n };\n var toArray = (x) => {\n if (Array.isArray(x)) {\n return x;\n } else {\n return [x];\n }\n };\n\n class EventEmitter {\n constructor() {\n this._listeners = new Map;\n }\n on(event, listener, options) {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set);\n }\n const entry = { fn: listener, once: options?.once ?? false };\n this._listeners.get(event).add(entry);\n return () => {\n this._listeners.get(event)?.delete(entry);\n };\n }\n _emit(...args) {\n const [event, data] = args;\n const listeners = this._listeners.get(event);\n if (!listeners) {\n return;\n }\n for (const entry of listeners) {\n try {\n entry.fn(data);\n } catch (error) {\n console.error(error);\n }\n if (entry.once) {\n listeners.delete(entry);\n }\n }\n }\n }\n var isRecordStringString = (value) => {\n return value !== null && typeof value === \"object\" && Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((x) => typeof x === \"string\");\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/logging.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var LogLevel;\n (function(LogLevel) {\n LogLevel[LogLevel[\"Silent\"] = 0] = \"Silent\";\n LogLevel[LogLevel[\"Errors\"] = 1] = \"Errors\";\n LogLevel[LogLevel[\"Warnings\"] = 2] = \"Warnings\";\n LogLevel[LogLevel[\"Info\"] = 3] = \"Info\";\n })(LogLevel || (LogLevel = {}));\n\n class Logging {\n constructor() {}\n static get level() {\n return Logging._level;\n }\n static set level(value) {\n if (value !== LogLevel.Silent && value !== LogLevel.Errors && value !== LogLevel.Warnings && value !== LogLevel.Info) {\n throw new TypeError(\"Invalid log level. Use one of the values of the LogLevel enum.\");\n }\n Logging._level = value;\n }\n static get _emitter() {\n return Logging._emitterInstance ??= new EventEmitter;\n }\n static on(event, listener, options) {\n return Logging._emitter.on(event, listener, options);\n }\n static _error(...args) {\n Logging._emitter._emit(\"error\", args);\n if (Logging._level >= LogLevel.Errors) {\n console.error(...args);\n }\n }\n static _warn(...args) {\n Logging._emitter._emit(\"warn\", args);\n if (Logging._level >= LogLevel.Warnings) {\n console.warn(...args);\n }\n }\n static _info(...args) {\n Logging._emitter._emit(\"info\", args);\n if (Logging._level >= LogLevel.Info) {\n console.info(...args);\n }\n }\n }\n Logging._level = LogLevel.Info;\n Logging._emitterInstance = null;\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/metadata.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class RichImageData {\n constructor(data, mimeType) {\n this.data = data;\n this.mimeType = mimeType;\n if (!(data instanceof Uint8Array)) {\n throw new TypeError(\"data must be a Uint8Array.\");\n }\n if (typeof mimeType !== \"string\") {\n throw new TypeError(\"mimeType must be a string.\");\n }\n }\n }\n\n class AttachedFile {\n constructor(data, mimeType, name, description) {\n this.data = data;\n this.mimeType = mimeType;\n this.name = name;\n this.description = description;\n if (!(data instanceof Uint8Array)) {\n throw new TypeError(\"data must be a Uint8Array.\");\n }\n if (mimeType !== undefined && typeof mimeType !== \"string\") {\n throw new TypeError(\"mimeType, when provided, must be a string.\");\n }\n if (name !== undefined && typeof name !== \"string\") {\n throw new TypeError(\"name, when provided, must be a string.\");\n }\n if (description !== undefined && typeof description !== \"string\") {\n throw new TypeError(\"description, when provided, must be a string.\");\n }\n }\n }\n var validateMetadataTags = (tags) => {\n if (!tags || typeof tags !== \"object\") {\n throw new TypeError(\"tags must be an object.\");\n }\n if (tags.title !== undefined && typeof tags.title !== \"string\") {\n throw new TypeError(\"tags.title, when provided, must be a string.\");\n }\n if (tags.description !== undefined && typeof tags.description !== \"string\") {\n throw new TypeError(\"tags.description, when provided, must be a string.\");\n }\n if (tags.artist !== undefined && typeof tags.artist !== \"string\") {\n throw new TypeError(\"tags.artist, when provided, must be a string.\");\n }\n if (tags.album !== undefined && typeof tags.album !== \"string\") {\n throw new TypeError(\"tags.album, when provided, must be a string.\");\n }\n if (tags.albumArtist !== undefined && typeof tags.albumArtist !== \"string\") {\n throw new TypeError(\"tags.albumArtist, when provided, must be a string.\");\n }\n if (tags.trackNumber !== undefined && (!Number.isInteger(tags.trackNumber) || tags.trackNumber <= 0)) {\n throw new TypeError(\"tags.trackNumber, when provided, must be a positive integer.\");\n }\n if (tags.tracksTotal !== undefined && (!Number.isInteger(tags.tracksTotal) || tags.tracksTotal <= 0)) {\n throw new TypeError(\"tags.tracksTotal, when provided, must be a positive integer.\");\n }\n if (tags.discNumber !== undefined && (!Number.isInteger(tags.discNumber) || tags.discNumber <= 0)) {\n throw new TypeError(\"tags.discNumber, when provided, must be a positive integer.\");\n }\n if (tags.discsTotal !== undefined && (!Number.isInteger(tags.discsTotal) || tags.discsTotal <= 0)) {\n throw new TypeError(\"tags.discsTotal, when provided, must be a positive integer.\");\n }\n if (tags.genre !== undefined && typeof tags.genre !== \"string\") {\n throw new TypeError(\"tags.genre, when provided, must be a string.\");\n }\n if (tags.date !== undefined && (!(tags.date instanceof Date) || Number.isNaN(tags.date.getTime()))) {\n throw new TypeError(\"tags.date, when provided, must be a valid Date.\");\n }\n if (tags.beatsPerMinute !== undefined && (!Number.isInteger(tags.beatsPerMinute) || tags.beatsPerMinute <= 0)) {\n throw new TypeError(\"tags.beatsPerMinute, when provided, must be a positive integer.\");\n }\n if (tags.lyrics !== undefined && typeof tags.lyrics !== \"string\") {\n throw new TypeError(\"tags.lyrics, when provided, must be a string.\");\n }\n if (tags.images !== undefined) {\n if (!Array.isArray(tags.images)) {\n throw new TypeError(\"tags.images, when provided, must be an array.\");\n }\n for (const image of tags.images) {\n if (!image || typeof image !== \"object\") {\n throw new TypeError(\"Each image in tags.images must be an object.\");\n }\n if (!(image.data instanceof Uint8Array)) {\n throw new TypeError(\"Each image.data must be a Uint8Array.\");\n }\n if (typeof image.mimeType !== \"string\") {\n throw new TypeError(\"Each image.mimeType must be a string.\");\n }\n if (![\"coverFront\", \"coverBack\", \"unknown\"].includes(image.kind)) {\n throw new TypeError(\"Each image.kind must be 'coverFront', 'coverBack', or 'unknown'.\");\n }\n }\n }\n if (tags.comment !== undefined && typeof tags.comment !== \"string\") {\n throw new TypeError(\"tags.comment, when provided, must be a string.\");\n }\n if (tags.raw !== undefined) {\n if (!tags.raw || typeof tags.raw !== \"object\") {\n throw new TypeError(\"tags.raw, when provided, must be an object.\");\n }\n for (const value of Object.values(tags.raw)) {\n if (value !== null && typeof value !== \"string\" && !(Array.isArray(value) && value.every((x) => typeof x === \"string\")) && !(value instanceof Uint8Array) && !(value instanceof RichImageData) && !(value instanceof AttachedFile) && !isRecordStringString(value)) {\n throw new TypeError(\"Each value in tags.raw must be a string, string array, Uint8Array, RichImageData, AttachedFile, \" + \"Record<string, string>, or null.\");\n }\n }\n }\n };\n var validateTrackDisposition = (disposition) => {\n if (!disposition || typeof disposition !== \"object\") {\n throw new TypeError(\"disposition must be an object.\");\n }\n if (disposition.default !== undefined && typeof disposition.default !== \"boolean\") {\n throw new TypeError(\"disposition.default must be a boolean.\");\n }\n if (disposition.primary !== undefined && typeof disposition.primary !== \"boolean\") {\n throw new TypeError(\"disposition.primary must be a boolean.\");\n }\n if (disposition.forced !== undefined && typeof disposition.forced !== \"boolean\") {\n throw new TypeError(\"disposition.forced must be a boolean.\");\n }\n if (disposition.original !== undefined && typeof disposition.original !== \"boolean\") {\n throw new TypeError(\"disposition.original must be a boolean.\");\n }\n if (disposition.commentary !== undefined && typeof disposition.commentary !== \"boolean\") {\n throw new TypeError(\"disposition.commentary must be a boolean.\");\n }\n if (disposition.hearingImpaired !== undefined && typeof disposition.hearingImpaired !== \"boolean\") {\n throw new TypeError(\"disposition.hearingImpaired must be a boolean.\");\n }\n if (disposition.visuallyImpaired !== undefined && typeof disposition.visuallyImpaired !== \"boolean\") {\n throw new TypeError(\"disposition.visuallyImpaired must be a boolean.\");\n }\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/shared/bitstream.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class Bitstream {\n constructor(bytes) {\n this.bytes = bytes;\n this.pos = 0;\n }\n seekToByte(byteOffset) {\n this.pos = 8 * byteOffset;\n }\n readBit() {\n const byteIndex = Math.floor(this.pos / 8);\n const byte = this.bytes[byteIndex] ?? 0;\n const bitIndex = 7 - (this.pos & 7);\n const bit = (byte & 1 << bitIndex) >> bitIndex;\n this.pos++;\n return bit;\n }\n readBits(n) {\n if (n === 1) {\n return this.readBit();\n }\n let result = 0;\n for (let i = 0;i < n; i++) {\n result <<= 1;\n result |= this.readBit();\n }\n return result;\n }\n writeBits(n, value) {\n const end = this.pos + n;\n for (let i = this.pos;i < end; i++) {\n const byteIndex = Math.floor(i / 8);\n let byte = this.bytes[byteIndex];\n const bitIndex = 7 - (i & 7);\n byte &= ~(1 << bitIndex);\n byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex;\n this.bytes[byteIndex] = byte;\n }\n this.pos = end;\n }\n copyBits(n, other) {\n let i = 0;\n for (i;i < n - 7; i += 8) {\n this.writeBits(8, other.readBits(8));\n }\n const leftover = n - i;\n if (leftover > 0) {\n this.writeBits(leftover, other.readBits(leftover));\n }\n }\n readAlignedByte() {\n if (this.pos % 8 !== 0) {\n throw new Error(\"Bitstream is not byte-aligned.\");\n }\n const byteIndex = this.pos / 8;\n const byte = this.bytes[byteIndex] ?? 0;\n this.pos += 8;\n return byte;\n }\n skipBits(n) {\n this.pos += n;\n }\n getBitsLeft() {\n return this.bytes.length * 8 - this.pos;\n }\n clone() {\n const clone = new Bitstream(this.bytes);\n clone.pos = this.pos;\n return clone;\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/shared/aac-misc.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var aacFrequencyTable = [\n 96000,\n 88200,\n 64000,\n 48000,\n 44100,\n 32000,\n 24000,\n 22050,\n 16000,\n 12000,\n 11025,\n 8000,\n 7350\n ];\n var aacChannelMap = [-1, 1, 2, 3, 4, 5, 6, 8];\n var parseAacAudioSpecificConfig = (bytes) => {\n if (!bytes || bytes.byteLength < 2) {\n throw new TypeError(\"AAC description must be at least 2 bytes long.\");\n }\n const bitstream = new Bitstream(bytes);\n const objectType = readAacObjectType(bitstream);\n const { frequencyIndex, sampleRate } = readAacSamplingFrequency(bitstream);\n const channelConfiguration = bitstream.readBits(4);\n let numberOfChannels = null;\n if (channelConfiguration >= 1 && channelConfiguration <= 7) {\n numberOfChannels = aacChannelMap[channelConfiguration];\n }\n let coreObjectType = objectType;\n let psPresent = false;\n let outputSampleRate = sampleRate;\n if (objectType === 5 || objectType === 29) {\n psPresent = objectType === 29;\n outputSampleRate = readAacSamplingFrequency(bitstream).sampleRate;\n coreObjectType = readAacObjectType(bitstream);\n if (coreObjectType === 22) {\n bitstream.skipBits(4);\n }\n } else {\n while (bitstream.getBitsLeft() > 15) {\n const searchStart = bitstream.pos;\n if (bitstream.readBits(11) !== 695) {\n bitstream.pos = searchStart + 1;\n continue;\n }\n if (readAacObjectType(bitstream) === 5 && bitstream.readBits(1)) {\n outputSampleRate = readAacSamplingFrequency(bitstream).sampleRate;\n if (bitstream.getBitsLeft() > 11 && bitstream.readBits(11) === 1352) {\n psPresent = !!bitstream.readBits(1);\n }\n }\n break;\n }\n }\n if (numberOfChannels !== null && numberOfChannels > 1) {\n psPresent = false;\n }\n return {\n objectType,\n coreObjectType,\n frequencyIndex,\n channelConfiguration,\n outputSampleRate,\n outputNumberOfChannels: psPresent && numberOfChannels === 1 ? 2 : numberOfChannels\n };\n };\n var readAacObjectType = (bitstream) => {\n const objectType = bitstream.readBits(5);\n return objectType === 31 ? 32 + bitstream.readBits(6) : objectType;\n };\n var readAacSamplingFrequency = (bitstream) => {\n const frequencyIndex = bitstream.readBits(4);\n if (frequencyIndex === 15) {\n return {\n frequencyIndex,\n sampleRate: bitstream.readBits(24)\n };\n }\n return {\n frequencyIndex,\n sampleRate: frequencyIndex < aacFrequencyTable.length ? aacFrequencyTable[frequencyIndex] : null\n };\n };\n var buildAacAudioSpecificConfig = (config) => {\n const usesSbr = config.objectType === 5 || config.objectType === 29;\n const usesPs = config.objectType === 29;\n const coreSampleRate = usesSbr ? config.outputSampleRate / 2 : config.outputSampleRate;\n const coreNumberOfChannels = usesPs ? 1 : config.outputNumberOfChannels;\n const channelConfiguration = aacChannelMap.indexOf(coreNumberOfChannels);\n if (channelConfiguration === -1) {\n throw new TypeError(`Unsupported number of channels: ${config.outputNumberOfChannels}`);\n }\n let bitCount = 5 + 4 + 4 + 3;\n if (config.objectType >= 32) {\n bitCount += 6;\n }\n if (findAacFrequencyIndex(coreSampleRate) === 15) {\n bitCount += 24;\n }\n if (usesSbr) {\n bitCount += 4 + 5;\n if (findAacFrequencyIndex(config.outputSampleRate) === 15) {\n bitCount += 24;\n }\n }\n const byteCount = Math.ceil(bitCount / 8);\n const bytes = new Uint8Array(byteCount);\n const bitstream = new Bitstream(bytes);\n writeAacObjectType(bitstream, config.objectType);\n writeAacSamplingFrequency(bitstream, coreSampleRate);\n bitstream.writeBits(4, channelConfiguration);\n if (usesSbr) {\n writeAacSamplingFrequency(bitstream, config.outputSampleRate);\n writeAacObjectType(bitstream, 2);\n }\n bitstream.writeBits(3, 0);\n return bytes;\n };\n var writeAacObjectType = (bitstream, objectType) => {\n if (objectType < 32) {\n bitstream.writeBits(5, objectType);\n } else {\n bitstream.writeBits(5, 31);\n bitstream.writeBits(6, objectType - 32);\n }\n };\n var writeAacSamplingFrequency = (bitstream, sampleRate) => {\n const frequencyIndex = findAacFrequencyIndex(sampleRate);\n bitstream.writeBits(4, frequencyIndex);\n if (frequencyIndex === 15) {\n bitstream.writeBits(24, sampleRate);\n }\n };\n var findAacFrequencyIndex = (sampleRate) => {\n const index = aacFrequencyTable.indexOf(sampleRate);\n return index === -1 ? 15 : index;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/shared/ac3-misc.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var AC3_SAMPLE_RATES = [48000, 44100, 32000];\n var EAC3_REDUCED_SAMPLE_RATES = [24000, 22050, 16000];\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/version.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var VERSION = \"1.61.3\";\n var VERSIONED_NAME = `Mediabunny v${VERSION}`;\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/codec-data.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var AvcNalUnitType;\n (function(AvcNalUnitType) {\n AvcNalUnitType[AvcNalUnitType[\"NON_IDR_SLICE\"] = 1] = \"NON_IDR_SLICE\";\n AvcNalUnitType[AvcNalUnitType[\"SLICE_DPA\"] = 2] = \"SLICE_DPA\";\n AvcNalUnitType[AvcNalUnitType[\"SLICE_DPB\"] = 3] = \"SLICE_DPB\";\n AvcNalUnitType[AvcNalUnitType[\"SLICE_DPC\"] = 4] = \"SLICE_DPC\";\n AvcNalUnitType[AvcNalUnitType[\"IDR\"] = 5] = \"IDR\";\n AvcNalUnitType[AvcNalUnitType[\"SEI\"] = 6] = \"SEI\";\n AvcNalUnitType[AvcNalUnitType[\"SPS\"] = 7] = \"SPS\";\n AvcNalUnitType[AvcNalUnitType[\"PPS\"] = 8] = \"PPS\";\n AvcNalUnitType[AvcNalUnitType[\"AUD\"] = 9] = \"AUD\";\n AvcNalUnitType[AvcNalUnitType[\"SPS_EXT\"] = 13] = \"SPS_EXT\";\n })(AvcNalUnitType || (AvcNalUnitType = {}));\n var HevcNalUnitType;\n (function(HevcNalUnitType) {\n HevcNalUnitType[HevcNalUnitType[\"RASL_N\"] = 8] = \"RASL_N\";\n HevcNalUnitType[HevcNalUnitType[\"RASL_R\"] = 9] = \"RASL_R\";\n HevcNalUnitType[HevcNalUnitType[\"BLA_W_LP\"] = 16] = \"BLA_W_LP\";\n HevcNalUnitType[HevcNalUnitType[\"RSV_IRAP_VCL23\"] = 23] = \"RSV_IRAP_VCL23\";\n HevcNalUnitType[HevcNalUnitType[\"VPS_NUT\"] = 32] = \"VPS_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"SPS_NUT\"] = 33] = \"SPS_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"PPS_NUT\"] = 34] = \"PPS_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"AUD_NUT\"] = 35] = \"AUD_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"PREFIX_SEI_NUT\"] = 39] = \"PREFIX_SEI_NUT\";\n HevcNalUnitType[HevcNalUnitType[\"SUFFIX_SEI_NUT\"] = 40] = \"SUFFIX_SEI_NUT\";\n })(HevcNalUnitType || (HevcNalUnitType = {}));\n var iterateNalUnitsInAnnexB = function* (packetData) {\n let i = 0;\n let nalStart = -1;\n while (i < packetData.length - 2) {\n const zeroIndex = packetData.indexOf(0, i);\n if (zeroIndex === -1 || zeroIndex >= packetData.length - 2) {\n break;\n }\n i = zeroIndex;\n let startCodeLength = 0;\n if (i + 3 < packetData.length && packetData[i + 1] === 0 && packetData[i + 2] === 0 && packetData[i + 3] === 1) {\n startCodeLength = 4;\n } else if (packetData[i + 1] === 0 && packetData[i + 2] === 1) {\n startCodeLength = 3;\n }\n if (startCodeLength === 0) {\n i++;\n continue;\n }\n if (nalStart !== -1 && i > nalStart) {\n yield {\n offset: nalStart,\n length: i - nalStart\n };\n }\n nalStart = i + startCodeLength;\n i = nalStart;\n }\n if (nalStart !== -1 && nalStart < packetData.length) {\n yield {\n offset: nalStart,\n length: packetData.length - nalStart\n };\n }\n };\n var iterateNalUnitsInLengthPrefixed = function* (packetData, lengthSize) {\n let offset = 0;\n const dataView = new DataView(packetData.buffer, packetData.byteOffset, packetData.byteLength);\n while (offset + lengthSize <= packetData.length) {\n let nalUnitLength;\n if (lengthSize === 1) {\n nalUnitLength = dataView.getUint8(offset);\n } else if (lengthSize === 2) {\n nalUnitLength = dataView.getUint16(offset, false);\n } else if (lengthSize === 3) {\n nalUnitLength = getUint24(dataView, offset, false);\n } else {\n assert(lengthSize === 4);\n nalUnitLength = dataView.getUint32(offset, false);\n }\n offset += lengthSize;\n yield {\n offset,\n length: nalUnitLength\n };\n offset += nalUnitLength;\n }\n };\n var iterateAvcNalUnits = (packetData, decoderConfig) => {\n if (decoderConfig.description) {\n const bytes = toUint8Array(decoderConfig.description);\n const lengthSizeMinusOne = bytes[4] & 3;\n const lengthSize = lengthSizeMinusOne + 1;\n return iterateNalUnitsInLengthPrefixed(packetData, lengthSize);\n } else {\n return iterateNalUnitsInAnnexB(packetData);\n }\n };\n var extractNalUnitTypeForAvc = (byte) => {\n return byte & 31;\n };\n var removeEmulationPreventionBytes = (data) => {\n const result = [];\n const len = data.length;\n for (let i = 0;i < len; i++) {\n if (i + 2 < len && data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 3) {\n result.push(0, 0);\n i += 2;\n } else {\n result.push(data[i]);\n }\n }\n return new Uint8Array(result);\n };\n var ANNEX_B_START_CODE = new Uint8Array([0, 0, 0, 1]);\n var concatNalUnitsInLengthPrefixed = (nalUnits, lengthSize) => {\n const totalLength = nalUnits.reduce((a, b) => a + lengthSize + b.byteLength, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const nalUnit of nalUnits) {\n const dataView = new DataView(result.buffer, result.byteOffset, result.byteLength);\n switch (lengthSize) {\n case 1:\n dataView.setUint8(offset, nalUnit.byteLength);\n break;\n case 2:\n dataView.setUint16(offset, nalUnit.byteLength, false);\n break;\n case 3:\n setUint24(dataView, offset, nalUnit.byteLength, false);\n break;\n case 4:\n dataView.setUint32(offset, nalUnit.byteLength, false);\n break;\n }\n offset += lengthSize;\n result.set(nalUnit, offset);\n offset += nalUnit.byteLength;\n }\n return result;\n };\n var extractAvcDecoderConfigurationRecord = (packetData) => {\n try {\n const spsUnits = [];\n const ppsUnits = [];\n const spsExtUnits = [];\n for (const loc of iterateNalUnitsInAnnexB(packetData)) {\n const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length);\n const type = extractNalUnitTypeForAvc(nalUnit[0]);\n if (type === AvcNalUnitType.SPS) {\n spsUnits.push(nalUnit);\n } else if (type === AvcNalUnitType.PPS) {\n ppsUnits.push(nalUnit);\n } else if (type === AvcNalUnitType.SPS_EXT) {\n spsExtUnits.push(nalUnit);\n }\n }\n if (spsUnits.length === 0) {\n return null;\n }\n if (ppsUnits.length === 0) {\n return null;\n }\n const spsData = spsUnits[0];\n const spsInfo = parseAvcSps(spsData);\n assert(spsInfo !== null);\n const hasExtendedData = spsInfo.profileIdc === 100 || spsInfo.profileIdc === 110 || spsInfo.profileIdc === 122 || spsInfo.profileIdc === 144;\n return {\n configurationVersion: 1,\n avcProfileIndication: spsInfo.profileIdc,\n profileCompatibility: spsInfo.constraintFlags,\n avcLevelIndication: spsInfo.levelIdc,\n lengthSizeMinusOne: 3,\n sequenceParameterSets: spsUnits,\n pictureParameterSets: ppsUnits,\n chromaFormat: hasExtendedData ? spsInfo.chromaFormatIdc : null,\n bitDepthLumaMinus8: hasExtendedData ? spsInfo.bitDepthLumaMinus8 : null,\n bitDepthChromaMinus8: hasExtendedData ? spsInfo.bitDepthChromaMinus8 : null,\n sequenceParameterSetExt: hasExtendedData ? spsExtUnits : null\n };\n } catch (error) {\n Logging._error(\"Error building AVC Decoder Configuration Record:\", error);\n return null;\n }\n };\n var serializeAvcDecoderConfigurationRecord = (record) => {\n const bytes = [];\n bytes.push(record.configurationVersion);\n bytes.push(record.avcProfileIndication);\n bytes.push(record.profileCompatibility);\n bytes.push(record.avcLevelIndication);\n bytes.push(252 | record.lengthSizeMinusOne & 3);\n bytes.push(224 | record.sequenceParameterSets.length & 31);\n for (const sps of record.sequenceParameterSets) {\n const length = sps.byteLength;\n bytes.push(length >> 8);\n bytes.push(length & 255);\n for (let i = 0;i < length; i++) {\n bytes.push(sps[i]);\n }\n }\n bytes.push(record.pictureParameterSets.length);\n for (const pps of record.pictureParameterSets) {\n const length = pps.byteLength;\n bytes.push(length >> 8);\n bytes.push(length & 255);\n for (let i = 0;i < length; i++) {\n bytes.push(pps[i]);\n }\n }\n if ((record.avcProfileIndication === 100 || record.avcProfileIndication === 110 || record.avcProfileIndication === 122 || record.avcProfileIndication === 144) && record.chromaFormat !== null) {\n assert(record.bitDepthLumaMinus8 !== null);\n assert(record.bitDepthChromaMinus8 !== null);\n assert(record.sequenceParameterSetExt !== null);\n bytes.push(252 | record.chromaFormat & 3);\n bytes.push(248 | record.bitDepthLumaMinus8 & 7);\n bytes.push(248 | record.bitDepthChromaMinus8 & 7);\n bytes.push(record.sequenceParameterSetExt.length);\n for (const spsExt of record.sequenceParameterSetExt) {\n const length = spsExt.byteLength;\n bytes.push(length >> 8);\n bytes.push(length & 255);\n for (let i = 0;i < length; i++) {\n bytes.push(spsExt[i]);\n }\n }\n }\n return new Uint8Array(bytes);\n };\n var AVC_HEVC_ASPECT_RATIO_IDC_TABLE = {\n 1: { num: 1, den: 1 },\n 2: { num: 12, den: 11 },\n 3: { num: 10, den: 11 },\n 4: { num: 16, den: 11 },\n 5: { num: 40, den: 33 },\n 6: { num: 24, den: 11 },\n 7: { num: 20, den: 11 },\n 8: { num: 32, den: 11 },\n 9: { num: 80, den: 33 },\n 10: { num: 18, den: 11 },\n 11: { num: 15, den: 11 },\n 12: { num: 64, den: 33 },\n 13: { num: 160, den: 99 },\n 14: { num: 4, den: 3 },\n 15: { num: 3, den: 2 },\n 16: { num: 2, den: 1 }\n };\n var parseAvcSps = (sps) => {\n try {\n const emulationUnpreventedBytes = removeEmulationPreventionBytes(sps);\n const bitstream = new Bitstream(emulationUnpreventedBytes);\n bitstream.skipBits(1);\n bitstream.skipBits(2);\n const nalUnitType = bitstream.readBits(5);\n if (nalUnitType !== 7) {\n return null;\n }\n const profileIdc = bitstream.readAlignedByte();\n const constraintFlags = bitstream.readAlignedByte();\n const levelIdc = bitstream.readAlignedByte();\n readExpGolomb(bitstream);\n let chromaFormatIdc = 1;\n let bitDepthLumaMinus8 = 0;\n let bitDepthChromaMinus8 = 0;\n let separateColourPlaneFlag = 0;\n if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {\n chromaFormatIdc = readExpGolomb(bitstream);\n if (chromaFormatIdc === 3) {\n separateColourPlaneFlag = bitstream.readBits(1);\n }\n bitDepthLumaMinus8 = readExpGolomb(bitstream);\n bitDepthChromaMinus8 = readExpGolomb(bitstream);\n bitstream.skipBits(1);\n const seqScalingMatrixPresentFlag = bitstream.readBits(1);\n if (seqScalingMatrixPresentFlag) {\n for (let i = 0;i < (chromaFormatIdc !== 3 ? 8 : 12); i++) {\n const seqScalingListPresentFlag = bitstream.readBits(1);\n if (seqScalingListPresentFlag) {\n const sizeOfScalingList = i < 6 ? 16 : 64;\n let lastScale = 8;\n let nextScale = 8;\n for (let j = 0;j < sizeOfScalingList; j++) {\n if (nextScale !== 0) {\n const deltaScale = readSignedExpGolomb(bitstream);\n nextScale = (lastScale + deltaScale + 256) % 256;\n }\n lastScale = nextScale === 0 ? lastScale : nextScale;\n }\n }\n }\n }\n }\n readExpGolomb(bitstream);\n const picOrderCntType = readExpGolomb(bitstream);\n if (picOrderCntType === 0) {\n readExpGolomb(bitstream);\n } else if (picOrderCntType === 1) {\n bitstream.skipBits(1);\n readSignedExpGolomb(bitstream);\n readSignedExpGolomb(bitstream);\n const numRefFramesInPicOrderCntCycle = readExpGolomb(bitstream);\n for (let i = 0;i < numRefFramesInPicOrderCntCycle; i++) {\n readSignedExpGolomb(bitstream);\n }\n }\n readExpGolomb(bitstream);\n bitstream.skipBits(1);\n const picWidthInMbsMinus1 = readExpGolomb(bitstream);\n const picHeightInMapUnitsMinus1 = readExpGolomb(bitstream);\n const codedWidth = 16 * (picWidthInMbsMinus1 + 1);\n const codedHeight = 16 * (picHeightInMapUnitsMinus1 + 1);\n let displayWidth = codedWidth;\n let displayHeight = codedHeight;\n const frameMbsOnlyFlag = bitstream.readBits(1);\n if (!frameMbsOnlyFlag) {\n bitstream.skipBits(1);\n }\n bitstream.skipBits(1);\n const frameCroppingFlag = bitstream.readBits(1);\n if (frameCroppingFlag) {\n const frameCropLeftOffset = readExpGolomb(bitstream);\n const frameCropRightOffset = readExpGolomb(bitstream);\n const frameCropTopOffset = readExpGolomb(bitstream);\n const frameCropBottomOffset = readExpGolomb(bitstream);\n let cropUnitX;\n let cropUnitY;\n const chromaArrayType = separateColourPlaneFlag === 0 ? chromaFormatIdc : 0;\n if (chromaArrayType === 0) {\n cropUnitX = 1;\n cropUnitY = 2 - frameMbsOnlyFlag;\n } else {\n const subWidthC = chromaFormatIdc === 3 ? 1 : 2;\n const subHeightC = chromaFormatIdc === 1 ? 2 : 1;\n cropUnitX = subWidthC;\n cropUnitY = subHeightC * (2 - frameMbsOnlyFlag);\n }\n displayWidth -= cropUnitX * (frameCropLeftOffset + frameCropRightOffset);\n displayHeight -= cropUnitY * (frameCropTopOffset + frameCropBottomOffset);\n }\n let colourPrimaries = 2;\n let transferCharacteristics = 2;\n let matrixCoefficients = 2;\n let fullRangeFlag = 0;\n let pixelAspectRatio = { num: 1, den: 1 };\n let numReorderFrames = null;\n let maxDecFrameBuffering = null;\n let bitstreamRestrictionFlagBitOffset = null;\n let bitstreamRestrictionFlag = null;\n const vuiParametersFlagBitOffset = bitstream.pos;\n const vuiParametersPresentFlag = bitstream.readBits(1);\n if (vuiParametersPresentFlag) {\n const aspectRatioInfoPresentFlag = bitstream.readBits(1);\n if (aspectRatioInfoPresentFlag) {\n const aspectRatioIdc = bitstream.readBits(8);\n if (aspectRatioIdc === 255) {\n pixelAspectRatio = {\n num: bitstream.readBits(16),\n den: bitstream.readBits(16)\n };\n } else {\n const aspectRatio = AVC_HEVC_ASPECT_RATIO_IDC_TABLE[aspectRatioIdc];\n if (aspectRatio) {\n pixelAspectRatio = aspectRatio;\n }\n }\n }\n const overscanInfoPresentFlag = bitstream.readBits(1);\n if (overscanInfoPresentFlag) {\n bitstream.skipBits(1);\n }\n const videoSignalTypePresentFlag = bitstream.readBits(1);\n if (videoSignalTypePresentFlag) {\n bitstream.skipBits(3);\n fullRangeFlag = bitstream.readBits(1);\n const colourDescriptionPresentFlag = bitstream.readBits(1);\n if (colourDescriptionPresentFlag) {\n colourPrimaries = bitstream.readBits(8);\n transferCharacteristics = bitstream.readBits(8);\n matrixCoefficients = bitstream.readBits(8);\n }\n }\n const chromaLocInfoPresentFlag = bitstream.readBits(1);\n if (chromaLocInfoPresentFlag) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n const timingInfoPresentFlag = bitstream.readBits(1);\n if (timingInfoPresentFlag) {\n bitstream.skipBits(32);\n bitstream.skipBits(32);\n bitstream.skipBits(1);\n }\n const nalHrdParametersPresentFlag = bitstream.readBits(1);\n if (nalHrdParametersPresentFlag) {\n skipAvcHrdParameters(bitstream);\n }\n const vclHrdParametersPresentFlag = bitstream.readBits(1);\n if (vclHrdParametersPresentFlag) {\n skipAvcHrdParameters(bitstream);\n }\n if (nalHrdParametersPresentFlag || vclHrdParametersPresentFlag) {\n bitstream.skipBits(1);\n }\n bitstream.skipBits(1);\n bitstreamRestrictionFlagBitOffset = bitstream.pos;\n bitstreamRestrictionFlag = bitstream.readBits(1);\n if (bitstreamRestrictionFlag) {\n bitstream.skipBits(1);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n numReorderFrames = readExpGolomb(bitstream);\n maxDecFrameBuffering = readExpGolomb(bitstream);\n }\n }\n if (numReorderFrames === null) {\n assert(maxDecFrameBuffering === null);\n const constraintSet3Flag = constraintFlags & 16;\n if ((profileIdc === 44 || profileIdc === 86 || profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244) && constraintSet3Flag) {\n numReorderFrames = 0;\n maxDecFrameBuffering = 0;\n } else {\n const picWidthInMbs = picWidthInMbsMinus1 + 1;\n const picHeightInMapUnits = picHeightInMapUnitsMinus1 + 1;\n const frameHeightInMbs = (2 - frameMbsOnlyFlag) * picHeightInMapUnits;\n const levelInfo = AVC_LEVEL_TABLE.find((x) => x.level >= levelIdc) ?? last(AVC_LEVEL_TABLE);\n const maxDpbFrames = Math.min(Math.floor(levelInfo.maxDpbMbs / (picWidthInMbs * frameHeightInMbs)), 16);\n numReorderFrames = maxDpbFrames;\n maxDecFrameBuffering = maxDpbFrames;\n }\n }\n assert(maxDecFrameBuffering !== null);\n return {\n emulationUnpreventedBytes,\n profileIdc,\n constraintFlags,\n levelIdc,\n frameMbsOnlyFlag,\n chromaFormatIdc,\n bitDepthLumaMinus8,\n bitDepthChromaMinus8,\n codedWidth,\n codedHeight,\n displayWidth,\n displayHeight,\n pixelAspectRatio,\n colourPrimaries,\n matrixCoefficients,\n transferCharacteristics,\n fullRangeFlag,\n numReorderFrames,\n maxDecFrameBuffering,\n vuiParametersFlagBitOffset,\n bitstreamRestrictionFlagBitOffset,\n bitstreamRestrictionFlag\n };\n } catch (error) {\n Logging._error(\"Error parsing AVC SPS:\", error);\n return null;\n }\n };\n var skipAvcHrdParameters = (bitstream) => {\n const cpb_cnt_minus1 = readExpGolomb(bitstream);\n bitstream.skipBits(4);\n bitstream.skipBits(4);\n for (let i = 0;i <= cpb_cnt_minus1; i++) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n bitstream.skipBits(1);\n }\n bitstream.skipBits(5);\n bitstream.skipBits(5);\n bitstream.skipBits(5);\n bitstream.skipBits(5);\n };\n var iterateHevcNalUnits = (packetData, decoderConfig) => {\n if (decoderConfig.description) {\n const bytes = toUint8Array(decoderConfig.description);\n const lengthSizeMinusOne = bytes[21] & 3;\n const lengthSize = lengthSizeMinusOne + 1;\n return iterateNalUnitsInLengthPrefixed(packetData, lengthSize);\n } else {\n return iterateNalUnitsInAnnexB(packetData);\n }\n };\n var extractNalUnitTypeForHevc = (byte) => {\n return byte >> 1 & 63;\n };\n var parseHevcSps = (sps) => {\n try {\n const bitstream = new Bitstream(removeEmulationPreventionBytes(sps));\n bitstream.skipBits(16);\n bitstream.readBits(4);\n const spsMaxSubLayersMinus1 = bitstream.readBits(3);\n const spsTemporalIdNestingFlag = bitstream.readBits(1);\n const { general_profile_space, general_tier_flag, general_profile_idc, general_profile_compatibility_flags, general_constraint_indicator_flags, general_level_idc } = parseProfileTierLevel(bitstream, spsMaxSubLayersMinus1);\n readExpGolomb(bitstream);\n const chromaFormatIdc = readExpGolomb(bitstream);\n let separateColourPlaneFlag = 0;\n if (chromaFormatIdc === 3) {\n separateColourPlaneFlag = bitstream.readBits(1);\n }\n const picWidthInLumaSamples = readExpGolomb(bitstream);\n const picHeightInLumaSamples = readExpGolomb(bitstream);\n let displayWidth = picWidthInLumaSamples;\n let displayHeight = picHeightInLumaSamples;\n if (bitstream.readBits(1)) {\n const confWinLeftOffset = readExpGolomb(bitstream);\n const confWinRightOffset = readExpGolomb(bitstream);\n const confWinTopOffset = readExpGolomb(bitstream);\n const confWinBottomOffset = readExpGolomb(bitstream);\n let subWidthC = 1;\n let subHeightC = 1;\n const chromaArrayType = separateColourPlaneFlag === 0 ? chromaFormatIdc : 0;\n if (chromaArrayType === 1) {\n subWidthC = 2;\n subHeightC = 2;\n } else if (chromaArrayType === 2) {\n subWidthC = 2;\n subHeightC = 1;\n }\n displayWidth -= (confWinLeftOffset + confWinRightOffset) * subWidthC;\n displayHeight -= (confWinTopOffset + confWinBottomOffset) * subHeightC;\n }\n const bitDepthLumaMinus8 = readExpGolomb(bitstream);\n const bitDepthChromaMinus8 = readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n const spsSubLayerOrderingInfoPresentFlag = bitstream.readBits(1);\n const startI = spsSubLayerOrderingInfoPresentFlag ? 0 : spsMaxSubLayersMinus1;\n let spsMaxNumReorderPics = 0;\n for (let i = startI;i <= spsMaxSubLayersMinus1; i++) {\n readExpGolomb(bitstream);\n spsMaxNumReorderPics = readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n if (bitstream.readBits(1)) {\n if (bitstream.readBits(1)) {\n skipScalingListData(bitstream);\n }\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n if (bitstream.readBits(1)) {\n bitstream.skipBits(4);\n bitstream.skipBits(4);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n bitstream.skipBits(1);\n }\n const numShortTermRefPicSets = readExpGolomb(bitstream);\n skipAllStRefPicSets(bitstream, numShortTermRefPicSets);\n if (bitstream.readBits(1)) {\n const numLongTermRefPicsSps = readExpGolomb(bitstream);\n for (let i = 0;i < numLongTermRefPicsSps; i++) {\n readExpGolomb(bitstream);\n bitstream.skipBits(1);\n }\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n let colourPrimaries = 2;\n let transferCharacteristics = 2;\n let matrixCoefficients = 2;\n let fullRangeFlag = 0;\n let minSpatialSegmentationIdc = 0;\n let pixelAspectRatio = { num: 1, den: 1 };\n if (bitstream.readBits(1)) {\n const vui = parseHevcVui(bitstream, spsMaxSubLayersMinus1);\n pixelAspectRatio = vui.pixelAspectRatio;\n colourPrimaries = vui.colourPrimaries;\n transferCharacteristics = vui.transferCharacteristics;\n matrixCoefficients = vui.matrixCoefficients;\n fullRangeFlag = vui.fullRangeFlag;\n minSpatialSegmentationIdc = vui.minSpatialSegmentationIdc;\n }\n return {\n displayWidth,\n displayHeight,\n pixelAspectRatio,\n colourPrimaries,\n transferCharacteristics,\n matrixCoefficients,\n fullRangeFlag,\n maxDecFrameBuffering: spsMaxNumReorderPics + 1,\n spsMaxSubLayersMinus1,\n spsTemporalIdNestingFlag,\n generalProfileSpace: general_profile_space,\n generalTierFlag: general_tier_flag,\n generalProfileIdc: general_profile_idc,\n generalProfileCompatibilityFlags: general_profile_compatibility_flags,\n generalConstraintIndicatorFlags: general_constraint_indicator_flags,\n generalLevelIdc: general_level_idc,\n chromaFormatIdc,\n bitDepthLumaMinus8,\n bitDepthChromaMinus8,\n minSpatialSegmentationIdc\n };\n } catch (error) {\n Logging._error(\"Error parsing HEVC SPS:\", error);\n return null;\n }\n };\n var extractHevcDecoderConfigurationRecord = (packetData) => {\n try {\n const vpsUnits = [];\n const spsUnits = [];\n const ppsUnits = [];\n const seiUnits = [];\n for (const loc of iterateNalUnitsInAnnexB(packetData)) {\n const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length);\n const type = extractNalUnitTypeForHevc(nalUnit[0]);\n if (type === HevcNalUnitType.VPS_NUT) {\n vpsUnits.push(nalUnit);\n } else if (type === HevcNalUnitType.SPS_NUT) {\n spsUnits.push(nalUnit);\n } else if (type === HevcNalUnitType.PPS_NUT) {\n ppsUnits.push(nalUnit);\n } else if (type === HevcNalUnitType.PREFIX_SEI_NUT || type === HevcNalUnitType.SUFFIX_SEI_NUT) {\n seiUnits.push(nalUnit);\n }\n }\n if (spsUnits.length === 0 || ppsUnits.length === 0)\n return null;\n const spsInfo = parseHevcSps(spsUnits[0]);\n if (!spsInfo)\n return null;\n let parallelismType = 0;\n if (ppsUnits.length > 0) {\n const pps = ppsUnits[0];\n const ppsBitstream = new Bitstream(removeEmulationPreventionBytes(pps));\n ppsBitstream.skipBits(16);\n readExpGolomb(ppsBitstream);\n readExpGolomb(ppsBitstream);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(3);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n readExpGolomb(ppsBitstream);\n readExpGolomb(ppsBitstream);\n readSignedExpGolomb(ppsBitstream);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n if (ppsBitstream.readBits(1)) {\n readExpGolomb(ppsBitstream);\n }\n readSignedExpGolomb(ppsBitstream);\n readSignedExpGolomb(ppsBitstream);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n ppsBitstream.skipBits(1);\n const tiles_enabled_flag = ppsBitstream.readBits(1);\n const entropy_coding_sync_enabled_flag = ppsBitstream.readBits(1);\n if (!tiles_enabled_flag && !entropy_coding_sync_enabled_flag)\n parallelismType = 0;\n else if (tiles_enabled_flag && !entropy_coding_sync_enabled_flag)\n parallelismType = 2;\n else if (!tiles_enabled_flag && entropy_coding_sync_enabled_flag)\n parallelismType = 3;\n else\n parallelismType = 0;\n }\n const arrays = [\n ...vpsUnits.length ? [\n {\n arrayCompleteness: 1,\n nalUnitType: HevcNalUnitType.VPS_NUT,\n nalUnits: vpsUnits\n }\n ] : [],\n ...spsUnits.length ? [\n {\n arrayCompleteness: 1,\n nalUnitType: HevcNalUnitType.SPS_NUT,\n nalUnits: spsUnits\n }\n ] : [],\n ...ppsUnits.length ? [\n {\n arrayCompleteness: 1,\n nalUnitType: HevcNalUnitType.PPS_NUT,\n nalUnits: ppsUnits\n }\n ] : [],\n ...seiUnits.length ? [\n {\n arrayCompleteness: 1,\n nalUnitType: extractNalUnitTypeForHevc(seiUnits[0][0]),\n nalUnits: seiUnits\n }\n ] : []\n ];\n const record = {\n configurationVersion: 1,\n generalProfileSpace: spsInfo.generalProfileSpace,\n generalTierFlag: spsInfo.generalTierFlag,\n generalProfileIdc: spsInfo.generalProfileIdc,\n generalProfileCompatibilityFlags: spsInfo.generalProfileCompatibilityFlags,\n generalConstraintIndicatorFlags: spsInfo.generalConstraintIndicatorFlags,\n generalLevelIdc: spsInfo.generalLevelIdc,\n minSpatialSegmentationIdc: spsInfo.minSpatialSegmentationIdc,\n parallelismType,\n chromaFormatIdc: spsInfo.chromaFormatIdc,\n bitDepthLumaMinus8: spsInfo.bitDepthLumaMinus8,\n bitDepthChromaMinus8: spsInfo.bitDepthChromaMinus8,\n avgFrameRate: 0,\n constantFrameRate: 0,\n numTemporalLayers: spsInfo.spsMaxSubLayersMinus1 + 1,\n temporalIdNested: spsInfo.spsTemporalIdNestingFlag,\n lengthSizeMinusOne: 3,\n arrays\n };\n return record;\n } catch (error) {\n Logging._error(\"Error building HEVC Decoder Configuration Record:\", error);\n return null;\n }\n };\n var parseProfileTierLevel = (bitstream, maxNumSubLayersMinus1) => {\n const general_profile_space = bitstream.readBits(2);\n const general_tier_flag = bitstream.readBits(1);\n const general_profile_idc = bitstream.readBits(5);\n let general_profile_compatibility_flags = 0;\n for (let i = 0;i < 32; i++) {\n general_profile_compatibility_flags = general_profile_compatibility_flags << 1 | bitstream.readBits(1);\n }\n const general_constraint_indicator_flags = new Uint8Array(6);\n for (let i = 0;i < 6; i++) {\n general_constraint_indicator_flags[i] = bitstream.readBits(8);\n }\n const general_level_idc = bitstream.readBits(8);\n const sub_layer_profile_present_flag = [];\n const sub_layer_level_present_flag = [];\n for (let i = 0;i < maxNumSubLayersMinus1; i++) {\n sub_layer_profile_present_flag.push(bitstream.readBits(1));\n sub_layer_level_present_flag.push(bitstream.readBits(1));\n }\n if (maxNumSubLayersMinus1 > 0) {\n for (let i = maxNumSubLayersMinus1;i < 8; i++) {\n bitstream.skipBits(2);\n }\n }\n for (let i = 0;i < maxNumSubLayersMinus1; i++) {\n if (sub_layer_profile_present_flag[i])\n bitstream.skipBits(88);\n if (sub_layer_level_present_flag[i])\n bitstream.skipBits(8);\n }\n return {\n general_profile_space,\n general_tier_flag,\n general_profile_idc,\n general_profile_compatibility_flags,\n general_constraint_indicator_flags,\n general_level_idc\n };\n };\n var skipScalingListData = (bitstream) => {\n for (let sizeId = 0;sizeId < 4; sizeId++) {\n for (let matrixId = 0;matrixId < (sizeId === 3 ? 2 : 6); matrixId++) {\n const scaling_list_pred_mode_flag = bitstream.readBits(1);\n if (!scaling_list_pred_mode_flag) {\n readExpGolomb(bitstream);\n } else {\n const coefNum = Math.min(64, 1 << 4 + (sizeId << 1));\n if (sizeId > 1) {\n readSignedExpGolomb(bitstream);\n }\n for (let i = 0;i < coefNum; i++) {\n readSignedExpGolomb(bitstream);\n }\n }\n }\n }\n };\n var skipAllStRefPicSets = (bitstream, num_short_term_ref_pic_sets) => {\n const NumDeltaPocs = [];\n for (let stRpsIdx = 0;stRpsIdx < num_short_term_ref_pic_sets; stRpsIdx++) {\n NumDeltaPocs[stRpsIdx] = skipStRefPicSet(bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs);\n }\n };\n var skipStRefPicSet = (bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs) => {\n let NumDeltaPocsThis = 0;\n let inter_ref_pic_set_prediction_flag = 0;\n let RefRpsIdx = 0;\n if (stRpsIdx !== 0) {\n inter_ref_pic_set_prediction_flag = bitstream.readBits(1);\n }\n if (inter_ref_pic_set_prediction_flag) {\n if (stRpsIdx === num_short_term_ref_pic_sets) {\n const delta_idx_minus1 = readExpGolomb(bitstream);\n RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1);\n } else {\n RefRpsIdx = stRpsIdx - 1;\n }\n bitstream.readBits(1);\n readExpGolomb(bitstream);\n const numDelta = NumDeltaPocs[RefRpsIdx] ?? 0;\n for (let j = 0;j <= numDelta; j++) {\n const used_by_curr_pic_flag = bitstream.readBits(1);\n if (!used_by_curr_pic_flag) {\n bitstream.readBits(1);\n }\n }\n NumDeltaPocsThis = NumDeltaPocs[RefRpsIdx];\n } else {\n const num_negative_pics = readExpGolomb(bitstream);\n const num_positive_pics = readExpGolomb(bitstream);\n for (let i = 0;i < num_negative_pics; i++) {\n readExpGolomb(bitstream);\n bitstream.readBits(1);\n }\n for (let i = 0;i < num_positive_pics; i++) {\n readExpGolomb(bitstream);\n bitstream.readBits(1);\n }\n NumDeltaPocsThis = num_negative_pics + num_positive_pics;\n }\n return NumDeltaPocsThis;\n };\n var parseHevcVui = (bitstream, sps_max_sub_layers_minus1) => {\n let colourPrimaries = 2;\n let transferCharacteristics = 2;\n let matrixCoefficients = 2;\n let fullRangeFlag = 0;\n let minSpatialSegmentationIdc = 0;\n let pixelAspectRatio = { num: 1, den: 1 };\n if (bitstream.readBits(1)) {\n const aspect_ratio_idc = bitstream.readBits(8);\n if (aspect_ratio_idc === 255) {\n pixelAspectRatio = {\n num: bitstream.readBits(16),\n den: bitstream.readBits(16)\n };\n } else {\n const aspectRatio = AVC_HEVC_ASPECT_RATIO_IDC_TABLE[aspect_ratio_idc];\n if (aspectRatio) {\n pixelAspectRatio = aspectRatio;\n }\n }\n }\n if (bitstream.readBits(1)) {\n bitstream.readBits(1);\n }\n if (bitstream.readBits(1)) {\n bitstream.readBits(3);\n fullRangeFlag = bitstream.readBits(1);\n if (bitstream.readBits(1)) {\n colourPrimaries = bitstream.readBits(8);\n transferCharacteristics = bitstream.readBits(8);\n matrixCoefficients = bitstream.readBits(8);\n }\n }\n if (bitstream.readBits(1)) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n bitstream.readBits(1);\n bitstream.readBits(1);\n bitstream.readBits(1);\n if (bitstream.readBits(1)) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n if (bitstream.readBits(1)) {\n bitstream.readBits(32);\n bitstream.readBits(32);\n if (bitstream.readBits(1)) {\n readExpGolomb(bitstream);\n }\n if (bitstream.readBits(1)) {\n skipHevcHrdParameters(bitstream, true, sps_max_sub_layers_minus1);\n }\n }\n if (bitstream.readBits(1)) {\n bitstream.readBits(1);\n bitstream.readBits(1);\n bitstream.readBits(1);\n minSpatialSegmentationIdc = readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n return {\n pixelAspectRatio,\n colourPrimaries,\n transferCharacteristics,\n matrixCoefficients,\n fullRangeFlag,\n minSpatialSegmentationIdc\n };\n };\n var skipHevcHrdParameters = (bitstream, commonInfPresentFlag, maxNumSubLayersMinus1) => {\n let nal_hrd_parameters_present_flag = false;\n let vcl_hrd_parameters_present_flag = false;\n let sub_pic_hrd_params_present_flag = false;\n if (commonInfPresentFlag) {\n nal_hrd_parameters_present_flag = bitstream.readBits(1) === 1;\n vcl_hrd_parameters_present_flag = bitstream.readBits(1) === 1;\n if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {\n sub_pic_hrd_params_present_flag = bitstream.readBits(1) === 1;\n if (sub_pic_hrd_params_present_flag) {\n bitstream.readBits(8);\n bitstream.readBits(5);\n bitstream.readBits(1);\n bitstream.readBits(5);\n }\n bitstream.readBits(4);\n bitstream.readBits(4);\n if (sub_pic_hrd_params_present_flag) {\n bitstream.readBits(4);\n }\n bitstream.readBits(5);\n bitstream.readBits(5);\n bitstream.readBits(5);\n }\n }\n for (let i = 0;i <= maxNumSubLayersMinus1; i++) {\n const fixed_pic_rate_general_flag = bitstream.readBits(1) === 1;\n let fixed_pic_rate_within_cvs_flag = true;\n if (!fixed_pic_rate_general_flag) {\n fixed_pic_rate_within_cvs_flag = bitstream.readBits(1) === 1;\n }\n let low_delay_hrd_flag = false;\n if (fixed_pic_rate_within_cvs_flag) {\n readExpGolomb(bitstream);\n } else {\n low_delay_hrd_flag = bitstream.readBits(1) === 1;\n }\n let CpbCnt = 1;\n if (!low_delay_hrd_flag) {\n const cpb_cnt_minus1 = readExpGolomb(bitstream);\n CpbCnt = cpb_cnt_minus1 + 1;\n }\n if (nal_hrd_parameters_present_flag) {\n skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag);\n }\n if (vcl_hrd_parameters_present_flag) {\n skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag);\n }\n }\n };\n var skipSubLayerHrdParameters = (bitstream, CpbCnt, sub_pic_hrd_params_present_flag) => {\n for (let i = 0;i < CpbCnt; i++) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n if (sub_pic_hrd_params_present_flag) {\n readExpGolomb(bitstream);\n readExpGolomb(bitstream);\n }\n bitstream.readBits(1);\n }\n };\n var serializeHevcDecoderConfigurationRecord = (record) => {\n const bytes = [];\n bytes.push(record.configurationVersion);\n bytes.push((record.generalProfileSpace & 3) << 6 | (record.generalTierFlag & 1) << 5 | record.generalProfileIdc & 31);\n bytes.push(record.generalProfileCompatibilityFlags >>> 24 & 255);\n bytes.push(record.generalProfileCompatibilityFlags >>> 16 & 255);\n bytes.push(record.generalProfileCompatibilityFlags >>> 8 & 255);\n bytes.push(record.generalProfileCompatibilityFlags & 255);\n bytes.push(...record.generalConstraintIndicatorFlags);\n bytes.push(record.generalLevelIdc & 255);\n bytes.push(240 | record.minSpatialSegmentationIdc >> 8 & 15);\n bytes.push(record.minSpatialSegmentationIdc & 255);\n bytes.push(252 | record.parallelismType & 3);\n bytes.push(252 | record.chromaFormatIdc & 3);\n bytes.push(248 | record.bitDepthLumaMinus8 & 7);\n bytes.push(248 | record.bitDepthChromaMinus8 & 7);\n bytes.push(record.avgFrameRate >> 8 & 255);\n bytes.push(record.avgFrameRate & 255);\n bytes.push((record.constantFrameRate & 3) << 6 | (record.numTemporalLayers & 7) << 3 | (record.temporalIdNested & 1) << 2 | record.lengthSizeMinusOne & 3);\n bytes.push(record.arrays.length & 255);\n for (const arr of record.arrays) {\n bytes.push((arr.arrayCompleteness & 1) << 7 | 0 << 6 | arr.nalUnitType & 63);\n bytes.push(arr.nalUnits.length >> 8 & 255);\n bytes.push(arr.nalUnits.length & 255);\n for (const nal of arr.nalUnits) {\n bytes.push(nal.length >> 8 & 255);\n bytes.push(nal.length & 255);\n for (let i = 0;i < nal.length; i++) {\n bytes.push(nal[i]);\n }\n }\n }\n return new Uint8Array(bytes);\n };\n var HevcNaluOrderState;\n (function(HevcNaluOrderState) {\n HevcNaluOrderState[HevcNaluOrderState[\"audAllowed\"] = 0] = \"audAllowed\";\n HevcNaluOrderState[HevcNaluOrderState[\"beforeFirstVcl\"] = 1] = \"beforeFirstVcl\";\n HevcNaluOrderState[HevcNaluOrderState[\"afterFirstVcl\"] = 2] = \"afterFirstVcl\";\n HevcNaluOrderState[HevcNaluOrderState[\"eoBitstreamAllowed\"] = 3] = \"eoBitstreamAllowed\";\n HevcNaluOrderState[HevcNaluOrderState[\"noMoreDataAllowed\"] = 4] = \"noMoreDataAllowed\";\n })(HevcNaluOrderState || (HevcNaluOrderState = {}));\n var VP9_COLOR_SPACE_TABLE = {\n 1: { colourPrimaries: 5, transferCharacteristics: 6, matrixCoefficients: 5 },\n 2: { colourPrimaries: 1, transferCharacteristics: 1, matrixCoefficients: 1 },\n 3: { colourPrimaries: 6, transferCharacteristics: 6, matrixCoefficients: 6 },\n 4: { colourPrimaries: 7, transferCharacteristics: 7, matrixCoefficients: 7 },\n 5: { colourPrimaries: 9, transferCharacteristics: 14, matrixCoefficients: 9 },\n 7: { colourPrimaries: 1, transferCharacteristics: 13, matrixCoefficients: 0 }\n };\n var extractVp9CodecInfoFromPacket = (packet) => {\n const bitstream = new Bitstream(packet);\n const frameMarker = bitstream.readBits(2);\n if (frameMarker !== 2) {\n return null;\n }\n const profileLowBit = bitstream.readBits(1);\n const profileHighBit = bitstream.readBits(1);\n const profile = (profileHighBit << 1) + profileLowBit;\n if (profile === 3) {\n bitstream.skipBits(1);\n }\n const showExistingFrame = bitstream.readBits(1);\n if (showExistingFrame === 1) {\n return null;\n }\n const frameType = bitstream.readBits(1);\n if (frameType !== 0) {\n return null;\n }\n bitstream.skipBits(2);\n const syncCode = bitstream.readBits(24);\n if (syncCode !== 4817730) {\n return null;\n }\n let bitDepth = 8;\n if (profile >= 2) {\n const tenOrTwelveBit = bitstream.readBits(1);\n bitDepth = tenOrTwelveBit ? 12 : 10;\n }\n const colorSpace = bitstream.readBits(3);\n let chromaSubsampling = 0;\n let videoFullRangeFlag = 0;\n if (colorSpace !== 7) {\n const colorRange = bitstream.readBits(1);\n videoFullRangeFlag = colorRange;\n if (profile === 1 || profile === 3) {\n const subsamplingX = bitstream.readBits(1);\n const subsamplingY = bitstream.readBits(1);\n chromaSubsampling = !subsamplingX && !subsamplingY ? 3 : subsamplingX && !subsamplingY ? 2 : 1;\n bitstream.skipBits(1);\n } else {\n chromaSubsampling = 1;\n }\n } else {\n chromaSubsampling = 3;\n videoFullRangeFlag = 1;\n }\n const widthMinusOne = bitstream.readBits(16);\n const heightMinusOne = bitstream.readBits(16);\n const width = widthMinusOne + 1;\n const height = heightMinusOne + 1;\n const pictureSize = width * height;\n let level = last(VP9_LEVEL_TABLE).level;\n for (const entry of VP9_LEVEL_TABLE) {\n if (pictureSize <= entry.maxPictureSize) {\n level = entry.level;\n break;\n }\n }\n const colorSpaceValues = VP9_COLOR_SPACE_TABLE[colorSpace];\n const colourPrimaries = colorSpaceValues?.colourPrimaries ?? 2;\n const transferCharacteristics = colorSpaceValues?.transferCharacteristics ?? 2;\n const matrixCoefficients = colorSpaceValues?.matrixCoefficients ?? 2;\n return {\n profile,\n level,\n bitDepth,\n chromaSubsampling,\n videoFullRangeFlag,\n colourPrimaries,\n transferCharacteristics,\n matrixCoefficients\n };\n };\n var iterateAv1PacketObus = function* (packet) {\n const bitstream = new Bitstream(packet);\n const readLeb128 = () => {\n let value = 0;\n for (let i = 0;i < 8; i++) {\n const byte = bitstream.readAlignedByte();\n value += (byte & 127) * 2 ** (i * 7);\n if (!(byte & 128)) {\n break;\n }\n if (i === 7 && byte & 128) {\n return null;\n }\n }\n if (value > 2 ** 32 - 1) {\n return null;\n }\n return value;\n };\n while (bitstream.getBitsLeft() >= 8) {\n bitstream.skipBits(1);\n const obuType = bitstream.readBits(4);\n const obuExtension = bitstream.readBits(1);\n const obuHasSizeField = bitstream.readBits(1);\n bitstream.skipBits(1);\n if (obuExtension) {\n bitstream.skipBits(8);\n }\n let obuSize;\n if (obuHasSizeField) {\n const obuSizeValue = readLeb128();\n if (obuSizeValue === null)\n return;\n obuSize = obuSizeValue;\n } else {\n obuSize = Math.floor(bitstream.getBitsLeft() / 8);\n }\n assert(bitstream.pos % 8 === 0);\n yield {\n type: obuType,\n data: packet.subarray(bitstream.pos / 8, bitstream.pos / 8 + obuSize)\n };\n bitstream.skipBits(obuSize * 8);\n }\n };\n var extractAv1CodecInfoFromPacket = (packet) => {\n for (const { type, data } of iterateAv1PacketObus(packet)) {\n if (type !== 1) {\n continue;\n }\n const bitstream = new Bitstream(data);\n const seqProfile = bitstream.readBits(3);\n const stillPicture = bitstream.readBits(1);\n const reducedStillPictureHeader = bitstream.readBits(1);\n let seqLevel = 0;\n let seqTier = 0;\n let bufferDelayLengthMinus1 = 0;\n if (reducedStillPictureHeader) {\n seqLevel = bitstream.readBits(5);\n } else {\n const timingInfoPresentFlag = bitstream.readBits(1);\n let decoderModelInfoPresentFlag = 0;\n if (timingInfoPresentFlag) {\n bitstream.skipBits(32);\n bitstream.skipBits(32);\n const equalPictureInterval = bitstream.readBits(1);\n if (equalPictureInterval) {\n let leadingZeros = 0;\n while (leadingZeros < 32 && !bitstream.readBits(1)) {\n leadingZeros++;\n }\n if (leadingZeros < 32) {\n bitstream.skipBits(leadingZeros);\n }\n }\n decoderModelInfoPresentFlag = bitstream.readBits(1);\n if (decoderModelInfoPresentFlag) {\n bufferDelayLengthMinus1 = bitstream.readBits(5);\n bitstream.skipBits(32);\n bitstream.skipBits(5);\n bitstream.skipBits(5);\n }\n }\n const initialDisplayDelayPresentFlag = bitstream.readBits(1);\n const operatingPointsCntMinus1 = bitstream.readBits(5);\n for (let i = 0;i <= operatingPointsCntMinus1; i++) {\n bitstream.skipBits(12);\n const seqLevelIdx = bitstream.readBits(5);\n if (i === 0) {\n seqLevel = seqLevelIdx;\n }\n if (seqLevelIdx > 7) {\n const seqTierTemp = bitstream.readBits(1);\n if (i === 0) {\n seqTier = seqTierTemp;\n }\n }\n if (decoderModelInfoPresentFlag) {\n const decoderModelPresentForThisOp = bitstream.readBits(1);\n if (decoderModelPresentForThisOp) {\n const n = bufferDelayLengthMinus1 + 1;\n bitstream.skipBits(n);\n bitstream.skipBits(n);\n bitstream.skipBits(1);\n }\n }\n if (initialDisplayDelayPresentFlag) {\n const initialDisplayDelayPresentForThisOp = bitstream.readBits(1);\n if (initialDisplayDelayPresentForThisOp) {\n bitstream.skipBits(4);\n }\n }\n }\n }\n const frameWidthBitsMinus1 = bitstream.readBits(4);\n const frameHeightBitsMinus1 = bitstream.readBits(4);\n const n1 = frameWidthBitsMinus1 + 1;\n bitstream.skipBits(n1);\n const n2 = frameHeightBitsMinus1 + 1;\n bitstream.skipBits(n2);\n let frameIdNumbersPresentFlag = 0;\n if (reducedStillPictureHeader) {\n frameIdNumbersPresentFlag = 0;\n } else {\n frameIdNumbersPresentFlag = bitstream.readBits(1);\n }\n if (frameIdNumbersPresentFlag) {\n bitstream.skipBits(4);\n bitstream.skipBits(3);\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n if (!reducedStillPictureHeader) {\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n const enableOrderHint = bitstream.readBits(1);\n if (enableOrderHint) {\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n }\n const seqChooseScreenContentTools = bitstream.readBits(1);\n let seqForceScreenContentTools = 0;\n if (seqChooseScreenContentTools) {\n seqForceScreenContentTools = 2;\n } else {\n seqForceScreenContentTools = bitstream.readBits(1);\n }\n if (seqForceScreenContentTools > 0) {\n const seqChooseIntegerMv = bitstream.readBits(1);\n if (!seqChooseIntegerMv) {\n bitstream.skipBits(1);\n }\n }\n if (enableOrderHint) {\n bitstream.skipBits(3);\n }\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n const highBitdepth = bitstream.readBits(1);\n let bitDepth = 8;\n if (seqProfile === 2 && highBitdepth) {\n const twelveBit = bitstream.readBits(1);\n bitDepth = twelveBit ? 12 : 10;\n } else if (seqProfile <= 2) {\n bitDepth = highBitdepth ? 10 : 8;\n }\n let monochrome = 0;\n if (seqProfile !== 1) {\n monochrome = bitstream.readBits(1);\n }\n let colourPrimaries = 2;\n let transferCharacteristics = 2;\n let matrixCoefficients = 2;\n const colorDescriptionPresentFlag = bitstream.readBits(1);\n if (colorDescriptionPresentFlag) {\n colourPrimaries = bitstream.readBits(8);\n transferCharacteristics = bitstream.readBits(8);\n matrixCoefficients = bitstream.readBits(8);\n }\n let videoFullRangeFlag = 0;\n let chromaSubsamplingX = 1;\n let chromaSubsamplingY = 1;\n let chromaSamplePosition = 0;\n if (monochrome) {\n videoFullRangeFlag = bitstream.readBits(1);\n } else if (colourPrimaries === 1 && transferCharacteristics === 13 && matrixCoefficients === 0) {\n videoFullRangeFlag = 1;\n chromaSubsamplingX = 0;\n chromaSubsamplingY = 0;\n } else {\n videoFullRangeFlag = bitstream.readBits(1);\n if (seqProfile === 0) {\n chromaSubsamplingX = 1;\n chromaSubsamplingY = 1;\n } else if (seqProfile === 1) {\n chromaSubsamplingX = 0;\n chromaSubsamplingY = 0;\n } else {\n if (bitDepth === 12) {\n chromaSubsamplingX = bitstream.readBits(1);\n chromaSubsamplingY = chromaSubsamplingX ? bitstream.readBits(1) : 0;\n } else {\n chromaSubsamplingX = 1;\n chromaSubsamplingY = 0;\n }\n }\n if (chromaSubsamplingX && chromaSubsamplingY) {\n chromaSamplePosition = bitstream.readBits(2);\n }\n }\n return {\n profile: seqProfile,\n level: seqLevel,\n tier: seqTier,\n bitDepth,\n monochrome,\n chromaSubsamplingX,\n chromaSubsamplingY,\n chromaSamplePosition,\n videoFullRangeFlag,\n colourPrimaries,\n transferCharacteristics,\n matrixCoefficients\n };\n }\n return null;\n };\n var av1CodecInfoHasColorInfo = (info) => {\n return info.colourPrimaries !== 2 || info.transferCharacteristics !== 2 || info.matrixCoefficients !== 2;\n };\n var parseOpusIdentificationHeader = (bytes) => {\n const view = toDataView(bytes);\n const outputChannelCount = view.getUint8(9);\n const preSkip = view.getUint16(10, true);\n const inputSampleRate = view.getUint32(12, true);\n const outputGain = view.getInt16(16, true);\n const channelMappingFamily = view.getUint8(18);\n let channelMappingTable = null;\n if (channelMappingFamily) {\n channelMappingTable = bytes.subarray(19, 19 + 2 + outputChannelCount);\n }\n return {\n outputChannelCount,\n preSkip,\n inputSampleRate,\n outputGain,\n channelMappingFamily,\n channelMappingTable\n };\n };\n var determineVideoPacketType = (codec, decoderConfig, packetData) => {\n switch (codec) {\n case \"avc\":\n {\n for (const loc of iterateAvcNalUnits(packetData, decoderConfig)) {\n const nalTypeByte = packetData[loc.offset];\n const type = extractNalUnitTypeForAvc(nalTypeByte);\n if (type >= AvcNalUnitType.NON_IDR_SLICE && type <= AvcNalUnitType.SLICE_DPC) {\n return \"delta\";\n }\n if (type === AvcNalUnitType.IDR) {\n return \"key\";\n }\n if (type === AvcNalUnitType.SEI && !isChromium()) {\n const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length);\n const bytes = removeEmulationPreventionBytes(nalUnit);\n let pos = 1;\n do {\n let payloadType = 0;\n while (true) {\n const nextByte = bytes[pos++];\n if (nextByte === undefined)\n break;\n payloadType += nextByte;\n if (nextByte < 255) {\n break;\n }\n }\n let payloadSize = 0;\n while (true) {\n const nextByte = bytes[pos++];\n if (nextByte === undefined)\n break;\n payloadSize += nextByte;\n if (nextByte < 255) {\n break;\n }\n }\n const PAYLOAD_TYPE_RECOVERY_POINT = 6;\n if (payloadType === PAYLOAD_TYPE_RECOVERY_POINT) {\n const bitstream = new Bitstream(bytes);\n bitstream.pos = 8 * pos;\n const recoveryFrameCount = readExpGolomb(bitstream);\n const exactMatchFlag = bitstream.readBits(1);\n if (recoveryFrameCount === 0 && exactMatchFlag === 1) {\n return \"key\";\n }\n }\n pos += payloadSize;\n } while (pos < bytes.length - 1);\n }\n }\n return \"delta\";\n }\n ;\n case \"hevc\":\n {\n for (const loc of iterateHevcNalUnits(packetData, decoderConfig)) {\n const type = extractNalUnitTypeForHevc(packetData[loc.offset]);\n if (type < HevcNalUnitType.BLA_W_LP) {\n return \"delta\";\n }\n if (type <= HevcNalUnitType.RSV_IRAP_VCL23) {\n return \"key\";\n }\n }\n return \"delta\";\n }\n ;\n case \"vp8\":\n {\n const frameType = packetData[0] & 1;\n return frameType === 0 ? \"key\" : \"delta\";\n }\n ;\n case \"vp9\":\n {\n const bitstream = new Bitstream(packetData);\n if (bitstream.readBits(2) !== 2) {\n return null;\n }\n const profileLowBit = bitstream.readBits(1);\n const profileHighBit = bitstream.readBits(1);\n const profile = (profileHighBit << 1) + profileLowBit;\n if (profile === 3) {\n bitstream.skipBits(1);\n }\n const showExistingFrame = bitstream.readBits(1);\n if (showExistingFrame) {\n return null;\n }\n const frameType = bitstream.readBits(1);\n return frameType === 0 ? \"key\" : \"delta\";\n }\n ;\n case \"av1\":\n {\n let reducedStillPictureHeader = false;\n for (const { type, data } of iterateAv1PacketObus(packetData)) {\n if (type === 1) {\n const bitstream = new Bitstream(data);\n bitstream.skipBits(4);\n reducedStillPictureHeader = !!bitstream.readBits(1);\n } else if (type === 3 || type === 6 || type === 7) {\n if (reducedStillPictureHeader) {\n return \"key\";\n }\n const bitstream = new Bitstream(data);\n const showExistingFrame = bitstream.readBits(1);\n if (showExistingFrame) {\n return null;\n }\n const frameType = bitstream.readBits(2);\n return frameType === 0 ? \"key\" : \"delta\";\n }\n }\n return null;\n }\n ;\n case \"prores\":\n {\n return \"key\";\n }\n ;\n default:\n {\n assertNever(codec);\n assert(false);\n }\n ;\n }\n };\n var FlacBlockType;\n (function(FlacBlockType) {\n FlacBlockType[FlacBlockType[\"STREAMINFO\"] = 0] = \"STREAMINFO\";\n FlacBlockType[FlacBlockType[\"VORBIS_COMMENT\"] = 4] = \"VORBIS_COMMENT\";\n FlacBlockType[FlacBlockType[\"PICTURE\"] = 6] = \"PICTURE\";\n })(FlacBlockType || (FlacBlockType = {}));\n var parseAc3SyncFrame = (data) => {\n if (data.length < 7) {\n return null;\n }\n if (data[0] !== 11 || data[1] !== 119) {\n return null;\n }\n const bitstream = new Bitstream(data);\n bitstream.skipBits(16);\n bitstream.skipBits(16);\n const fscod = bitstream.readBits(2);\n if (fscod === 3) {\n return null;\n }\n const frmsizecod = bitstream.readBits(6);\n const bsid = bitstream.readBits(5);\n if (bsid > 8) {\n return null;\n }\n const bsmod = bitstream.readBits(3);\n const acmod = bitstream.readBits(3);\n if ((acmod & 1) !== 0 && acmod !== 1) {\n bitstream.skipBits(2);\n }\n if ((acmod & 4) !== 0) {\n bitstream.skipBits(2);\n }\n if (acmod === 2) {\n bitstream.skipBits(2);\n }\n const lfeon = bitstream.readBits(1);\n const bitRateCode = Math.floor(frmsizecod / 2);\n return { fscod, bsid, bsmod, acmod, lfeon, bitRateCode };\n };\n var AC3_FRAME_SIZES = [\n 64 * 2,\n 69 * 2,\n 96 * 2,\n 64 * 2,\n 70 * 2,\n 96 * 2,\n 80 * 2,\n 87 * 2,\n 120 * 2,\n 80 * 2,\n 88 * 2,\n 120 * 2,\n 96 * 2,\n 104 * 2,\n 144 * 2,\n 96 * 2,\n 105 * 2,\n 144 * 2,\n 112 * 2,\n 121 * 2,\n 168 * 2,\n 112 * 2,\n 122 * 2,\n 168 * 2,\n 128 * 2,\n 139 * 2,\n 192 * 2,\n 128 * 2,\n 140 * 2,\n 192 * 2,\n 160 * 2,\n 174 * 2,\n 240 * 2,\n 160 * 2,\n 175 * 2,\n 240 * 2,\n 192 * 2,\n 208 * 2,\n 288 * 2,\n 192 * 2,\n 209 * 2,\n 288 * 2,\n 224 * 2,\n 243 * 2,\n 336 * 2,\n 224 * 2,\n 244 * 2,\n 336 * 2,\n 256 * 2,\n 278 * 2,\n 384 * 2,\n 256 * 2,\n 279 * 2,\n 384 * 2,\n 320 * 2,\n 348 * 2,\n 480 * 2,\n 320 * 2,\n 349 * 2,\n 480 * 2,\n 384 * 2,\n 417 * 2,\n 576 * 2,\n 384 * 2,\n 418 * 2,\n 576 * 2,\n 448 * 2,\n 487 * 2,\n 672 * 2,\n 448 * 2,\n 488 * 2,\n 672 * 2,\n 512 * 2,\n 557 * 2,\n 768 * 2,\n 512 * 2,\n 558 * 2,\n 768 * 2,\n 640 * 2,\n 696 * 2,\n 960 * 2,\n 640 * 2,\n 697 * 2,\n 960 * 2,\n 768 * 2,\n 835 * 2,\n 1152 * 2,\n 768 * 2,\n 836 * 2,\n 1152 * 2,\n 896 * 2,\n 975 * 2,\n 1344 * 2,\n 896 * 2,\n 976 * 2,\n 1344 * 2,\n 1024 * 2,\n 1114 * 2,\n 1536 * 2,\n 1024 * 2,\n 1115 * 2,\n 1536 * 2,\n 1152 * 2,\n 1253 * 2,\n 1728 * 2,\n 1152 * 2,\n 1254 * 2,\n 1728 * 2,\n 1280 * 2,\n 1393 * 2,\n 1920 * 2,\n 1280 * 2,\n 1394 * 2,\n 1920 * 2\n ];\n var AC3_REGISTRATION_DESCRIPTOR = new Uint8Array([5, 4, 65, 67, 45, 51]);\n var EAC3_REGISTRATION_DESCRIPTOR = new Uint8Array([5, 4, 69, 65, 67, 51]);\n var EAC3_NUMBLKS_TABLE = [1, 2, 3, 6];\n var parseEac3SyncFrame = (data) => {\n if (data.length < 6) {\n return null;\n }\n if (data[0] !== 11 || data[1] !== 119) {\n return null;\n }\n const bitstream = new Bitstream(data);\n bitstream.skipBits(16);\n const strmtyp = bitstream.readBits(2);\n bitstream.skipBits(3);\n if (strmtyp !== 0 && strmtyp !== 2) {\n return null;\n }\n const frmsiz = bitstream.readBits(11);\n const fscod = bitstream.readBits(2);\n let fscod2 = 0;\n let numblkscod;\n if (fscod === 3) {\n fscod2 = bitstream.readBits(2);\n numblkscod = 3;\n } else {\n numblkscod = bitstream.readBits(2);\n }\n const acmod = bitstream.readBits(3);\n const lfeon = bitstream.readBits(1);\n const bsid = bitstream.readBits(5);\n if (bsid < 11 || bsid > 16) {\n return null;\n }\n const numblks = EAC3_NUMBLKS_TABLE[numblkscod];\n let fs;\n if (fscod < 3) {\n fs = AC3_SAMPLE_RATES[fscod] / 1000;\n } else {\n fs = EAC3_REDUCED_SAMPLE_RATES[fscod2] / 1000;\n }\n const dataRate = Math.round((frmsiz + 1) * fs / (numblks * 16));\n const bsmod = 0;\n const numDepSub = 0;\n const chanLoc = 0;\n const substream = {\n fscod,\n fscod2,\n bsid,\n bsmod,\n acmod,\n lfeon,\n numDepSub,\n chanLoc\n };\n return {\n dataRate,\n substreams: [substream]\n };\n };\n var DTS_EXSS_SYNC_WORD = 1683496997;\n var DTS_CORE_FRAME_HEADER_SIZE = 18;\n var DTS_EXSS_HEADER_PREFIX_SIZE = 10;\n var DTS_PCM_BLOCK_SAMPLES = 32;\n var DTS_SPECIFIC_BOX_SIZE = 20;\n var DTS_SUBBAND_SAMPLES = 8;\n var DTS_CORE_SAMPLE_RATES = [\n 0,\n 8000,\n 16000,\n 32000,\n 0,\n 0,\n 11025,\n 22050,\n 44100,\n 0,\n 0,\n 12000,\n 24000,\n 48000,\n 96000,\n 192000\n ];\n var DTS_CORE_BIT_RATES = [\n 32000,\n 56000,\n 64000,\n 96000,\n 112000,\n 128000,\n 192000,\n 224000,\n 256000,\n 320000,\n 384000,\n 448000,\n 512000,\n 576000,\n 640000,\n 768000,\n 960000,\n 1024000,\n 1152000,\n 1280000,\n 1344000,\n 1408000,\n 1411200,\n 1472000,\n 1536000,\n 1920000,\n 2048000,\n 3072000,\n 3840000,\n 0,\n 0,\n 0\n ];\n var DTS_PCM_RESOLUTIONS = [16, 16, 20, 20, 0, 24, 24, 0];\n var DTS_AMODE_CHANNEL_COUNTS = [1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8];\n var DTS_AMODE_CHANNEL_LAYOUTS = [\n 1,\n 2,\n 2,\n 2,\n 2,\n 3,\n 18,\n 19,\n 6,\n 7,\n 518,\n 323,\n 83,\n 519,\n 582,\n 535\n ];\n var DTS_CHANNEL_LAYOUT_LFE1 = 8;\n var DTS_EXSS_REF_CLOCKS = [32000, 44100, 48000, 0];\n var DTS_EXSS_SAMPLE_RATES = [\n 8000,\n 16000,\n 32000,\n 64000,\n 128000,\n 22050,\n 44100,\n 88200,\n 176400,\n 352800,\n 12000,\n 24000,\n 48000,\n 96000,\n 192000,\n 384000\n ];\n var DTS_SPECIFIC_BOX_FRAME_DURATIONS = [512, 1024, 2048, 4096];\n var parseDtsFrame = (data) => {\n const core = parseDtsCoreFrameHeader(data);\n const view = toDataView(data);\n let offset = core ? Math.ceil(core.frameSize / 4) * 4 : 0;\n let firstExss = null;\n while (offset + 4 <= data.length && view.getUint32(offset) === DTS_EXSS_SYNC_WORD) {\n const exss = parseDtsExssHeader(data.subarray(offset));\n if (!exss) {\n break;\n }\n firstExss ??= exss;\n offset += exss.frameSize;\n }\n if (core) {\n return {\n frameSize: firstExss ? offset : core.frameSize,\n sampleRate: core.sampleRate,\n numberOfChannels: core.numberOfChannels,\n sampleCount: core.sampleCount,\n channelLayout: core.channelLayout,\n pcmResolution: core.pcmResolution,\n bitRate: core.bitRate,\n core,\n hasExtensions: firstExss !== null\n };\n }\n if (!firstExss?.asset) {\n return null;\n }\n const { asset } = firstExss;\n return {\n frameSize: offset,\n sampleRate: asset.sampleRate,\n numberOfChannels: asset.numberOfChannels,\n sampleCount: asset.sampleCount,\n channelLayout: asset.channelLayout,\n pcmResolution: asset.pcmResolution,\n bitRate: 0,\n core: null,\n hasExtensions: true\n };\n };\n var parseDtsCoreFrameHeader = (data) => {\n if (data.length < DTS_CORE_FRAME_HEADER_SIZE) {\n return null;\n }\n if (data[0] !== 127 || data[1] !== 254 || data[2] !== 128 || data[3] !== 1) {\n return null;\n }\n const bitstream = new Bitstream(data);\n bitstream.skipBits(32);\n bitstream.skipBits(1);\n if (bitstream.readBits(5) !== DTS_PCM_BLOCK_SAMPLES - 1) {\n return null;\n }\n const cpf = bitstream.readBits(1);\n const npcmblocks = bitstream.readBits(7) + 1;\n if (npcmblocks % DTS_SUBBAND_SAMPLES !== 0) {\n return null;\n }\n const frameSize = bitstream.readBits(14) + 1;\n if (frameSize < 96) {\n return null;\n }\n const amode = bitstream.readBits(6);\n if (amode >= DTS_AMODE_CHANNEL_COUNTS.length) {\n return null;\n }\n const sampleRate = DTS_CORE_SAMPLE_RATES[bitstream.readBits(4)];\n if (sampleRate === 0) {\n return null;\n }\n const bitRate = DTS_CORE_BIT_RATES[bitstream.readBits(5)];\n if (bitstream.readBits(1) !== 0) {\n return null;\n }\n bitstream.skipBits(1 + 1 + 1 + 1);\n bitstream.skipBits(3 + 1 + 1);\n const lff = bitstream.readBits(2);\n if (lff === 3) {\n return null;\n }\n bitstream.skipBits(1);\n if (cpf) {\n bitstream.skipBits(16);\n }\n bitstream.skipBits(1 + 4 + 2);\n const pcmResolution = DTS_PCM_RESOLUTIONS[bitstream.readBits(3)];\n if (pcmResolution === 0) {\n return null;\n }\n const lfePresent = lff !== 0;\n return {\n frameSize,\n sampleRate,\n numberOfChannels: DTS_AMODE_CHANNEL_COUNTS[amode] + (lfePresent ? 1 : 0),\n sampleCount: npcmblocks * DTS_PCM_BLOCK_SAMPLES,\n channelLayout: DTS_AMODE_CHANNEL_LAYOUTS[amode] | (lfePresent ? DTS_CHANNEL_LAYOUT_LFE1 : 0),\n amode,\n lfePresent,\n bitRate,\n pcmResolution\n };\n };\n var parseDtsExssHeader = (data) => {\n if (data.length < DTS_EXSS_HEADER_PREFIX_SIZE) {\n return null;\n }\n if (data[0] !== 100 || data[1] !== 88 || data[2] !== 32 || data[3] !== 37) {\n return null;\n }\n const bitstream = new Bitstream(data);\n bitstream.skipBits(32);\n bitstream.skipBits(8);\n const extSsIndex = bitstream.readBits(2);\n const wideHeader = bitstream.readBits(1);\n const headerSizeBits = 8 + 4 * wideHeader;\n const frameSizeBits = 16 + 4 * wideHeader;\n bitstream.skipBits(headerSizeBits);\n const frameSize = bitstream.readBits(frameSizeBits) + 1;\n const incomplete = { frameSize, asset: null };\n if (!bitstream.readBits(1)) {\n return incomplete;\n }\n const refClock = DTS_EXSS_REF_CLOCKS[bitstream.readBits(2)];\n const frameDurationCycles = 512 * (bitstream.readBits(3) + 1);\n if (bitstream.readBits(1)) {\n bitstream.skipBits(32 + 4);\n }\n const numAudioPresentations = bitstream.readBits(3) + 1;\n const numAssets = bitstream.readBits(3) + 1;\n const activeExssMasks = [];\n for (let i = 0;i < numAudioPresentations; i++) {\n activeExssMasks.push(bitstream.readBits(extSsIndex + 1));\n }\n for (const mask of activeExssMasks) {\n bitstream.skipBits(8 * popcount(mask));\n }\n if (bitstream.readBits(1)) {\n bitstream.skipBits(2);\n const spkrMaskBits = bitstream.readBits(2) + 1 << 2;\n const numMixOutConfigs = bitstream.readBits(2) + 1;\n bitstream.skipBits(numMixOutConfigs * spkrMaskBits);\n }\n for (let i = 0;i < numAssets; i++) {\n bitstream.skipBits(frameSizeBits);\n }\n bitstream.skipBits(9);\n bitstream.skipBits(3);\n if (bitstream.readBits(1)) {\n bitstream.skipBits(4);\n }\n if (bitstream.readBits(1)) {\n bitstream.skipBits(24);\n }\n if (bitstream.readBits(1)) {\n bitstream.skipBits(8 * (bitstream.readBits(10) + 1));\n }\n const pcmResolution = bitstream.readBits(5) + 1;\n const sampleRate = DTS_EXSS_SAMPLE_RATES[bitstream.readBits(4)];\n const numberOfChannels = bitstream.readBits(8) + 1;\n let channelLayout = 0;\n if (bitstream.readBits(1)) {\n if (numberOfChannels > 2) {\n bitstream.skipBits(1);\n }\n if (numberOfChannels > 6) {\n bitstream.skipBits(1);\n }\n if (bitstream.readBits(1)) {\n const spkrMaskBits = bitstream.readBits(2) + 1 << 2;\n channelLayout = bitstream.readBits(spkrMaskBits);\n }\n }\n if (refClock === 0 || bitstream.getBitsLeft() < 0) {\n return incomplete;\n }\n return {\n frameSize,\n asset: {\n sampleRate,\n numberOfChannels,\n sampleCount: Math.round(frameDurationCycles * sampleRate / refClock),\n channelLayout,\n pcmResolution\n }\n };\n };\n var buildDtsSpecificBox = (frameInfo) => {\n const bytes = new Uint8Array(DTS_SPECIFIC_BOX_SIZE);\n const view = toDataView(bytes);\n view.setUint32(0, frameInfo.sampleRate);\n view.setUint32(4, frameInfo.bitRate);\n view.setUint32(8, frameInfo.bitRate);\n bytes[12] = frameInfo.pcmResolution;\n const streamConstruction = frameInfo.core && !frameInfo.hasExtensions ? 1 : 0;\n const bitstream = new Bitstream(bytes);\n bitstream.seekToByte(13);\n bitstream.writeBits(2, Math.max(DTS_SPECIFIC_BOX_FRAME_DURATIONS.indexOf(frameInfo.sampleCount), 0));\n bitstream.writeBits(5, streamConstruction);\n bitstream.writeBits(1, frameInfo.core?.lfePresent ? 1 : 0);\n bitstream.writeBits(6, frameInfo.core?.amode ?? 0);\n bitstream.writeBits(14, frameInfo.core ? frameInfo.core.frameSize - 1 : 0);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(3, 0);\n bitstream.writeBits(16, frameInfo.channelLayout);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(5, 0);\n return bytes;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/codec.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var VIDEO_CODECS = [\n \"avc\",\n \"hevc\",\n \"vp9\",\n \"av1\",\n \"vp8\",\n \"prores\"\n ];\n var PCM_AUDIO_CODECS = [\n \"pcm-s16\",\n \"pcm-s16be\",\n \"pcm-s24\",\n \"pcm-s24be\",\n \"pcm-s32\",\n \"pcm-s32be\",\n \"pcm-f32\",\n \"pcm-f32be\",\n \"pcm-f64\",\n \"pcm-f64be\",\n \"pcm-u8\",\n \"pcm-s8\",\n \"ulaw\",\n \"alaw\"\n ];\n var NON_PCM_AUDIO_CODECS = [\n \"aac\",\n \"opus\",\n \"mp3\",\n \"vorbis\",\n \"flac\",\n \"ac3\",\n \"eac3\",\n \"dts\"\n ];\n var AUDIO_CODECS = [\n ...NON_PCM_AUDIO_CODECS,\n ...PCM_AUDIO_CODECS\n ];\n var SUBTITLE_CODECS = [\n \"webvtt\"\n ];\n var AVC_LEVEL_TABLE = [\n { maxMacroblocks: 99, maxBitrate: 64000, maxDpbMbs: 396, level: 10 },\n { maxMacroblocks: 396, maxBitrate: 192000, maxDpbMbs: 900, level: 11 },\n { maxMacroblocks: 396, maxBitrate: 384000, maxDpbMbs: 2376, level: 12 },\n { maxMacroblocks: 396, maxBitrate: 768000, maxDpbMbs: 2376, level: 13 },\n { maxMacroblocks: 396, maxBitrate: 2000000, maxDpbMbs: 2376, level: 20 },\n { maxMacroblocks: 792, maxBitrate: 4000000, maxDpbMbs: 4752, level: 21 },\n { maxMacroblocks: 1620, maxBitrate: 4000000, maxDpbMbs: 8100, level: 22 },\n { maxMacroblocks: 1620, maxBitrate: 1e7, maxDpbMbs: 8100, level: 30 },\n { maxMacroblocks: 3600, maxBitrate: 14000000, maxDpbMbs: 18000, level: 31 },\n { maxMacroblocks: 5120, maxBitrate: 20000000, maxDpbMbs: 20480, level: 32 },\n { maxMacroblocks: 8192, maxBitrate: 20000000, maxDpbMbs: 32768, level: 40 },\n { maxMacroblocks: 8192, maxBitrate: 50000000, maxDpbMbs: 32768, level: 41 },\n { maxMacroblocks: 8704, maxBitrate: 50000000, maxDpbMbs: 34816, level: 42 },\n { maxMacroblocks: 22080, maxBitrate: 135000000, maxDpbMbs: 110400, level: 50 },\n { maxMacroblocks: 36864, maxBitrate: 240000000, maxDpbMbs: 184320, level: 51 },\n { maxMacroblocks: 36864, maxBitrate: 240000000, maxDpbMbs: 184320, level: 52 },\n { maxMacroblocks: 139264, maxBitrate: 240000000, maxDpbMbs: 696320, level: 60 },\n { maxMacroblocks: 139264, maxBitrate: 480000000, maxDpbMbs: 696320, level: 61 },\n { maxMacroblocks: 139264, maxBitrate: 800000000, maxDpbMbs: 696320, level: 62 }\n ];\n var HEVC_LEVEL_TABLE = [\n { maxPictureSize: 36864, maxBitrate: 128000, tier: \"L\", level: 30 },\n { maxPictureSize: 122880, maxBitrate: 1500000, tier: \"L\", level: 60 },\n { maxPictureSize: 245760, maxBitrate: 3000000, tier: \"L\", level: 63 },\n { maxPictureSize: 552960, maxBitrate: 6000000, tier: \"L\", level: 90 },\n { maxPictureSize: 983040, maxBitrate: 1e7, tier: \"L\", level: 93 },\n { maxPictureSize: 2228224, maxBitrate: 12000000, tier: \"L\", level: 120 },\n { maxPictureSize: 2228224, maxBitrate: 30000000, tier: \"H\", level: 120 },\n { maxPictureSize: 2228224, maxBitrate: 20000000, tier: \"L\", level: 123 },\n { maxPictureSize: 2228224, maxBitrate: 50000000, tier: \"H\", level: 123 },\n { maxPictureSize: 8912896, maxBitrate: 25000000, tier: \"L\", level: 150 },\n { maxPictureSize: 8912896, maxBitrate: 1e8, tier: \"H\", level: 150 },\n { maxPictureSize: 8912896, maxBitrate: 40000000, tier: \"L\", level: 153 },\n { maxPictureSize: 8912896, maxBitrate: 160000000, tier: \"H\", level: 153 },\n { maxPictureSize: 8912896, maxBitrate: 60000000, tier: \"L\", level: 156 },\n { maxPictureSize: 8912896, maxBitrate: 240000000, tier: \"H\", level: 156 },\n { maxPictureSize: 35651584, maxBitrate: 60000000, tier: \"L\", level: 180 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, tier: \"H\", level: 180 },\n { maxPictureSize: 35651584, maxBitrate: 120000000, tier: \"L\", level: 183 },\n { maxPictureSize: 35651584, maxBitrate: 480000000, tier: \"H\", level: 183 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, tier: \"L\", level: 186 },\n { maxPictureSize: 35651584, maxBitrate: 800000000, tier: \"H\", level: 186 }\n ];\n var VP9_LEVEL_TABLE = [\n { maxPictureSize: 36864, maxBitrate: 200000, level: 10 },\n { maxPictureSize: 73728, maxBitrate: 800000, level: 11 },\n { maxPictureSize: 122880, maxBitrate: 1800000, level: 20 },\n { maxPictureSize: 245760, maxBitrate: 3600000, level: 21 },\n { maxPictureSize: 552960, maxBitrate: 7200000, level: 30 },\n { maxPictureSize: 983040, maxBitrate: 12000000, level: 31 },\n { maxPictureSize: 2228224, maxBitrate: 18000000, level: 40 },\n { maxPictureSize: 2228224, maxBitrate: 30000000, level: 41 },\n { maxPictureSize: 8912896, maxBitrate: 60000000, level: 50 },\n { maxPictureSize: 8912896, maxBitrate: 120000000, level: 51 },\n { maxPictureSize: 8912896, maxBitrate: 180000000, level: 52 },\n { maxPictureSize: 35651584, maxBitrate: 180000000, level: 60 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, level: 61 },\n { maxPictureSize: 35651584, maxBitrate: 480000000, level: 62 }\n ];\n var AV1_LEVEL_TABLE = [\n { maxPictureSize: 147456, maxBitrate: 1500000, tier: \"M\", level: 0 },\n { maxPictureSize: 278784, maxBitrate: 3000000, tier: \"M\", level: 1 },\n { maxPictureSize: 665856, maxBitrate: 6000000, tier: \"M\", level: 4 },\n { maxPictureSize: 1065024, maxBitrate: 1e7, tier: \"M\", level: 5 },\n { maxPictureSize: 2359296, maxBitrate: 12000000, tier: \"M\", level: 8 },\n { maxPictureSize: 2359296, maxBitrate: 30000000, tier: \"H\", level: 8 },\n { maxPictureSize: 2359296, maxBitrate: 20000000, tier: \"M\", level: 9 },\n { maxPictureSize: 2359296, maxBitrate: 50000000, tier: \"H\", level: 9 },\n { maxPictureSize: 8912896, maxBitrate: 30000000, tier: \"M\", level: 12 },\n { maxPictureSize: 8912896, maxBitrate: 1e8, tier: \"H\", level: 12 },\n { maxPictureSize: 8912896, maxBitrate: 40000000, tier: \"M\", level: 13 },\n { maxPictureSize: 8912896, maxBitrate: 160000000, tier: \"H\", level: 13 },\n { maxPictureSize: 8912896, maxBitrate: 60000000, tier: \"M\", level: 14 },\n { maxPictureSize: 8912896, maxBitrate: 240000000, tier: \"H\", level: 14 },\n { maxPictureSize: 35651584, maxBitrate: 60000000, tier: \"M\", level: 15 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, tier: \"H\", level: 15 },\n { maxPictureSize: 35651584, maxBitrate: 60000000, tier: \"M\", level: 16 },\n { maxPictureSize: 35651584, maxBitrate: 240000000, tier: \"H\", level: 16 },\n { maxPictureSize: 35651584, maxBitrate: 1e8, tier: \"M\", level: 17 },\n { maxPictureSize: 35651584, maxBitrate: 480000000, tier: \"H\", level: 17 },\n { maxPictureSize: 35651584, maxBitrate: 160000000, tier: \"M\", level: 18 },\n { maxPictureSize: 35651584, maxBitrate: 800000000, tier: \"H\", level: 18 },\n { maxPictureSize: 35651584, maxBitrate: 160000000, tier: \"M\", level: 19 },\n { maxPictureSize: 35651584, maxBitrate: 800000000, tier: \"H\", level: 19 }\n ];\n var VP9_DEFAULT_SUFFIX = \".01.01.01.01.00\";\n var AV1_DEFAULT_SUFFIX = \".0.110.01.01.01.0\";\n var PRORES_FOURCCS = [\n \"ap4x\",\n \"ap4h\",\n \"apch\",\n \"apcn\",\n \"apcs\",\n \"apco\"\n ];\n var DTS_FOURCCS = [\n \"dtsc\",\n \"dtsh\",\n \"dtsl\",\n \"dtse\"\n ];\n var PRORES_PROFILE_TARGET_BITRATES = [\n { fourCc: \"apco\", bitrate: 45000000, alpha: false },\n { fourCc: \"apcs\", bitrate: 102000000, alpha: false },\n { fourCc: \"apcn\", bitrate: 147000000, alpha: false },\n { fourCc: \"apch\", bitrate: 220000000, alpha: false },\n { fourCc: \"ap4h\", bitrate: 330000000, alpha: true },\n { fourCc: \"ap4x\", bitrate: 500000000, alpha: true }\n ];\n var buildVideoCodecString = (codec, width, height, bitrate, alpha) => {\n if (codec === \"avc\") {\n const profileIndication = 100;\n const totalMacroblocks = Math.ceil(width / 16) * Math.ceil(height / 16);\n const levelInfo = AVC_LEVEL_TABLE.find((level) => totalMacroblocks <= level.maxMacroblocks && bitrate <= level.maxBitrate) ?? last(AVC_LEVEL_TABLE);\n const levelIndication = levelInfo ? levelInfo.level : 0;\n const hexProfileIndication = profileIndication.toString(16).padStart(2, \"0\");\n const hexProfileCompatibility = \"00\";\n const hexLevelIndication = levelIndication.toString(16).padStart(2, \"0\");\n return `avc1.${hexProfileIndication}${hexProfileCompatibility}${hexLevelIndication}`;\n } else if (codec === \"hevc\") {\n const profilePrefix = \"\";\n const profileIdc = 1;\n const compatibilityFlags = \"6\";\n const pictureSize = width * height;\n const levelInfo = HEVC_LEVEL_TABLE.find((level) => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate) ?? last(HEVC_LEVEL_TABLE);\n const constraintFlags = \"B0\";\n return \"hev1.\" + `${profilePrefix}${profileIdc}.` + `${compatibilityFlags}.` + `${levelInfo.tier}${levelInfo.level}.` + `${constraintFlags}`;\n } else if (codec === \"vp8\") {\n return \"vp8\";\n } else if (codec === \"vp9\") {\n const profile = \"00\";\n const pictureSize = width * height;\n const levelInfo = VP9_LEVEL_TABLE.find((level) => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate) ?? last(VP9_LEVEL_TABLE);\n const bitDepth = \"08\";\n return `vp09.${profile}.${levelInfo.level.toString().padStart(2, \"0\")}.${bitDepth}`;\n } else if (codec === \"av1\") {\n const profile = 0;\n const pictureSize = width * height;\n const levelInfo = AV1_LEVEL_TABLE.find((level) => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate) ?? last(AV1_LEVEL_TABLE);\n const level = levelInfo.level.toString().padStart(2, \"0\");\n const bitDepth = \"08\";\n return `av01.${profile}.${level}${levelInfo.tier}.${bitDepth}`;\n } else if (codec === \"prores\") {\n const referencePixels = 1920 * 1080;\n const scaleFactor = Math.pow(width * height / referencePixels, 0.95);\n const candidates = PRORES_PROFILE_TARGET_BITRATES.filter((x) => x.alpha === alpha);\n let bestFourCc = candidates[0].fourCc;\n let smallestDifference = Infinity;\n for (const { fourCc, bitrate: targetBitrate } of candidates) {\n const difference = Math.abs(targetBitrate * scaleFactor - bitrate);\n if (difference < smallestDifference) {\n smallestDifference = difference;\n bestFourCc = fourCc;\n }\n }\n return bestFourCc;\n } else {\n assertNever(codec);\n }\n throw new TypeError(`Unhandled codec '${String(codec)}'.`);\n };\n var generateVp9CodecConfigurationFromCodecString = (codecString) => {\n const parts = codecString.split(\".\");\n const profile = Number(parts[1]);\n const level = Number(parts[2]);\n const bitDepth = Number(parts[3]);\n const chromaSubsampling = parts[4] ? Number(parts[4]) : 1;\n return [\n 1,\n 1,\n profile,\n 2,\n 1,\n level,\n 3,\n 1,\n bitDepth,\n 4,\n 1,\n chromaSubsampling\n ];\n };\n var generateAv1CodecConfigurationFromCodecString = (codecString) => {\n const parts = codecString.split(\".\");\n const marker = 1;\n const version = 1;\n const firstByte = (marker << 7) + version;\n const profile = Number(parts[1]);\n const levelAndTier = parts[2];\n const level = Number(levelAndTier.slice(0, -1));\n const secondByte = (profile << 5) + level;\n const tier = levelAndTier.slice(-1) === \"H\" ? 1 : 0;\n const bitDepth = Number(parts[3]);\n const highBitDepth = bitDepth === 8 ? 0 : 1;\n const twelveBit = bitDepth === 12 ? 1 : 0;\n const monochrome = parts[4] ? Number(parts[4]) : 0;\n const chromaSubsamplingX = parts[5] ? Number(parts[5][0]) : 1;\n const chromaSubsamplingY = parts[5] ? Number(parts[5][1]) : 1;\n const chromaSamplePosition = parts[5] ? Number(parts[5][2]) : 0;\n const thirdByte = (tier << 7) + (highBitDepth << 6) + (twelveBit << 5) + (monochrome << 4) + (chromaSubsamplingX << 3) + (chromaSubsamplingY << 2) + chromaSamplePosition;\n const initialPresentationDelayPresent = 0;\n const fourthByte = initialPresentationDelayPresent;\n return [firstByte, secondByte, thirdByte, fourthByte];\n };\n var extractVideoCodecString = (trackInfo) => {\n const { codec, codecDescription, colorSpace, avcCodecInfo, hevcCodecInfo, vp9CodecInfo, av1CodecInfo, proresFormat } = trackInfo;\n if (codec === \"avc\") {\n assert(trackInfo.avcType !== null);\n if (avcCodecInfo) {\n const bytes = new Uint8Array([\n avcCodecInfo.avcProfileIndication,\n avcCodecInfo.profileCompatibility,\n avcCodecInfo.avcLevelIndication\n ]);\n return `avc${trackInfo.avcType}.${bytesToHexString(bytes)}`;\n }\n if (!codecDescription || codecDescription.byteLength < 4) {\n throw new TypeError(\"AVC decoder description is not provided or is not at least 4 bytes long.\");\n }\n return `avc${trackInfo.avcType}.${bytesToHexString(codecDescription.subarray(1, 4))}`;\n } else if (codec === \"hevc\") {\n let generalProfileSpace;\n let generalProfileIdc;\n let compatibilityFlags;\n let generalTierFlag;\n let generalLevelIdc;\n let constraintFlags;\n if (hevcCodecInfo) {\n generalProfileSpace = hevcCodecInfo.generalProfileSpace;\n generalProfileIdc = hevcCodecInfo.generalProfileIdc;\n compatibilityFlags = reverseBitsU32(hevcCodecInfo.generalProfileCompatibilityFlags);\n generalTierFlag = hevcCodecInfo.generalTierFlag;\n generalLevelIdc = hevcCodecInfo.generalLevelIdc;\n constraintFlags = [...hevcCodecInfo.generalConstraintIndicatorFlags];\n } else {\n if (!codecDescription || codecDescription.byteLength < 23) {\n throw new TypeError(\"HEVC decoder description is not provided or is not at least 23 bytes long.\");\n }\n const view = toDataView(codecDescription);\n const profileByte = view.getUint8(1);\n generalProfileSpace = profileByte >> 6 & 3;\n generalProfileIdc = profileByte & 31;\n compatibilityFlags = reverseBitsU32(view.getUint32(2));\n generalTierFlag = profileByte >> 5 & 1;\n generalLevelIdc = view.getUint8(12);\n constraintFlags = [];\n for (let i = 0;i < 6; i++) {\n constraintFlags.push(view.getUint8(6 + i));\n }\n }\n let codecString = \"hev1.\";\n codecString += [\"\", \"A\", \"B\", \"C\"][generalProfileSpace] + generalProfileIdc;\n codecString += \".\";\n codecString += compatibilityFlags.toString(16).toUpperCase();\n codecString += \".\";\n codecString += generalTierFlag === 0 ? \"L\" : \"H\";\n codecString += generalLevelIdc;\n while (constraintFlags.length > 0 && constraintFlags[constraintFlags.length - 1] === 0) {\n constraintFlags.pop();\n }\n if (constraintFlags.length > 0) {\n codecString += \".\";\n codecString += constraintFlags.map((x) => x.toString(16).toUpperCase()).join(\".\");\n }\n return codecString;\n } else if (codec === \"vp8\") {\n return \"vp8\";\n } else if (codec === \"vp9\") {\n if (!vp9CodecInfo) {\n const pictureSize = trackInfo.width * trackInfo.height;\n let level = last(VP9_LEVEL_TABLE).level;\n for (const entry of VP9_LEVEL_TABLE) {\n if (pictureSize <= entry.maxPictureSize) {\n level = entry.level;\n break;\n }\n }\n return `vp09.00.${level.toString().padStart(2, \"0\")}.08`;\n }\n const profile = vp9CodecInfo.profile.toString().padStart(2, \"0\");\n const level = vp9CodecInfo.level.toString().padStart(2, \"0\");\n const bitDepth = vp9CodecInfo.bitDepth.toString().padStart(2, \"0\");\n const chromaSubsampling = vp9CodecInfo.chromaSubsampling.toString().padStart(2, \"0\");\n const colourPrimaries = vp9CodecInfo.colourPrimaries.toString().padStart(2, \"0\");\n const transferCharacteristics = vp9CodecInfo.transferCharacteristics.toString().padStart(2, \"0\");\n const matrixCoefficients = vp9CodecInfo.matrixCoefficients.toString().padStart(2, \"0\");\n const videoFullRangeFlag = vp9CodecInfo.videoFullRangeFlag.toString().padStart(2, \"0\");\n let string = `vp09.${profile}.${level}.${bitDepth}.${chromaSubsampling}`;\n string += `.${colourPrimaries}.${transferCharacteristics}.${matrixCoefficients}.${videoFullRangeFlag}`;\n if (string.endsWith(VP9_DEFAULT_SUFFIX)) {\n string = string.slice(0, -VP9_DEFAULT_SUFFIX.length);\n }\n return string;\n } else if (codec === \"av1\") {\n if (!av1CodecInfo) {\n const pictureSize = trackInfo.width * trackInfo.height;\n let level = last(VP9_LEVEL_TABLE).level;\n for (const entry of VP9_LEVEL_TABLE) {\n if (pictureSize <= entry.maxPictureSize) {\n level = entry.level;\n break;\n }\n }\n return `av01.0.${level.toString().padStart(2, \"0\")}M.08`;\n }\n const profile = av1CodecInfo.profile;\n const level = av1CodecInfo.level.toString().padStart(2, \"0\");\n const tier = av1CodecInfo.tier ? \"H\" : \"M\";\n const bitDepth = av1CodecInfo.bitDepth.toString().padStart(2, \"0\");\n const monochrome = av1CodecInfo.monochrome ? \"1\" : \"0\";\n const chromaSubsampling = 100 * av1CodecInfo.chromaSubsamplingX + 10 * av1CodecInfo.chromaSubsamplingY + 1 * (av1CodecInfo.chromaSubsamplingX && av1CodecInfo.chromaSubsamplingY ? av1CodecInfo.chromaSamplePosition : 0);\n let colorPrimaries;\n let transferCharacteristics;\n let matrixCoefficients;\n let videoFullRangeFlag;\n if (av1CodecInfoHasColorInfo(av1CodecInfo)) {\n colorPrimaries = av1CodecInfo.colourPrimaries;\n transferCharacteristics = av1CodecInfo.transferCharacteristics;\n matrixCoefficients = av1CodecInfo.matrixCoefficients;\n videoFullRangeFlag = av1CodecInfo.videoFullRangeFlag;\n } else {\n colorPrimaries = colorSpace?.primaries ? COLOR_PRIMARIES_MAP[colorSpace.primaries] : 1;\n transferCharacteristics = colorSpace?.transfer ? TRANSFER_CHARACTERISTICS_MAP[colorSpace.transfer] : 1;\n matrixCoefficients = colorSpace?.matrix ? MATRIX_COEFFICIENTS_MAP[colorSpace.matrix] : 1;\n videoFullRangeFlag = colorSpace?.fullRange ? 1 : 0;\n }\n let string = `av01.${profile}.${level}${tier}.${bitDepth}`;\n string += `.${monochrome}.${chromaSubsampling.toString().padStart(3, \"0\")}`;\n string += `.${colorPrimaries.toString().padStart(2, \"0\")}`;\n string += `.${transferCharacteristics.toString().padStart(2, \"0\")}`;\n string += `.${matrixCoefficients.toString().padStart(2, \"0\")}`;\n string += `.${videoFullRangeFlag}`;\n if (string.endsWith(AV1_DEFAULT_SUFFIX)) {\n string = string.slice(0, -AV1_DEFAULT_SUFFIX.length);\n }\n return string;\n } else if (codec === \"prores\") {\n return proresFormat ?? \"apch\";\n } else if (codec !== null) {\n assertNever(codec);\n }\n throw new TypeError(`Unhandled codec '${codec}'.`);\n };\n var buildAudioCodecString = (codec, numberOfChannels, sampleRate) => {\n if (codec === \"aac\") {\n if (numberOfChannels >= 2 && sampleRate <= 24000) {\n return \"mp4a.40.29\";\n }\n if (sampleRate <= 24000) {\n return \"mp4a.40.5\";\n }\n return \"mp4a.40.2\";\n } else if (codec === \"mp3\") {\n return \"mp3\";\n } else if (codec === \"opus\") {\n return \"opus\";\n } else if (codec === \"vorbis\") {\n return \"vorbis\";\n } else if (codec === \"flac\") {\n return \"flac\";\n } else if (codec === \"ac3\") {\n return \"ac-3\";\n } else if (codec === \"eac3\") {\n return \"ec-3\";\n } else if (codec === \"dts\") {\n return \"dtsc\";\n } else if (PCM_AUDIO_CODECS.includes(codec)) {\n return codec;\n }\n throw new TypeError(`Unhandled codec '${codec}'.`);\n };\n var OPUS_SAMPLE_RATE = 48000;\n var PCM_CODEC_REGEX = /^pcm-([usf])(\\d+)(be)?$/;\n var parsePcmCodec = (codec) => {\n assert(PCM_AUDIO_CODECS.includes(codec));\n if (codec === \"ulaw\") {\n return { dataType: \"ulaw\", sampleSize: 1, littleEndian: true, silentValue: 255 };\n } else if (codec === \"alaw\") {\n return { dataType: \"alaw\", sampleSize: 1, littleEndian: true, silentValue: 213 };\n }\n const match = PCM_CODEC_REGEX.exec(codec);\n assert(match);\n let dataType;\n if (match[1] === \"u\") {\n dataType = \"unsigned\";\n } else if (match[1] === \"s\") {\n dataType = \"signed\";\n } else {\n dataType = \"float\";\n }\n const sampleSize = Number(match[2]) / 8;\n const littleEndian = match[3] !== \"be\";\n const silentValue = codec === \"pcm-u8\" ? 2 ** 7 : 0;\n return { dataType, sampleSize, littleEndian, silentValue };\n };\n var inferCodecFromCodecString = (codecString) => {\n if (codecString.startsWith(\"avc1\") || codecString.startsWith(\"avc3\")) {\n return \"avc\";\n } else if (codecString.startsWith(\"hev1\") || codecString.startsWith(\"hvc1\")) {\n return \"hevc\";\n } else if (codecString === \"vp8\") {\n return \"vp8\";\n } else if (codecString.startsWith(\"vp09\")) {\n return \"vp9\";\n } else if (codecString.startsWith(\"av01\")) {\n return \"av1\";\n } else if (PRORES_FOURCCS.includes(codecString)) {\n return \"prores\";\n }\n if (codecString === \"mp3\" || codecString === \"mp4a.69\" || codecString === \"mp4a.6B\" || codecString === \"mp4a.6b\" || codecString === \"mp4a.40.34\") {\n return \"mp3\";\n } else if (codecString.startsWith(\"mp4a.40.\") || codecString === \"mp4a.67\") {\n return \"aac\";\n } else if (codecString === \"opus\" || codecString === \"Opus\") {\n return \"opus\";\n } else if (codecString === \"vorbis\") {\n return \"vorbis\";\n } else if (codecString === \"flac\") {\n return \"flac\";\n } else if (codecString === \"ac-3\" || codecString === \"ac3\") {\n return \"ac3\";\n } else if (codecString === \"ec-3\" || codecString === \"eac3\") {\n return \"eac3\";\n } else if (DTS_FOURCCS.includes(codecString)) {\n return \"dts\";\n } else if (codecString === \"ulaw\") {\n return \"ulaw\";\n } else if (codecString === \"alaw\") {\n return \"alaw\";\n } else if (PCM_CODEC_REGEX.test(codecString)) {\n return codecString;\n }\n if (codecString === \"webvtt\") {\n return \"webvtt\";\n }\n return null;\n };\n var getVideoEncoderConfigExtension = (codec) => {\n if (codec === \"avc\") {\n return {\n avc: {\n format: \"avc\"\n }\n };\n } else if (codec === \"hevc\") {\n return {\n hevc: {\n format: \"hevc\"\n }\n };\n }\n return {};\n };\n var getAudioEncoderConfigExtension = (codec) => {\n if (codec === \"aac\") {\n return {\n aac: {\n format: \"aac\"\n }\n };\n } else if (codec === \"opus\") {\n return {\n opus: {\n format: \"opus\"\n }\n };\n }\n return {};\n };\n var VALID_VIDEO_CODEC_STRING_PREFIXES = [\"avc1\", \"avc3\", \"hev1\", \"hvc1\", \"vp8\", \"vp09\", \"av01\", ...PRORES_FOURCCS];\n var AVC_CODEC_STRING_REGEX = /^(avc1|avc3)\\.[0-9a-fA-F]{6}$/;\n var HEVC_CODEC_STRING_REGEX = /^(hev1|hvc1)\\.(?:[ABC]?\\d+)\\.[0-9a-fA-F]{1,8}\\.[LH]\\d+(?:\\.[0-9a-fA-F]{1,2}){0,6}$/;\n var VP9_CODEC_STRING_REGEX = /^vp09(?:\\.\\d{2}){3}(?:(?:\\.\\d{2}){5})?$/;\n var AV1_CODEC_STRING_REGEX = /^av01\\.\\d\\.\\d{2}[MH]\\.\\d{2}(?:\\.\\d\\.\\d{3}\\.\\d{2}\\.\\d{2}\\.\\d{2}\\.\\d)?$/;\n var validateVideoChunkMetadata = (metadata, trackCodec) => {\n if (!metadata) {\n throw new TypeError(\"Video chunk metadata must be provided.\");\n }\n if (typeof metadata !== \"object\") {\n throw new TypeError(\"Video chunk metadata must be an object.\");\n }\n if (!metadata.decoderConfig) {\n throw new TypeError(\"Video chunk metadata must include a decoder configuration.\");\n }\n if (typeof metadata.decoderConfig !== \"object\") {\n throw new TypeError(\"Video chunk metadata decoder configuration must be an object.\");\n }\n if (typeof metadata.decoderConfig.codec !== \"string\") {\n throw new TypeError(\"Video chunk metadata decoder configuration must specify a codec string.\");\n }\n if (!VALID_VIDEO_CODEC_STRING_PREFIXES.some((prefix) => metadata.decoderConfig.codec.startsWith(prefix))) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string must be a valid video codec string as specified in\" + \" the Mediabunny Codec Registry.\");\n }\n if (!Number.isInteger(metadata.decoderConfig.codedWidth) || metadata.decoderConfig.codedWidth <= 0) {\n throw new TypeError(\"Video chunk metadata decoder configuration must specify a valid codedWidth (positive integer).\");\n }\n if (!Number.isInteger(metadata.decoderConfig.codedHeight) || metadata.decoderConfig.codedHeight <= 0) {\n throw new TypeError(\"Video chunk metadata decoder configuration must specify a valid codedHeight (positive integer).\");\n }\n if (metadata.decoderConfig.displayAspectWidth !== undefined && (!Number.isInteger(metadata.decoderConfig.displayAspectWidth) || metadata.decoderConfig.displayAspectWidth <= 0)) {\n throw new TypeError(\"Video chunk metadata decoder configuration displayAspectWidth, when defined, must be a positive integer.\");\n }\n if (metadata.decoderConfig.displayAspectHeight !== undefined && (!Number.isInteger(metadata.decoderConfig.displayAspectHeight) || metadata.decoderConfig.displayAspectHeight <= 0)) {\n throw new TypeError(\"Video chunk metadata decoder configuration displayAspectHeight, when defined, must be a positive integer.\");\n }\n if (metadata.decoderConfig.displayAspectWidth !== undefined !== (metadata.decoderConfig.displayAspectHeight !== undefined)) {\n throw new TypeError(\"Video chunk metadata decoder configuration must specify both displayAspectWidth and displayAspectHeight,\" + \" or neither.\");\n }\n if (metadata.decoderConfig.description !== undefined) {\n if (!isAllowSharedBufferSource(metadata.decoderConfig.description)) {\n throw new TypeError(\"Video chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an\" + \" ArrayBuffer view.\");\n }\n }\n if (metadata.decoderConfig.colorSpace !== undefined) {\n const { colorSpace } = metadata.decoderConfig;\n if (typeof colorSpace !== \"object\") {\n throw new TypeError(\"Video chunk metadata decoder configuration colorSpace, when provided, must be an object.\");\n }\n const primariesValues = Object.keys(COLOR_PRIMARIES_MAP);\n if (colorSpace.primaries != null && !primariesValues.includes(colorSpace.primaries)) {\n throw new TypeError(`Video chunk metadata decoder configuration colorSpace primaries, when defined, must be one of` + ` ${primariesValues.join(\", \")}.`);\n }\n const transferValues = Object.keys(TRANSFER_CHARACTERISTICS_MAP);\n if (colorSpace.transfer != null && !transferValues.includes(colorSpace.transfer)) {\n throw new TypeError(`Video chunk metadata decoder configuration colorSpace transfer, when defined, must be one of` + ` ${transferValues.join(\", \")}.`);\n }\n const matrixValues = Object.keys(MATRIX_COEFFICIENTS_MAP);\n if (colorSpace.matrix != null && !matrixValues.includes(colorSpace.matrix)) {\n throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of` + ` ${matrixValues.join(\", \")}.`);\n }\n if (colorSpace.fullRange != null && typeof colorSpace.fullRange !== \"boolean\") {\n throw new TypeError(\"Video chunk metadata decoder configuration colorSpace fullRange, when defined, must be a boolean.\");\n }\n }\n if (metadata.decoderConfig.codec.startsWith(\"avc1\") || metadata.decoderConfig.codec.startsWith(\"avc3\")) {\n if (!AVC_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string for AVC must be a valid AVC codec string as\" + \" specified in Section 3.4 of RFC 6381.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"hev1\") || metadata.decoderConfig.codec.startsWith(\"hvc1\")) {\n if (!HEVC_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string for HEVC must be a valid HEVC codec string as\" + \" specified in Section E.3 of ISO 14496-15.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"vp8\")) {\n if (metadata.decoderConfig.codec !== \"vp8\") {\n throw new TypeError('Video chunk metadata decoder configuration codec string for VP8 must be \"vp8\".');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"vp09\")) {\n if (!VP9_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string for VP9 must be a valid VP9 codec string as\" + ' specified in Section \"Codecs Parameter String\" of https://www.webmproject.org/vp9/mp4/.');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"av01\")) {\n if (!AV1_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Video chunk metadata decoder configuration codec string for AV1 must be a valid AV1 codec string as\" + ' specified in Section \"Codecs Parameter String\" of https://aomediacodec.github.io/av1-isobmff/.');\n }\n } else if (PRORES_FOURCCS.some((x) => metadata.decoderConfig.codec.startsWith(x))) {\n if (!PRORES_FOURCCS.some((x) => metadata.decoderConfig.codec === x)) {\n throw new TypeError(`Video chunk metadata decoder configuration codec string for ProRes must be one of the valid ProRes` + ` four-character codes: ${PRORES_FOURCCS.join(\", \")}.`);\n }\n }\n if (trackCodec !== null && inferCodecFromCodecString(metadata.decoderConfig.codec) !== trackCodec) {\n throw new TypeError(`Video chunk metadata decoder configuration codec string '${metadata.decoderConfig.codec}' does not fit to` + ` the track codec '${trackCodec}'.`);\n }\n };\n var VALID_AUDIO_CODEC_STRING_PREFIXES = [\n \"mp4a\",\n \"mp3\",\n \"opus\",\n \"vorbis\",\n \"flac\",\n \"ulaw\",\n \"alaw\",\n \"pcm\",\n \"ac-3\",\n \"ec-3\",\n \"dts\"\n ];\n var validateAudioChunkMetadata = (metadata, trackCodec) => {\n if (!metadata) {\n throw new TypeError(\"Audio chunk metadata must be provided.\");\n }\n if (typeof metadata !== \"object\") {\n throw new TypeError(\"Audio chunk metadata must be an object.\");\n }\n if (!metadata.decoderConfig) {\n throw new TypeError(\"Audio chunk metadata must include a decoder configuration.\");\n }\n if (typeof metadata.decoderConfig !== \"object\") {\n throw new TypeError(\"Audio chunk metadata decoder configuration must be an object.\");\n }\n if (typeof metadata.decoderConfig.codec !== \"string\") {\n throw new TypeError(\"Audio chunk metadata decoder configuration must specify a codec string.\");\n }\n if (!VALID_AUDIO_CODEC_STRING_PREFIXES.some((prefix) => metadata.decoderConfig.codec.startsWith(prefix))) {\n throw new TypeError(\"Audio chunk metadata decoder configuration codec string must be a valid audio codec string as specified in\" + \" the Mediabunny Codec Registry.\");\n }\n if (!Number.isInteger(metadata.decoderConfig.sampleRate) || metadata.decoderConfig.sampleRate <= 0) {\n throw new TypeError(\"Audio chunk metadata decoder configuration must specify a valid sampleRate (positive integer).\");\n }\n if (!Number.isInteger(metadata.decoderConfig.numberOfChannels) || metadata.decoderConfig.numberOfChannels <= 0) {\n throw new TypeError(\"Audio chunk metadata decoder configuration must specify a valid numberOfChannels (positive integer).\");\n }\n if (metadata.decoderConfig.description !== undefined) {\n if (!isAllowSharedBufferSource(metadata.decoderConfig.description)) {\n throw new TypeError(\"Audio chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an\" + \" ArrayBuffer view.\");\n }\n }\n if (metadata.decoderConfig.codec.startsWith(\"mp4a\") && metadata.decoderConfig.codec !== \"mp4a.69\" && metadata.decoderConfig.codec !== \"mp4a.6B\" && metadata.decoderConfig.codec !== \"mp4a.6b\") {\n const validStrings = [\"mp4a.40.2\", \"mp4a.40.02\", \"mp4a.40.5\", \"mp4a.40.05\", \"mp4a.40.29\", \"mp4a.67\"];\n if (!validStrings.includes(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Audio chunk metadata decoder configuration codec string for AAC must be a valid AAC codec string as\" + \" specified in https://www.w3.org/TR/webcodecs-aac-codec-registration/.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"mp3\") || metadata.decoderConfig.codec.startsWith(\"mp4a\")) {\n if (metadata.decoderConfig.codec !== \"mp3\" && metadata.decoderConfig.codec !== \"mp4a.69\" && metadata.decoderConfig.codec !== \"mp4a.6B\" && metadata.decoderConfig.codec !== \"mp4a.6b\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for MP3 must be \"mp3\", \"mp4a.69\" or' + ' \"mp4a.6B\".');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"opus\")) {\n if (metadata.decoderConfig.codec !== \"opus\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for Opus must be \"opus\".');\n }\n if (metadata.decoderConfig.description && metadata.decoderConfig.description.byteLength < 18) {\n throw new TypeError(\"Audio chunk metadata decoder configuration description, when specified, is expected to be an\" + \" Identification Header as specified in Section 5.1 of RFC 7845.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"vorbis\")) {\n if (metadata.decoderConfig.codec !== \"vorbis\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for Vorbis must be \"vorbis\".');\n }\n if (!metadata.decoderConfig.description) {\n throw new TypeError(\"Audio chunk metadata decoder configuration for Vorbis must include a description, which is expected to\" + \" adhere to the format described in https://www.w3.org/TR/webcodecs-vorbis-codec-registration/.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"flac\")) {\n if (metadata.decoderConfig.codec !== \"flac\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for FLAC must be \"flac\".');\n }\n const minDescriptionSize = 4 + 4 + 34;\n if (!metadata.decoderConfig.description || metadata.decoderConfig.description.byteLength < minDescriptionSize) {\n throw new TypeError(\"Audio chunk metadata decoder configuration for FLAC must include a description, which is expected to\" + \" adhere to the format described in https://www.w3.org/TR/webcodecs-flac-codec-registration/.\");\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"ac-3\") || metadata.decoderConfig.codec.startsWith(\"ac3\")) {\n if (metadata.decoderConfig.codec !== \"ac-3\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for AC-3 must be \"ac-3\".');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"ec-3\") || metadata.decoderConfig.codec.startsWith(\"eac3\")) {\n if (metadata.decoderConfig.codec !== \"ec-3\") {\n throw new TypeError('Audio chunk metadata decoder configuration codec string for EC-3 must be \"ec-3\".');\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"dts\")) {\n if (!DTS_FOURCCS.includes(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Audio chunk metadata decoder configuration codec string for DTS must be one of the following\" + ` four-character codes: ${DTS_FOURCCS.join(\", \")}.`);\n }\n } else if (metadata.decoderConfig.codec.startsWith(\"pcm\") || metadata.decoderConfig.codec.startsWith(\"ulaw\") || metadata.decoderConfig.codec.startsWith(\"alaw\")) {\n if (!PCM_AUDIO_CODECS.includes(metadata.decoderConfig.codec)) {\n throw new TypeError(\"Audio chunk metadata decoder configuration codec string for PCM must be one of the supported PCM\" + ` codecs (${PCM_AUDIO_CODECS.join(\", \")}).`);\n }\n }\n if (trackCodec !== null && inferCodecFromCodecString(metadata.decoderConfig.codec) !== trackCodec) {\n throw new TypeError(`Audio chunk metadata decoder configuration codec string '${metadata.decoderConfig.codec}' does not fit to` + ` the track codec '${trackCodec}'.`);\n }\n };\n var validateSubtitleMetadata = (metadata) => {\n if (!metadata) {\n throw new TypeError(\"Subtitle metadata must be provided.\");\n }\n if (typeof metadata !== \"object\") {\n throw new TypeError(\"Subtitle metadata must be an object.\");\n }\n if (!metadata.config) {\n throw new TypeError(\"Subtitle metadata must include a config object.\");\n }\n if (typeof metadata.config !== \"object\") {\n throw new TypeError(\"Subtitle metadata config must be an object.\");\n }\n if (typeof metadata.config.description !== \"string\") {\n throw new TypeError(\"Subtitle metadata config description must be a string.\");\n }\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/packet.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var PLACEHOLDER_DATA = /* @__PURE__ */ new Uint8Array(0);\n\n class EncodedPacket {\n constructor(data, type, timestamp, duration, sequenceNumber = -1, byteLength, sideData) {\n this.data = data;\n this.type = type;\n this.timestamp = timestamp;\n this.duration = duration;\n this.sequenceNumber = sequenceNumber;\n if (data === PLACEHOLDER_DATA && byteLength === undefined) {\n throw new Error(\"Internal error: byteLength must be explicitly provided when constructing metadata-only packets.\");\n }\n if (byteLength === undefined) {\n byteLength = data.byteLength;\n }\n if (!(data instanceof Uint8Array)) {\n throw new TypeError(\"data must be a Uint8Array.\");\n }\n if (type !== \"key\" && type !== \"delta\") {\n throw new TypeError('type must be either \"key\" or \"delta\".');\n }\n if (!Number.isFinite(timestamp)) {\n throw new TypeError(\"timestamp must be a number.\");\n }\n if (!Number.isFinite(duration) || duration < 0) {\n throw new TypeError(\"duration must be a non-negative number.\");\n }\n if (!Number.isFinite(sequenceNumber)) {\n throw new TypeError(\"sequenceNumber must be a number.\");\n }\n if (!Number.isInteger(byteLength) || byteLength < 0) {\n throw new TypeError(\"byteLength must be a non-negative integer.\");\n }\n if (sideData !== undefined && (typeof sideData !== \"object\" || !sideData)) {\n throw new TypeError(\"sideData, when provided, must be an object.\");\n }\n if (sideData?.alpha !== undefined && !(sideData.alpha instanceof Uint8Array)) {\n throw new TypeError(\"sideData.alpha, when provided, must be a Uint8Array.\");\n }\n if (sideData?.alphaByteLength !== undefined && (!Number.isInteger(sideData.alphaByteLength) || sideData.alphaByteLength < 0)) {\n throw new TypeError(\"sideData.alphaByteLength, when provided, must be a non-negative integer.\");\n }\n this.byteLength = byteLength;\n this.sideData = sideData ?? {};\n if (this.sideData.alpha && this.sideData.alphaByteLength === undefined) {\n this.sideData.alphaByteLength = this.sideData.alpha.byteLength;\n }\n }\n get isMetadataOnly() {\n return this.data === PLACEHOLDER_DATA;\n }\n get microsecondTimestamp() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);\n }\n get microsecondDuration() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);\n }\n toEncodedVideoChunk() {\n if (this.isMetadataOnly) {\n throw new TypeError(\"Metadata-only packets cannot be converted to a video chunk.\");\n }\n if (typeof EncodedVideoChunk === \"undefined\") {\n throw new Error(\"EncodedVideoChunk is not available in this environment.\");\n }\n return new EncodedVideoChunk({\n data: this.data,\n type: this.type,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration\n });\n }\n alphaToEncodedVideoChunk(type = this.type) {\n if (!this.sideData.alpha) {\n throw new TypeError(\"This packet does not contain alpha side data.\");\n }\n if (this.isMetadataOnly) {\n throw new TypeError(\"Metadata-only packets cannot be converted to a video chunk.\");\n }\n if (typeof EncodedVideoChunk === \"undefined\") {\n throw new Error(\"EncodedVideoChunk is not available in this environment.\");\n }\n return new EncodedVideoChunk({\n data: this.sideData.alpha,\n type,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration\n });\n }\n toEncodedAudioChunk() {\n if (this.isMetadataOnly) {\n throw new TypeError(\"Metadata-only packets cannot be converted to an audio chunk.\");\n }\n if (typeof EncodedAudioChunk === \"undefined\") {\n throw new Error(\"EncodedAudioChunk is not available in this environment.\");\n }\n return new EncodedAudioChunk({\n data: this.data,\n type: this.type,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration\n });\n }\n static fromEncodedChunk(chunk, sideData) {\n if (!(chunk instanceof EncodedVideoChunk || chunk instanceof EncodedAudioChunk)) {\n throw new TypeError(\"chunk must be an EncodedVideoChunk or EncodedAudioChunk.\");\n }\n const data = new Uint8Array(chunk.byteLength);\n chunk.copyTo(data);\n return new EncodedPacket(data, chunk.type, chunk.timestamp / 1e6, (chunk.duration ?? 0) / 1e6, undefined, undefined, sideData);\n }\n clone(options) {\n if (options !== undefined && (typeof options !== \"object\" || options === null)) {\n throw new TypeError(\"options, when provided, must be an object.\");\n }\n if (options?.data !== undefined && !(options.data instanceof Uint8Array)) {\n throw new TypeError(\"options.data, when provided, must be a Uint8Array.\");\n }\n if (options?.type !== undefined && options.type !== \"key\" && options.type !== \"delta\") {\n throw new TypeError('options.type, when provided, must be either \"key\" or \"delta\".');\n }\n if (options?.timestamp !== undefined && !Number.isFinite(options.timestamp)) {\n throw new TypeError(\"options.timestamp, when provided, must be a number.\");\n }\n if (options?.duration !== undefined && !Number.isFinite(options.duration)) {\n throw new TypeError(\"options.duration, when provided, must be a number.\");\n }\n if (options?.sequenceNumber !== undefined && !Number.isFinite(options.sequenceNumber)) {\n throw new TypeError(\"options.sequenceNumber, when provided, must be a number.\");\n }\n if (options?.sideData !== undefined && (typeof options.sideData !== \"object\" || options.sideData === null)) {\n throw new TypeError(\"options.sideData, when provided, must be an object.\");\n }\n return new EncodedPacket(options?.data ?? this.data, options?.type ?? this.type, options?.timestamp ?? this.timestamp, options?.duration ?? this.duration, options?.sequenceNumber ?? this.sequenceNumber, this.byteLength, options?.sideData ?? this.sideData);\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-misc.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var buildIsobmffMimeType = (info) => {\n const base = info.hasVideo ? \"video/\" : info.hasAudio ? \"audio/\" : \"application/\";\n let string = base + (info.isQuickTime ? \"quicktime\" : \"mp4\");\n if (info.codecStrings.length > 0) {\n const uniqueCodecMimeTypes = [...new Set(info.codecStrings)];\n string += `; codecs=\"${uniqueCodecMimeTypes.join(\", \")}\"`;\n }\n return string;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-reader.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var MIN_BOX_HEADER_SIZE = 8;\n var MAX_BOX_HEADER_SIZE = 16;\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/matroska/ebml.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class EBMLFloat32 {\n constructor(value) {\n this.value = value;\n }\n }\n\n class EBMLFloat64 {\n constructor(value) {\n this.value = value;\n }\n }\n\n class EBMLSignedInt {\n constructor(value) {\n this.value = value;\n }\n }\n\n class EBMLUnicodeString {\n constructor(value) {\n this.value = value;\n }\n }\n var EBMLId;\n (function(EBMLId) {\n EBMLId[EBMLId[\"EBML\"] = 440786851] = \"EBML\";\n EBMLId[EBMLId[\"EBMLVersion\"] = 17030] = \"EBMLVersion\";\n EBMLId[EBMLId[\"EBMLReadVersion\"] = 17143] = \"EBMLReadVersion\";\n EBMLId[EBMLId[\"EBMLMaxIDLength\"] = 17138] = \"EBMLMaxIDLength\";\n EBMLId[EBMLId[\"EBMLMaxSizeLength\"] = 17139] = \"EBMLMaxSizeLength\";\n EBMLId[EBMLId[\"DocType\"] = 17026] = \"DocType\";\n EBMLId[EBMLId[\"DocTypeVersion\"] = 17031] = \"DocTypeVersion\";\n EBMLId[EBMLId[\"DocTypeReadVersion\"] = 17029] = \"DocTypeReadVersion\";\n EBMLId[EBMLId[\"Void\"] = 236] = \"Void\";\n EBMLId[EBMLId[\"Segment\"] = 408125543] = \"Segment\";\n EBMLId[EBMLId[\"SeekHead\"] = 290298740] = \"SeekHead\";\n EBMLId[EBMLId[\"Seek\"] = 19899] = \"Seek\";\n EBMLId[EBMLId[\"SeekID\"] = 21419] = \"SeekID\";\n EBMLId[EBMLId[\"SeekPosition\"] = 21420] = \"SeekPosition\";\n EBMLId[EBMLId[\"Duration\"] = 17545] = \"Duration\";\n EBMLId[EBMLId[\"Info\"] = 357149030] = \"Info\";\n EBMLId[EBMLId[\"TimestampScale\"] = 2807729] = \"TimestampScale\";\n EBMLId[EBMLId[\"MuxingApp\"] = 19840] = \"MuxingApp\";\n EBMLId[EBMLId[\"WritingApp\"] = 22337] = \"WritingApp\";\n EBMLId[EBMLId[\"Tracks\"] = 374648427] = \"Tracks\";\n EBMLId[EBMLId[\"TrackEntry\"] = 174] = \"TrackEntry\";\n EBMLId[EBMLId[\"TrackNumber\"] = 215] = \"TrackNumber\";\n EBMLId[EBMLId[\"TrackUID\"] = 29637] = \"TrackUID\";\n EBMLId[EBMLId[\"TrackType\"] = 131] = \"TrackType\";\n EBMLId[EBMLId[\"FlagEnabled\"] = 185] = \"FlagEnabled\";\n EBMLId[EBMLId[\"FlagDefault\"] = 136] = \"FlagDefault\";\n EBMLId[EBMLId[\"FlagForced\"] = 21930] = \"FlagForced\";\n EBMLId[EBMLId[\"FlagOriginal\"] = 21934] = \"FlagOriginal\";\n EBMLId[EBMLId[\"FlagHearingImpaired\"] = 21931] = \"FlagHearingImpaired\";\n EBMLId[EBMLId[\"FlagVisualImpaired\"] = 21932] = \"FlagVisualImpaired\";\n EBMLId[EBMLId[\"FlagCommentary\"] = 21935] = \"FlagCommentary\";\n EBMLId[EBMLId[\"FlagLacing\"] = 156] = \"FlagLacing\";\n EBMLId[EBMLId[\"Name\"] = 21358] = \"Name\";\n EBMLId[EBMLId[\"Language\"] = 2274716] = \"Language\";\n EBMLId[EBMLId[\"LanguageBCP47\"] = 2274717] = \"LanguageBCP47\";\n EBMLId[EBMLId[\"CodecID\"] = 134] = \"CodecID\";\n EBMLId[EBMLId[\"CodecPrivate\"] = 25506] = \"CodecPrivate\";\n EBMLId[EBMLId[\"CodecDelay\"] = 22186] = \"CodecDelay\";\n EBMLId[EBMLId[\"SeekPreRoll\"] = 22203] = \"SeekPreRoll\";\n EBMLId[EBMLId[\"DefaultDuration\"] = 2352003] = \"DefaultDuration\";\n EBMLId[EBMLId[\"Video\"] = 224] = \"Video\";\n EBMLId[EBMLId[\"PixelWidth\"] = 176] = \"PixelWidth\";\n EBMLId[EBMLId[\"PixelHeight\"] = 186] = \"PixelHeight\";\n EBMLId[EBMLId[\"DisplayWidth\"] = 21680] = \"DisplayWidth\";\n EBMLId[EBMLId[\"DisplayHeight\"] = 21690] = \"DisplayHeight\";\n EBMLId[EBMLId[\"DisplayUnit\"] = 21682] = \"DisplayUnit\";\n EBMLId[EBMLId[\"AlphaMode\"] = 21440] = \"AlphaMode\";\n EBMLId[EBMLId[\"Audio\"] = 225] = \"Audio\";\n EBMLId[EBMLId[\"SamplingFrequency\"] = 181] = \"SamplingFrequency\";\n EBMLId[EBMLId[\"Channels\"] = 159] = \"Channels\";\n EBMLId[EBMLId[\"BitDepth\"] = 25188] = \"BitDepth\";\n EBMLId[EBMLId[\"SimpleBlock\"] = 163] = \"SimpleBlock\";\n EBMLId[EBMLId[\"BlockGroup\"] = 160] = \"BlockGroup\";\n EBMLId[EBMLId[\"Block\"] = 161] = \"Block\";\n EBMLId[EBMLId[\"BlockAdditions\"] = 30113] = \"BlockAdditions\";\n EBMLId[EBMLId[\"BlockMore\"] = 166] = \"BlockMore\";\n EBMLId[EBMLId[\"BlockAdditional\"] = 165] = \"BlockAdditional\";\n EBMLId[EBMLId[\"BlockAddID\"] = 238] = \"BlockAddID\";\n EBMLId[EBMLId[\"BlockDuration\"] = 155] = \"BlockDuration\";\n EBMLId[EBMLId[\"ReferenceBlock\"] = 251] = \"ReferenceBlock\";\n EBMLId[EBMLId[\"Cluster\"] = 524531317] = \"Cluster\";\n EBMLId[EBMLId[\"Timestamp\"] = 231] = \"Timestamp\";\n EBMLId[EBMLId[\"Cues\"] = 475249515] = \"Cues\";\n EBMLId[EBMLId[\"CuePoint\"] = 187] = \"CuePoint\";\n EBMLId[EBMLId[\"CueTime\"] = 179] = \"CueTime\";\n EBMLId[EBMLId[\"CueTrackPositions\"] = 183] = \"CueTrackPositions\";\n EBMLId[EBMLId[\"CueTrack\"] = 247] = \"CueTrack\";\n EBMLId[EBMLId[\"CueClusterPosition\"] = 241] = \"CueClusterPosition\";\n EBMLId[EBMLId[\"Colour\"] = 21936] = \"Colour\";\n EBMLId[EBMLId[\"MatrixCoefficients\"] = 21937] = \"MatrixCoefficients\";\n EBMLId[EBMLId[\"TransferCharacteristics\"] = 21946] = \"TransferCharacteristics\";\n EBMLId[EBMLId[\"Primaries\"] = 21947] = \"Primaries\";\n EBMLId[EBMLId[\"Range\"] = 21945] = \"Range\";\n EBMLId[EBMLId[\"Projection\"] = 30320] = \"Projection\";\n EBMLId[EBMLId[\"ProjectionType\"] = 30321] = \"ProjectionType\";\n EBMLId[EBMLId[\"ProjectionPoseYaw\"] = 30323] = \"ProjectionPoseYaw\";\n EBMLId[EBMLId[\"ProjectionPosePitch\"] = 30324] = \"ProjectionPosePitch\";\n EBMLId[EBMLId[\"ProjectionPoseRoll\"] = 30325] = \"ProjectionPoseRoll\";\n EBMLId[EBMLId[\"Attachments\"] = 423732329] = \"Attachments\";\n EBMLId[EBMLId[\"AttachedFile\"] = 24999] = \"AttachedFile\";\n EBMLId[EBMLId[\"FileDescription\"] = 18046] = \"FileDescription\";\n EBMLId[EBMLId[\"FileName\"] = 18030] = \"FileName\";\n EBMLId[EBMLId[\"FileMediaType\"] = 18016] = \"FileMediaType\";\n EBMLId[EBMLId[\"FileData\"] = 18012] = \"FileData\";\n EBMLId[EBMLId[\"FileUID\"] = 18094] = \"FileUID\";\n EBMLId[EBMLId[\"Chapters\"] = 272869232] = \"Chapters\";\n EBMLId[EBMLId[\"Tags\"] = 307544935] = \"Tags\";\n EBMLId[EBMLId[\"Tag\"] = 29555] = \"Tag\";\n EBMLId[EBMLId[\"Targets\"] = 25536] = \"Targets\";\n EBMLId[EBMLId[\"TargetTypeValue\"] = 26826] = \"TargetTypeValue\";\n EBMLId[EBMLId[\"TargetType\"] = 25546] = \"TargetType\";\n EBMLId[EBMLId[\"TagTrackUID\"] = 25541] = \"TagTrackUID\";\n EBMLId[EBMLId[\"TagEditionUID\"] = 25545] = \"TagEditionUID\";\n EBMLId[EBMLId[\"TagChapterUID\"] = 25540] = \"TagChapterUID\";\n EBMLId[EBMLId[\"TagAttachmentUID\"] = 25542] = \"TagAttachmentUID\";\n EBMLId[EBMLId[\"SimpleTag\"] = 26568] = \"SimpleTag\";\n EBMLId[EBMLId[\"TagName\"] = 17827] = \"TagName\";\n EBMLId[EBMLId[\"TagLanguage\"] = 17530] = \"TagLanguage\";\n EBMLId[EBMLId[\"TagString\"] = 17543] = \"TagString\";\n EBMLId[EBMLId[\"TagBinary\"] = 17541] = \"TagBinary\";\n EBMLId[EBMLId[\"ContentEncodings\"] = 28032] = \"ContentEncodings\";\n EBMLId[EBMLId[\"ContentEncoding\"] = 25152] = \"ContentEncoding\";\n EBMLId[EBMLId[\"ContentEncodingOrder\"] = 20529] = \"ContentEncodingOrder\";\n EBMLId[EBMLId[\"ContentEncodingScope\"] = 20530] = \"ContentEncodingScope\";\n EBMLId[EBMLId[\"ContentCompression\"] = 20532] = \"ContentCompression\";\n EBMLId[EBMLId[\"ContentCompAlgo\"] = 16980] = \"ContentCompAlgo\";\n EBMLId[EBMLId[\"ContentCompSettings\"] = 16981] = \"ContentCompSettings\";\n EBMLId[EBMLId[\"ContentEncryption\"] = 20533] = \"ContentEncryption\";\n })(EBMLId || (EBMLId = {}));\n var LEVEL_0_EBML_IDS = [\n EBMLId.EBML,\n EBMLId.Segment\n ];\n var LEVEL_1_EBML_IDS = [\n EBMLId.SeekHead,\n EBMLId.Info,\n EBMLId.Cluster,\n EBMLId.Tracks,\n EBMLId.Cues,\n EBMLId.Attachments,\n EBMLId.Chapters,\n EBMLId.Tags\n ];\n var LEVEL_0_AND_1_EBML_IDS = [\n ...LEVEL_0_EBML_IDS,\n ...LEVEL_1_EBML_IDS\n ];\n var measureUnsignedInt = (value) => {\n if (value < 1 << 8) {\n return 1;\n } else if (value < 1 << 16) {\n return 2;\n } else if (value < 1 << 24) {\n return 3;\n } else if (value < 2 ** 32) {\n return 4;\n } else if (value < 2 ** 40) {\n return 5;\n } else {\n return 6;\n }\n };\n var measureUnsignedBigInt = (value) => {\n if (value < 1n << 8n) {\n return 1;\n } else if (value < 1n << 16n) {\n return 2;\n } else if (value < 1n << 24n) {\n return 3;\n } else if (value < 1n << 32n) {\n return 4;\n } else if (value < 1n << 40n) {\n return 5;\n } else if (value < 1n << 48n) {\n return 6;\n } else if (value < 1n << 56n) {\n return 7;\n } else {\n return 8;\n }\n };\n var measureSignedInt = (value) => {\n if (value >= -(1 << 6) && value < 1 << 6) {\n return 1;\n } else if (value >= -(1 << 13) && value < 1 << 13) {\n return 2;\n } else if (value >= -(1 << 20) && value < 1 << 20) {\n return 3;\n } else if (value >= -(1 << 27) && value < 1 << 27) {\n return 4;\n } else if (value >= -(2 ** 34) && value < 2 ** 34) {\n return 5;\n } else {\n return 6;\n }\n };\n var measureVarInt = (value) => {\n if (value < (1 << 7) - 1) {\n return 1;\n } else if (value < (1 << 14) - 1) {\n return 2;\n } else if (value < (1 << 21) - 1) {\n return 3;\n } else if (value < (1 << 28) - 1) {\n return 4;\n } else if (value < 2 ** 35 - 1) {\n return 5;\n } else if (value < 2 ** 42 - 1) {\n return 6;\n } else {\n throw new Error(\"EBML varint size not supported \" + value);\n }\n };\n\n class EBMLWriter {\n constructor(writer) {\n this.writer = writer;\n this.helper = new Uint8Array(8);\n this.helperView = new DataView(this.helper.buffer);\n this.offsets = new WeakMap;\n this.dataOffsets = new WeakMap;\n }\n writeByte(value) {\n this.helperView.setUint8(0, value);\n this.writer.write(this.helper.subarray(0, 1));\n }\n writeFloat32(value) {\n this.helperView.setFloat32(0, value, false);\n this.writer.write(this.helper.subarray(0, 4));\n }\n writeFloat64(value) {\n this.helperView.setFloat64(0, value, false);\n this.writer.write(this.helper);\n }\n writeUnsignedInt(value, width = measureUnsignedInt(value)) {\n let pos = 0;\n switch (width) {\n case 6:\n this.helperView.setUint8(pos++, value / 2 ** 40 | 0);\n case 5:\n this.helperView.setUint8(pos++, value / 2 ** 32 | 0);\n case 4:\n this.helperView.setUint8(pos++, value >> 24);\n case 3:\n this.helperView.setUint8(pos++, value >> 16);\n case 2:\n this.helperView.setUint8(pos++, value >> 8);\n case 1:\n this.helperView.setUint8(pos++, value);\n break;\n default:\n throw new Error(\"Bad unsigned int size \" + width);\n }\n this.writer.write(this.helper.subarray(0, pos));\n }\n writeUnsignedBigInt(value, width = measureUnsignedBigInt(value)) {\n let pos = 0;\n for (let i = width - 1;i >= 0; i--) {\n this.helperView.setUint8(pos++, Number(value >> BigInt(i * 8) & 0xffn));\n }\n this.writer.write(this.helper.subarray(0, pos));\n }\n writeSignedInt(value, width = measureSignedInt(value)) {\n if (value < 0) {\n value += 2 ** (width * 8);\n }\n this.writeUnsignedInt(value, width);\n }\n writeVarInt(value, width = measureVarInt(value)) {\n let pos = 0;\n switch (width) {\n case 1:\n this.helperView.setUint8(pos++, 1 << 7 | value);\n break;\n case 2:\n this.helperView.setUint8(pos++, 1 << 6 | value >> 8);\n this.helperView.setUint8(pos++, value);\n break;\n case 3:\n this.helperView.setUint8(pos++, 1 << 5 | value >> 16);\n this.helperView.setUint8(pos++, value >> 8);\n this.helperView.setUint8(pos++, value);\n break;\n case 4:\n this.helperView.setUint8(pos++, 1 << 4 | value >> 24);\n this.helperView.setUint8(pos++, value >> 16);\n this.helperView.setUint8(pos++, value >> 8);\n this.helperView.setUint8(pos++, value);\n break;\n case 5:\n this.helperView.setUint8(pos++, 1 << 3 | value / 2 ** 32 & 7);\n this.helperView.setUint8(pos++, value >> 24);\n this.helperView.setUint8(pos++, value >> 16);\n this.helperView.setUint8(pos++, value >> 8);\n this.helperView.setUint8(pos++, value);\n break;\n case 6:\n this.helperView.setUint8(pos++, 1 << 2 | value / 2 ** 40 & 3);\n this.helperView.setUint8(pos++, value / 2 ** 32 | 0);\n this.helperView.setUint8(pos++, value >> 24);\n this.helperView.setUint8(pos++, value >> 16);\n this.helperView.setUint8(pos++, value >> 8);\n this.helperView.setUint8(pos++, value);\n break;\n default:\n throw new Error(\"Bad EBML varint size \" + width);\n }\n this.writer.write(this.helper.subarray(0, pos));\n }\n writeAsciiString(str) {\n this.writer.write(new Uint8Array(str.split(\"\").map((x) => x.charCodeAt(0))));\n }\n writeEBML(data) {\n if (data === null)\n return;\n if (data instanceof Uint8Array) {\n this.writer.write(data);\n } else if (Array.isArray(data)) {\n for (const elem of data) {\n this.writeEBML(elem);\n }\n } else {\n this.offsets.set(data, this.writer.getPos());\n this.writeUnsignedInt(data.id);\n if (Array.isArray(data.data)) {\n const sizePos = this.writer.getPos();\n const sizeSize = data.size === -1 ? 1 : data.size ?? 4;\n if (data.size === -1) {\n this.writeByte(255);\n } else {\n this.writer.seek(this.writer.getPos() + sizeSize);\n }\n const startPos = this.writer.getPos();\n this.dataOffsets.set(data, startPos);\n this.writeEBML(data.data);\n if (data.size !== -1) {\n const size = this.writer.getPos() - startPos;\n const endPos = this.writer.getPos();\n this.writer.seek(sizePos);\n this.writeVarInt(size, sizeSize);\n this.writer.seek(endPos);\n }\n } else if (typeof data.data === \"number\") {\n const size = data.size ?? measureUnsignedInt(data.data);\n this.writeVarInt(size);\n this.writeUnsignedInt(data.data, size);\n } else if (typeof data.data === \"bigint\") {\n const size = data.size ?? measureUnsignedBigInt(data.data);\n this.writeVarInt(size);\n this.writeUnsignedBigInt(data.data, size);\n } else if (typeof data.data === \"string\") {\n this.writeVarInt(data.data.length);\n this.writeAsciiString(data.data);\n } else if (data.data instanceof Uint8Array) {\n this.writeVarInt(data.data.byteLength, data.size);\n this.writer.write(data.data);\n } else if (data.data instanceof EBMLFloat32) {\n this.writeVarInt(4);\n this.writeFloat32(data.data.value);\n } else if (data.data instanceof EBMLFloat64) {\n this.writeVarInt(8);\n this.writeFloat64(data.data.value);\n } else if (data.data instanceof EBMLSignedInt) {\n const size = data.size ?? measureSignedInt(data.data.value);\n this.writeVarInt(size);\n this.writeSignedInt(data.data.value, size);\n } else if (data.data instanceof EBMLUnicodeString) {\n const bytes = textEncoder.encode(data.data.value);\n this.writeVarInt(bytes.length);\n this.writer.write(bytes);\n } else {\n assertNever(data.data);\n }\n }\n }\n }\n var MAX_VAR_INT_SIZE = 8;\n var MAX_HEADER_SIZE = 2 * MAX_VAR_INT_SIZE;\n var CODEC_STRING_MAP = {\n avc: \"V_MPEG4/ISO/AVC\",\n hevc: \"V_MPEGH/ISO/HEVC\",\n vp8: \"V_VP8\",\n vp9: \"V_VP9\",\n av1: \"V_AV1\",\n prores: \"V_PRORES\",\n aac: \"A_AAC\",\n mp3: \"A_MPEG/L3\",\n opus: \"A_OPUS\",\n vorbis: \"A_VORBIS\",\n flac: \"A_FLAC\",\n ac3: \"A_AC3\",\n eac3: \"A_EAC3\",\n dts: \"A_DTS\",\n \"pcm-u8\": \"A_PCM/INT/LIT\",\n \"pcm-s16\": \"A_PCM/INT/LIT\",\n \"pcm-s16be\": \"A_PCM/INT/BIG\",\n \"pcm-s24\": \"A_PCM/INT/LIT\",\n \"pcm-s24be\": \"A_PCM/INT/BIG\",\n \"pcm-s32\": \"A_PCM/INT/LIT\",\n \"pcm-s32be\": \"A_PCM/INT/BIG\",\n \"pcm-f32\": \"A_PCM/FLOAT/IEEE\",\n \"pcm-f64\": \"A_PCM/FLOAT/IEEE\",\n webvtt: \"S_TEXT/WEBVTT\"\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/matroska/matroska-misc.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var buildMatroskaMimeType = (info) => {\n const base = info.hasVideo ? \"video/\" : info.hasAudio ? \"audio/\" : \"application/\";\n let string = base + (info.isWebM ? \"webm\" : \"x-matroska\");\n if (info.codecStrings.length > 0) {\n const uniqueCodecMimeTypes = [...new Set(info.codecStrings.filter(Boolean))];\n string += `; codecs=\"${uniqueCodecMimeTypes.join(\", \")}\"`;\n }\n return string;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/adts/adts-reader.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var MIN_ADTS_FRAME_HEADER_SIZE = 7;\n var MAX_ADTS_FRAME_HEADER_SIZE = 9;\n var readAdtsFrameHeader = (slice) => {\n const startPos = slice.filePos;\n const bytes = readBytes(slice, 9);\n const bitstream = new Bitstream(bytes);\n const syncword = bitstream.readBits(12);\n if (syncword !== 4095) {\n return null;\n }\n bitstream.skipBits(1);\n const layer = bitstream.readBits(2);\n if (layer !== 0) {\n return null;\n }\n const protectionAbsence = bitstream.readBits(1);\n const objectType = bitstream.readBits(2) + 1;\n const samplingFrequencyIndex = bitstream.readBits(4);\n if (samplingFrequencyIndex === 15) {\n return null;\n }\n bitstream.skipBits(1);\n const channelConfiguration = bitstream.readBits(3);\n if (channelConfiguration === 0) {\n throw new Error(\"ADTS frames with channel configuration 0 are not supported.\");\n }\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n bitstream.skipBits(1);\n const frameLength = bitstream.readBits(13);\n bitstream.skipBits(11);\n const numberOfAacFrames = bitstream.readBits(2) + 1;\n if (numberOfAacFrames !== 1) {\n throw new Error(\"ADTS frames with more than one AAC frame are not supported.\");\n }\n let crcCheck = null;\n if (protectionAbsence === 1) {\n slice.filePos -= 2;\n } else {\n crcCheck = bitstream.readBits(16);\n }\n return {\n objectType,\n samplingFrequencyIndex,\n channelConfiguration,\n frameLength,\n numberOfAacFrames,\n crcCheck,\n startPos\n };\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/sample.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var __addDisposableResource = function(env, value, async) {\n if (value !== null && value !== undefined) {\n if (typeof value !== \"object\" && typeof value !== \"function\")\n throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose)\n throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === undefined) {\n if (!Symbol.dispose)\n throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async)\n inner = dispose;\n }\n if (typeof dispose !== \"function\")\n throw new TypeError(\"Object not disposable.\");\n if (inner)\n dispose = function() {\n try {\n inner.call(this);\n } catch (e) {\n return Promise.reject(e);\n }\n };\n env.stack.push({ value, dispose, async });\n } else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n };\n var __disposeResources = function(SuppressedError2) {\n return function(env) {\n function fail(e) {\n env.error = env.hasError ? new SuppressedError2(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1)\n return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async)\n return s |= 2, Promise.resolve(result).then(next, function(e) {\n fail(e);\n return next();\n });\n } else\n s |= 1;\n } catch (e) {\n fail(e);\n }\n }\n if (s === 1)\n return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError)\n throw env.error;\n }\n return next();\n };\n }(typeof SuppressedError === \"function\" ? SuppressedError : function(error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n });\n polyfillSymbolDispose();\n var lastVideoGcErrorLog = -Infinity;\n var lastAudioGcErrorLog = -Infinity;\n var finalizationRegistry = null;\n if (typeof FinalizationRegistry !== \"undefined\") {\n finalizationRegistry = new FinalizationRegistry((value) => {\n const now = performance.now();\n if (value.type === \"video\") {\n if (now - lastVideoGcErrorLog >= 1000) {\n Logging._error(`A VideoSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your VideoSamples as soon as you're done using them.`);\n lastVideoGcErrorLog = now;\n }\n if (typeof VideoFrame !== \"undefined\" && value.data instanceof VideoFrame) {\n value.data.close();\n }\n } else {\n if (now - lastAudioGcErrorLog >= 1000) {\n Logging._error(`An AudioSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your AudioSamples as soon as you're done using them.`);\n lastAudioGcErrorLog = now;\n }\n if (typeof AudioData !== \"undefined\" && value.data instanceof AudioData) {\n value.data.close();\n }\n }\n });\n }\n\n class VideoSampleResource {\n constructor() {\n this._referenceCount = 0;\n this._lastAllocationBuffer = null;\n }\n }\n var VIDEO_SAMPLE_PIXEL_FORMATS = [\n \"I420\",\n \"I420P10\",\n \"I420P12\",\n \"I420A\",\n \"I420AP10\",\n \"I420AP12\",\n \"I422\",\n \"I422P10\",\n \"I422P12\",\n \"I422A\",\n \"I422AP10\",\n \"I422AP12\",\n \"I444\",\n \"I444P10\",\n \"I444P12\",\n \"I444A\",\n \"I444AP10\",\n \"I444AP12\",\n \"NV12\",\n \"RGBA\",\n \"RGBX\",\n \"BGRA\",\n \"BGRX\"\n ];\n var VIDEO_SAMPLE_PIXEL_FORMATS_SET = new Set(VIDEO_SAMPLE_PIXEL_FORMATS);\n\n class VideoSample {\n get codedWidth() {\n return this.visibleRect.width;\n }\n get codedHeight() {\n return this.visibleRect.height;\n }\n get displayWidth() {\n return this.rotation % 180 === 0 ? this.squarePixelWidth : this.squarePixelHeight;\n }\n get displayHeight() {\n return this.rotation % 180 === 0 ? this.squarePixelHeight : this.squarePixelWidth;\n }\n get microsecondTimestamp() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);\n }\n get microsecondDuration() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);\n }\n get hasAlpha() {\n return this.format && this.format.includes(\"A\");\n }\n constructor(data, init) {\n this._closed = false;\n if (data instanceof ArrayBuffer || typeof SharedArrayBuffer !== \"undefined\" && data instanceof SharedArrayBuffer || ArrayBuffer.isView(data)) {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"init must be an object.\");\n }\n if (init.format === undefined || !VIDEO_SAMPLE_PIXEL_FORMATS_SET.has(init.format)) {\n throw new TypeError(\"init.format must be one of: \" + VIDEO_SAMPLE_PIXEL_FORMATS.join(\", \"));\n }\n if (!Number.isInteger(init.codedWidth) || init.codedWidth <= 0) {\n throw new TypeError(\"init.codedWidth must be a positive integer.\");\n }\n if (!Number.isInteger(init.codedHeight) || init.codedHeight <= 0) {\n throw new TypeError(\"init.codedHeight must be a positive integer.\");\n }\n if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {\n throw new TypeError(\"init.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (init.flip !== undefined && typeof init.flip !== \"boolean\") {\n throw new TypeError(\"init.flip, when provided, must be a boolean.\");\n }\n if (!Number.isFinite(init.timestamp)) {\n throw new TypeError(\"init.timestamp must be a number.\");\n }\n if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {\n throw new TypeError(\"init.duration, when provided, must be a non-negative number.\");\n }\n if (init.layout !== undefined) {\n if (!Array.isArray(init.layout)) {\n throw new TypeError(\"init.layout, when provided, must be an array.\");\n }\n for (const plane of init.layout) {\n if (!plane || typeof plane !== \"object\" || Array.isArray(plane)) {\n throw new TypeError(\"Each entry in init.layout must be an object.\");\n }\n if (!Number.isInteger(plane.offset) || plane.offset < 0) {\n throw new TypeError(\"plane.offset must be a non-negative integer.\");\n }\n if (!Number.isInteger(plane.stride) || plane.stride < 0) {\n throw new TypeError(\"plane.stride must be a non-negative integer.\");\n }\n }\n }\n if (init.visibleRect !== undefined) {\n validateRectangle(init.visibleRect, \"init.visibleRect\");\n }\n if (init.displayWidth !== undefined && (!Number.isInteger(init.displayWidth) || init.displayWidth <= 0)) {\n throw new TypeError(\"init.displayWidth, when provided, must be a positive integer.\");\n }\n if (init.displayHeight !== undefined && (!Number.isInteger(init.displayHeight) || init.displayHeight <= 0)) {\n throw new TypeError(\"init.displayHeight, when provided, must be a positive integer.\");\n }\n if (init.displayWidth !== undefined !== (init.displayHeight !== undefined)) {\n throw new TypeError(\"init.displayWidth and init.displayHeight must be either both provided or both omitted.\");\n }\n this.format = init.format;\n this.rotation = init.rotation ?? 0;\n this.flip = init.flip ?? false;\n this.timestamp = init.timestamp;\n this.duration = init.duration ?? 0;\n const layout = init.layout ?? createDefaultPlaneLayout(init.format, init.codedWidth, init.codedHeight);\n let colorSpaceInit = init.colorSpace ?? null;\n if (colorSpaceInit === null) {\n if (this.format === \"RGBA\" || this.format === \"RGBX\" || this.format === \"BGRA\" || this.format === \"BGRX\") {\n colorSpaceInit = {\n primaries: \"bt709\",\n transfer: \"iec61966-2-1\",\n matrix: \"rgb\",\n fullRange: true\n };\n } else {\n colorSpaceInit = {\n primaries: \"bt709\",\n transfer: \"bt709\",\n matrix: \"bt709\",\n fullRange: false\n };\n }\n }\n this.visibleRect = {\n left: init.visibleRect?.left ?? 0,\n top: init.visibleRect?.top ?? 0,\n width: init.visibleRect?.width ?? init.codedWidth,\n height: init.visibleRect?.height ?? init.codedHeight\n };\n if (init.displayWidth !== undefined) {\n this.squarePixelWidth = this.rotation % 180 === 0 ? init.displayWidth : init.displayHeight;\n this.squarePixelHeight = this.rotation % 180 === 0 ? init.displayHeight : init.displayWidth;\n } else {\n this.squarePixelWidth = this.visibleRect.width;\n this.squarePixelHeight = this.visibleRect.height;\n }\n this._data = init._doNotCopy ? toUint8Array(data) : toUint8Array(data).slice();\n this._layout = layout;\n this.colorSpace = new VideoSampleColorSpace(colorSpaceInit);\n } else if (typeof VideoFrame !== \"undefined\" && data instanceof VideoFrame) {\n if (init?.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {\n throw new TypeError(\"init.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (init?.flip !== undefined && typeof init.flip !== \"boolean\") {\n throw new TypeError(\"init.flip, when provided, must be a boolean.\");\n }\n if (init?.timestamp !== undefined && !Number.isFinite(init?.timestamp)) {\n throw new TypeError(\"init.timestamp, when provided, must be a number.\");\n }\n if (init?.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {\n throw new TypeError(\"init.duration, when provided, must be a non-negative number.\");\n }\n if (init?.visibleRect !== undefined) {\n validateRectangle(init.visibleRect, \"init.visibleRect\");\n }\n this._data = data;\n this._layout = null;\n this.format = data.format;\n this.visibleRect = {\n left: data.visibleRect?.x ?? 0,\n top: data.visibleRect?.y ?? 0,\n width: data.visibleRect?.width ?? data.codedWidth,\n height: data.visibleRect?.height ?? data.codedHeight\n };\n this.rotation = init?.rotation ?? 0;\n this.flip = init?.flip ?? false;\n this.squarePixelWidth = data.displayWidth;\n this.squarePixelHeight = data.displayHeight;\n this.timestamp = init?.timestamp ?? data.timestamp / 1e6;\n this.duration = init?.duration ?? (data.duration ?? 0) / 1e6;\n this.colorSpace = new VideoSampleColorSpace(data.colorSpace);\n } else if (typeof HTMLImageElement !== \"undefined\" && data instanceof HTMLImageElement || typeof SVGImageElement !== \"undefined\" && data instanceof SVGImageElement || typeof ImageBitmap !== \"undefined\" && data instanceof ImageBitmap || typeof HTMLVideoElement !== \"undefined\" && data instanceof HTMLVideoElement || typeof HTMLCanvasElement !== \"undefined\" && data instanceof HTMLCanvasElement || typeof OffscreenCanvas !== \"undefined\" && data instanceof OffscreenCanvas) {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"init must be an object.\");\n }\n if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {\n throw new TypeError(\"init.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (init.flip !== undefined && typeof init.flip !== \"boolean\") {\n throw new TypeError(\"init.flip, when provided, must be a boolean.\");\n }\n if (!Number.isFinite(init.timestamp)) {\n throw new TypeError(\"init.timestamp must be a number.\");\n }\n if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {\n throw new TypeError(\"init.duration, when provided, must be a non-negative number.\");\n }\n if (init.visibleRect !== undefined) {\n validateRectangle(init.visibleRect, \"init.visibleRect\");\n }\n if (typeof VideoFrame !== \"undefined\") {\n return new VideoSample(new VideoFrame(data, {\n timestamp: Math.trunc(init.timestamp * SECOND_TO_MICROSECOND_FACTOR),\n duration: Math.trunc((init.duration ?? 0) * SECOND_TO_MICROSECOND_FACTOR) || undefined,\n visibleRect: init.visibleRect && {\n x: init.visibleRect.left,\n y: init.visibleRect.top,\n width: init.visibleRect.width,\n height: init.visibleRect.height\n }\n }), init);\n }\n let width = 0;\n let height = 0;\n if (\"naturalWidth\" in data) {\n width = data.naturalWidth;\n height = data.naturalHeight;\n } else if (\"videoWidth\" in data) {\n width = data.videoWidth;\n height = data.videoHeight;\n } else if (\"width\" in data) {\n width = Number(data.width);\n height = Number(data.height);\n }\n if (!width || !height) {\n throw new TypeError(\"Could not determine dimensions.\");\n }\n const visibleRect = init.visibleRect ?? { left: 0, top: 0, width, height };\n const canvas = new OffscreenCanvas(visibleRect.width, visibleRect.height);\n const context = canvas.getContext(\"2d\", {\n alpha: isFirefox(),\n willReadFrequently: true\n });\n if (!context) {\n throw new Error(\"OffscreenCanvas must have support for the '2d' context in order to create a VideoSample from\" + \" this data.\");\n }\n context.drawImage(data, -visibleRect.left, -visibleRect.top);\n this._data = canvas;\n this._layout = null;\n this.format = \"RGBX\";\n this.visibleRect = { left: 0, top: 0, width: visibleRect.width, height: visibleRect.height };\n this.squarePixelWidth = visibleRect.width;\n this.squarePixelHeight = visibleRect.height;\n this.rotation = init.rotation ?? 0;\n this.flip = init.flip ?? false;\n this.timestamp = init.timestamp;\n this.duration = init.duration ?? 0;\n this.colorSpace = new VideoSampleColorSpace({\n matrix: \"rgb\",\n primaries: \"bt709\",\n transfer: \"iec61966-2-1\",\n fullRange: true\n });\n } else if (data instanceof VideoSampleResource) {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"init must be an object.\");\n }\n if (init.rotation !== undefined && ![0, 90, 180, 270].includes(init.rotation)) {\n throw new TypeError(\"init.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (init.flip !== undefined && typeof init.flip !== \"boolean\") {\n throw new TypeError(\"init.flip, when provided, must be a boolean.\");\n }\n if (!Number.isFinite(init.timestamp)) {\n throw new TypeError(\"init.timestamp must be a number.\");\n }\n if (init.duration !== undefined && (!Number.isFinite(init.duration) || init.duration < 0)) {\n throw new TypeError(\"init.duration, when provided, must be a non-negative number.\");\n }\n this._data = data;\n data._referenceCount++;\n this.format = data.getFormat();\n if (this.format !== null && !VIDEO_SAMPLE_PIXEL_FORMATS.includes(this.format)) {\n throw new TypeError(\"getFormat() must return a VideoSamplePixelFormat or null.\");\n }\n this.visibleRect = {\n left: 0,\n top: 0,\n width: data.getCodedWidth(),\n height: data.getCodedHeight()\n };\n if (!Number.isInteger(this.visibleRect.width) || this.visibleRect.width <= 0) {\n throw new TypeError(\"getCodedWidth() must return a positive integer.\");\n }\n if (!Number.isInteger(this.visibleRect.height) || this.visibleRect.height <= 0) {\n throw new TypeError(\"getCodedHeight() must return a positive integer.\");\n }\n this.squarePixelWidth = data.getSquarePixelWidth();\n if (!Number.isInteger(this.squarePixelWidth) || this.squarePixelWidth <= 0) {\n throw new TypeError(\"getSquarePixelWidth() must return a positive integer.\");\n }\n this.squarePixelHeight = data.getSquarePixelHeight();\n if (!Number.isInteger(this.squarePixelHeight) || this.squarePixelHeight <= 0) {\n throw new TypeError(\"getSquarePixelHeight() must return a positive integer.\");\n }\n this.rotation = init.rotation ?? 0;\n this.flip = init.flip ?? false;\n this.timestamp = init.timestamp;\n this.duration = init.duration ?? 0;\n this.colorSpace = data.getColorSpace();\n } else {\n throw new TypeError(\"Invalid data type: Must be a BufferSource, CanvasImageSource, or VideoSampleResource.\");\n }\n this.encodeOptions = init?.encodeOptions ?? {};\n this.pixelAspectRatio = simplifyRational({\n num: this.squarePixelWidth * this.codedHeight,\n den: this.squarePixelHeight * this.codedWidth\n });\n finalizationRegistry?.register(this, { type: \"video\", data: this._data }, this);\n }\n clone() {\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n assert(this._data !== null);\n if (this._data instanceof VideoSampleResource) {\n return new VideoSample(this._data, {\n timestamp: this.timestamp,\n duration: this.duration,\n rotation: this.rotation,\n flip: this.flip,\n encodeOptions: this.encodeOptions\n });\n } else if (isVideoFrame(this._data)) {\n return new VideoSample(this._data.clone(), {\n timestamp: this.timestamp,\n duration: this.duration,\n rotation: this.rotation,\n flip: this.flip,\n encodeOptions: this.encodeOptions\n });\n } else if (this._data instanceof Uint8Array) {\n assert(this._layout);\n return new VideoSample(this._data, {\n format: this.format,\n layout: this._layout,\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n timestamp: this.timestamp,\n duration: this.duration,\n colorSpace: this.colorSpace,\n rotation: this.rotation,\n flip: this.flip,\n visibleRect: this.visibleRect,\n displayWidth: this.displayWidth,\n displayHeight: this.displayHeight,\n encodeOptions: this.encodeOptions,\n _doNotCopy: true\n });\n } else {\n return new VideoSample(this._data, {\n format: this.format,\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n timestamp: this.timestamp,\n duration: this.duration,\n colorSpace: this.colorSpace,\n rotation: this.rotation,\n flip: this.flip,\n visibleRect: this.visibleRect,\n displayWidth: this.displayWidth,\n displayHeight: this.displayHeight,\n encodeOptions: this.encodeOptions\n });\n }\n }\n close() {\n if (this._closed) {\n return;\n }\n finalizationRegistry?.unregister(this);\n if (this._data instanceof VideoSampleResource) {\n this._data._referenceCount--;\n if (this._data._referenceCount === 0) {\n this._data.close();\n }\n } else if (isVideoFrame(this._data)) {\n this._data.close();\n } else {\n this._data = null;\n }\n this._closed = true;\n }\n allocationSize(options = {}) {\n validateVideoFrameCopyToOptions(options);\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n if ((options.format ?? this.format) == null) {\n throw new Error(\"Cannot get allocation size when format is null.\");\n }\n if (isVideoFrame(this._data)) {\n return this._data.allocationSize(options);\n }\n const combinedLayout = ParseVideoFrameCopyToOptions(this, options);\n return combinedLayout.allocationSize;\n }\n async copyTo(destination, options = {}) {\n if (!isAllowSharedBufferSource(destination)) {\n throw new TypeError(\"destination must be an ArrayBuffer or an ArrayBuffer view.\");\n }\n validateVideoFrameCopyToOptions(options);\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n if ((options.format ?? this.format) == null) {\n throw new Error(\"Cannot copy video sample data when format is null.\");\n }\n assert(this._data !== null);\n if (isVideoFrame(this._data)) {\n return this._data.copyTo(destination, options);\n }\n if (options.format && ![\"RGBA\", \"RGBX\", \"BGRA\", \"BGRX\"].includes(this.format) && [\"RGBA\", \"RGBX\", \"BGRA\", \"BGRX\"].includes(options.format)) {\n if (this._data instanceof VideoSampleResource) {\n const env_1 = { stack: [], error: undefined, hasError: false };\n try {\n const rgbSample = __addDisposableResource(env_1, await this._data.toRgbSample({\n timestamp: this.timestamp,\n duration: this.duration,\n rotation: this.rotation,\n flip: this.flip\n }, options.colorSpace ?? \"srgb\"), false);\n if (!(rgbSample instanceof VideoSample)) {\n throw new TypeError(\"toRgbSample() must return a VideoSample.\");\n }\n if (![\"RGBA\", \"RGBX\", \"BGRA\", \"BGRX\"].includes(rgbSample.format)) {\n throw new Error(`Sample returned by toRgbSample was expected to have an RGB format, got` + ` '${rgbSample.format}' instead.`);\n }\n return await rgbSample.copyTo(destination, options);\n } catch (e_1) {\n env_1.error = e_1;\n env_1.hasError = true;\n } finally {\n __disposeResources(env_1);\n }\n } else {\n if (typeof VideoFrame === \"undefined\") {\n throw new Error(\"For this sample, converting from a non-RGB to an RGB format requires VideoFrame to\" + \" be defined.\");\n }\n const tempFrame = this.toVideoFrame();\n const result = await tempFrame.copyTo(destination, options);\n tempFrame.close();\n return result;\n }\n }\n const combinedLayout = ParseVideoFrameCopyToOptions(this, options);\n assert(this.format);\n const destBytes = toUint8Array(destination);\n if (destBytes.byteLength < combinedLayout.allocationSize) {\n throw new TypeError(`Destination buffer too small. Required: ${combinedLayout.allocationSize},` + ` Available: ${destBytes.byteLength}`);\n }\n const planeConfigs = getPlaneConfigs(this.format);\n let dataPlanes;\n if (this._data instanceof VideoSampleResource) {\n let result = this._data.getDataPlanes();\n if (isThenable(result))\n result = await result;\n if (!Array.isArray(result) || result.some((x) => !(x.data instanceof Uint8Array) || !Number.isInteger(x.stride) || x.stride < 0)) {\n throw new TypeError('getDataPlanes() must return an array of objects with a Uint8Array \"data\" property and a' + ' non-negative integer \"stride\" property.');\n }\n dataPlanes = result;\n } else if (this._data instanceof Uint8Array) {\n assert(this._layout);\n assert(this._layout.length === planeConfigs.length);\n dataPlanes = this._layout.map((planeLayout, i) => {\n const height = Math.ceil(this.codedHeight / planeConfigs[i].heightDivisor);\n return {\n data: this._data.subarray(planeLayout.offset, planeLayout.offset + planeLayout.stride * height),\n stride: planeLayout.stride\n };\n });\n } else {\n const canvas = this._data;\n const context = canvas.getContext(\"2d\");\n assert(context);\n const imageData = context.getImageData(0, 0, this.codedWidth, this.codedHeight);\n dataPlanes = [{\n data: toUint8Array(imageData.data),\n stride: 4 * this.codedWidth\n }];\n }\n const planeLayouts = [];\n const numPlanes = planeConfigs.length;\n for (let planeIndex = 0;planeIndex < numPlanes; planeIndex++) {\n const computedLayout = combinedLayout.computedLayouts[planeIndex];\n const sourceStride = dataPlanes[planeIndex].stride;\n const sourceData = dataPlanes[planeIndex].data;\n let sourceOffset = computedLayout.sourceTop * sourceStride;\n sourceOffset += computedLayout.sourceLeftBytes;\n let destinationOffset = computedLayout.destinationOffset;\n const rowBytes = computedLayout.sourceWidthBytes;\n const layout = {\n offset: destinationOffset,\n stride: computedLayout.destinationStride\n };\n for (let row = 0;row < computedLayout.sourceHeight; row++) {\n if (sourceOffset + rowBytes > sourceData.byteLength) {\n throw new Error(`Source buffer OOB read.`);\n }\n if (destinationOffset + rowBytes > destBytes.byteLength) {\n throw new Error(`Destination buffer OOB write.`);\n }\n const srcSub = sourceData.subarray(sourceOffset, sourceOffset + rowBytes);\n destBytes.set(srcSub, destinationOffset);\n sourceOffset += sourceStride;\n destinationOffset += computedLayout.destinationStride;\n }\n planeLayouts.push(layout);\n }\n if (options.format !== undefined) {\n const needsRgbConversion = this.format.startsWith(\"RGB\") !== options.format.startsWith(\"RGB\");\n const needsAlphaConversion = this.format.includes(\"X\") && options.format.includes(\"A\");\n if (needsRgbConversion || needsAlphaConversion) {\n for (let i = 0;i < combinedLayout.allocationSize; i += 4) {\n if (needsRgbConversion) {\n const r = destBytes[i];\n const b = destBytes[i + 2];\n destBytes[i] = b;\n destBytes[i + 2] = r;\n }\n if (needsAlphaConversion) {\n destBytes[i + 3] = 255;\n }\n }\n }\n }\n return planeLayouts;\n }\n toVideoFrame() {\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n assert(this._data !== null);\n if (this._data instanceof VideoSampleResource) {\n if (this.format === null) {\n throw new Error(\"Cannot convert a VideoSampleResource-backed VideoSample to VideoFrame if format is null.\");\n }\n const planes = this._data.getDataPlanes();\n if (isThenable(planes)) {\n throw new Error(\"Cannot convert a VideoSampleResource-backed VideoSample to VideoFrame if getDataPlanes() returns\" + \" a promise.\");\n }\n const size = planes.reduce((a, b) => a + b.data.byteLength, 0);\n const buffer = new Uint8Array(size);\n let offset = 0;\n const offsets = [];\n for (const plane of planes) {\n buffer.set(plane.data, offset);\n offsets.push(offset);\n offset += plane.data.byteLength;\n }\n return new VideoFrame(buffer, {\n format: this.format,\n layout: planes.map((x, i) => ({\n offset: offsets[i],\n stride: x.stride\n })),\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration,\n colorSpace: this.colorSpace,\n visibleRect: this.visibleRect,\n displayWidth: this.squarePixelWidth,\n displayHeight: this.squarePixelHeight\n });\n } else if (isVideoFrame(this._data)) {\n return new VideoFrame(this._data, {\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration || undefined\n });\n } else if (this._data instanceof Uint8Array) {\n assert(this._layout);\n return new VideoFrame(this._data, {\n format: this.format,\n codedWidth: this.codedWidth,\n codedHeight: this.codedHeight,\n layout: this._layout,\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration || undefined,\n colorSpace: this.colorSpace,\n visibleRect: this.visibleRect,\n displayWidth: this.squarePixelWidth,\n displayHeight: this.squarePixelHeight\n });\n } else {\n return new VideoFrame(this._data, {\n timestamp: this.microsecondTimestamp,\n duration: this.microsecondDuration || undefined\n });\n }\n }\n draw(context, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) {\n let sx = 0;\n let sy = 0;\n let sWidth = this.displayWidth;\n let sHeight = this.displayHeight;\n let dx = 0;\n let dy = 0;\n let dWidth = this.displayWidth;\n let dHeight = this.displayHeight;\n if (arg5 !== undefined) {\n sx = arg1;\n sy = arg2;\n sWidth = arg3;\n sHeight = arg4;\n dx = arg5;\n dy = arg6;\n if (arg7 !== undefined) {\n dWidth = arg7;\n dHeight = arg8;\n } else {\n dWidth = sWidth;\n dHeight = sHeight;\n }\n } else {\n dx = arg1;\n dy = arg2;\n if (arg3 !== undefined) {\n dWidth = arg3;\n dHeight = arg4;\n }\n }\n if (!(typeof CanvasRenderingContext2D !== \"undefined\" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== \"undefined\" && context instanceof OffscreenCanvasRenderingContext2D)) {\n throw new TypeError(\"context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D.\");\n }\n if (!Number.isFinite(sx)) {\n throw new TypeError(\"sx must be a number.\");\n }\n if (!Number.isFinite(sy)) {\n throw new TypeError(\"sy must be a number.\");\n }\n if (!Number.isFinite(sWidth) || sWidth < 0) {\n throw new TypeError(\"sWidth must be a non-negative number.\");\n }\n if (!Number.isFinite(sHeight) || sHeight < 0) {\n throw new TypeError(\"sHeight must be a non-negative number.\");\n }\n if (!Number.isFinite(dx)) {\n throw new TypeError(\"dx must be a number.\");\n }\n if (!Number.isFinite(dy)) {\n throw new TypeError(\"dy must be a number.\");\n }\n if (!Number.isFinite(dWidth) || dWidth < 0) {\n throw new TypeError(\"dWidth must be a non-negative number.\");\n }\n if (!Number.isFinite(dHeight) || dHeight < 0) {\n throw new TypeError(\"dHeight must be a non-negative number.\");\n }\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n ({ sx, sy, sWidth, sHeight } = this._unmapSourceRegion(sx, sy, sWidth, sHeight, this.rotation, this.flip));\n const source = this.toCanvasImageSource();\n context.save();\n const centerX = dx + dWidth / 2;\n const centerY = dy + dHeight / 2;\n context.translate(centerX, centerY);\n if (this.flip) {\n context.scale(-1, 1);\n }\n context.rotate(this.rotation * Math.PI / 180);\n const aspectRatioChange = this.rotation % 180 === 0 ? 1 : dWidth / dHeight;\n context.scale(1 / aspectRatioChange, aspectRatioChange);\n context.drawImage(source, sx, sy, sWidth, sHeight, -dWidth / 2, -dHeight / 2, dWidth, dHeight);\n context.restore();\n }\n drawWithFit(context, options) {\n if (!(typeof CanvasRenderingContext2D !== \"undefined\" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== \"undefined\" && context instanceof OffscreenCanvasRenderingContext2D)) {\n throw new TypeError(\"context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D.\");\n }\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (![\"fill\", \"contain\", \"cover\"].includes(options.fit)) {\n throw new TypeError(\"options.fit must be 'fill', 'contain', or 'cover'.\");\n }\n if (options.rotation !== undefined && ![0, 90, 180, 270].includes(options.rotation)) {\n throw new TypeError(\"options.rotation, when provided, must be 0, 90, 180, or 270.\");\n }\n if (options.flip !== undefined && typeof options.flip !== \"boolean\") {\n throw new TypeError(\"options.flip, when provided, must be a boolean.\");\n }\n if (options.crop !== undefined) {\n validateCropRectangle(options.crop, \"options.\");\n }\n const canvasWidth = context.canvas.width;\n const canvasHeight = context.canvas.height;\n const rotation = options.rotation ?? this.rotation;\n const flip = options.flip ?? this.flip;\n const [rotatedWidth, rotatedHeight] = rotation % 180 === 0 ? [this.squarePixelWidth, this.squarePixelHeight] : [this.squarePixelHeight, this.squarePixelWidth];\n let finalCrop = options.crop;\n if (finalCrop) {\n finalCrop = clampCropRectangle(finalCrop, rotatedWidth, rotatedHeight);\n }\n let dx;\n let dy;\n let newWidth;\n let newHeight;\n const { sx, sy, sWidth, sHeight } = this._unmapSourceRegion(options.crop?.left ?? 0, options.crop?.top ?? 0, options.crop?.width ?? rotatedWidth, options.crop?.height ?? rotatedHeight, rotation, flip);\n if (options.fit === \"fill\") {\n dx = 0;\n dy = 0;\n newWidth = canvasWidth;\n newHeight = canvasHeight;\n } else {\n const [sampleWidth, sampleHeight] = options.crop ? [options.crop.width, options.crop.height] : [rotatedWidth, rotatedHeight];\n const scale = options.fit === \"contain\" ? Math.min(canvasWidth / sampleWidth, canvasHeight / sampleHeight) : Math.max(canvasWidth / sampleWidth, canvasHeight / sampleHeight);\n newWidth = sampleWidth * scale;\n newHeight = sampleHeight * scale;\n dx = (canvasWidth - newWidth) / 2;\n dy = (canvasHeight - newHeight) / 2;\n }\n context.save();\n const aspectRatioChange = rotation % 180 === 0 ? 1 : newWidth / newHeight;\n context.translate(canvasWidth / 2, canvasHeight / 2);\n if (flip) {\n context.scale(-1, 1);\n }\n context.rotate(rotation * Math.PI / 180);\n context.scale(1 / aspectRatioChange, aspectRatioChange);\n context.translate(-canvasWidth / 2, -canvasHeight / 2);\n context.drawImage(this.toCanvasImageSource(), sx, sy, sWidth, sHeight, dx, dy, newWidth, newHeight);\n context.restore();\n }\n _unmapSourceRegion(sx, sy, sWidth, sHeight, rotation, flip) {\n if (flip) {\n const rotatedWidth = rotation % 180 === 0 ? this.squarePixelWidth : this.squarePixelHeight;\n sx = rotatedWidth - sx - sWidth;\n }\n if (rotation === 90) {\n [sx, sy, sWidth, sHeight] = [\n sy,\n this.squarePixelHeight - sx - sWidth,\n sHeight,\n sWidth\n ];\n } else if (rotation === 180) {\n [sx, sy] = [\n this.squarePixelWidth - sx - sWidth,\n this.squarePixelHeight - sy - sHeight\n ];\n } else if (rotation === 270) {\n [sx, sy, sWidth, sHeight] = [\n this.squarePixelWidth - sy - sHeight,\n sx,\n sHeight,\n sWidth\n ];\n }\n return { sx, sy, sWidth, sHeight };\n }\n _drawWithFitAndMipmapping(targetCanvas, targetContext, options) {\n const targetWidth = targetCanvas.width;\n const targetHeight = targetCanvas.height;\n const [rotatedWidth, rotatedHeight] = options.rotation % 180 === 0 ? [this.squarePixelWidth, this.squarePixelHeight] : [this.squarePixelHeight, this.squarePixelWidth];\n const sourceWidth = options.crop ? options.crop.width : rotatedWidth;\n const sourceHeight = options.crop ? options.crop.height : rotatedHeight;\n let mipLevels = 0;\n if (2 * targetWidth < sourceWidth && 2 * targetHeight < sourceHeight) {\n mipLevels = Math.floor(Math.log2(Math.min(sourceWidth / targetWidth, sourceHeight / targetHeight)));\n }\n const drawWidth = targetWidth * 2 ** mipLevels;\n const drawHeight = targetHeight * 2 ** mipLevels;\n const { canvas, context, isNew } = mipLevels > 0 ? getTransformationCanvas(drawWidth, drawHeight) : { canvas: targetCanvas, context: targetContext, isNew: options.targetIsFresh };\n context.imageSmoothingQuality = \"high\";\n if (options.fillBlack) {\n context.fillStyle = \"black\";\n context.fillRect(0, 0, drawWidth, drawHeight);\n } else if (!isNew) {\n context.clearRect(0, 0, drawWidth, drawHeight);\n }\n this.drawWithFit(context, {\n fit: options.fit,\n rotation: options.rotation,\n flip: options.flip,\n crop: options.crop\n });\n context.globalCompositeOperation = \"copy\";\n for (let i = mipLevels;i > 1; i--) {\n const levelWidth = targetWidth * 2 ** i;\n const levelHeight = targetHeight * 2 ** i;\n context.drawImage(canvas, 0, 0, levelWidth, levelHeight, 0, 0, levelWidth / 2, levelHeight / 2);\n }\n context.globalCompositeOperation = \"source-over\";\n if (mipLevels > 0) {\n targetContext.imageSmoothingQuality = \"high\";\n targetContext.globalCompositeOperation = \"copy\";\n targetContext.drawImage(canvas, 0, 0, 2 * targetWidth, 2 * targetHeight, 0, 0, targetWidth, targetHeight);\n targetContext.globalCompositeOperation = \"source-over\";\n }\n }\n toCanvasImageSource() {\n if (this._closed) {\n throw new Error(\"VideoSample is closed.\");\n }\n assert(this._data !== null);\n if (this._data instanceof VideoSampleResource || this._data instanceof Uint8Array) {\n const videoFrame = this.toVideoFrame();\n queueMicrotask(() => videoFrame.close());\n return videoFrame;\n } else {\n return this._data;\n }\n }\n async transform(options) {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (options.width !== undefined && (!Number.isInteger(options.width) || options.width <= 0)) {\n throw new TypeError(\"options.width, when provided, must be a positive integer.\");\n }\n if (options.height !== undefined && (!Number.isInteger(options.height) || options.height <= 0)) {\n throw new TypeError(\"options.height, when provided, must be a positive integer.\");\n }\n if (options.roundDimensionsTo !== undefined && (!Number.isInteger(options.roundDimensionsTo) || options.roundDimensionsTo <= 0)) {\n throw new TypeError(\"options.roundDimensionsTo, when provided, must be a positive integer.\");\n }\n if (options.fit !== undefined && ![\"fill\", \"contain\", \"cover\"].includes(options.fit)) {\n throw new TypeError('options.fit, when provided, must be one of \"fill\", \"contain\", or \"cover\".');\n }\n if (options.width !== undefined && options.height !== undefined && options.fit === undefined) {\n throw new TypeError(\"When both options.width and options.height are provided, options.fit must also be provided.\");\n }\n if (options.rotate !== undefined && ![0, 90, 180, 270].includes(options.rotate)) {\n throw new TypeError(\"options.rotate, when provided, must be 0, 90, 180 or 270.\");\n }\n if (options.flip !== undefined && typeof options.flip !== \"boolean\") {\n throw new TypeError(\"options.flip, when provided, must be a boolean.\");\n }\n if (options.crop !== undefined) {\n validateCropRectangle(options.crop, \"options.\");\n }\n if (options.alpha !== undefined && ![\"keep\", \"discard\"].includes(options.alpha)) {\n throw new TypeError(\"options.alpha, when provided, must be 'keep' or 'discard'.\");\n }\n const { rotation, flip } = composeRotationAndFlip(this.rotation, this.flip, options.rotate ?? 0, options.flip ?? false);\n const [rotatedWidth, rotatedHeight] = rotation % 180 === 0 ? [this.squarePixelWidth, this.squarePixelHeight] : [this.squarePixelHeight, this.squarePixelWidth];\n let finalCrop = options.crop;\n if (finalCrop) {\n finalCrop = clampCropRectangle(finalCrop, rotatedWidth, rotatedHeight);\n }\n const cropWidth = finalCrop ? finalCrop.width : rotatedWidth;\n const cropHeight = finalCrop ? finalCrop.height : rotatedHeight;\n const originalAspectRatio = cropWidth / cropHeight;\n let targetWidth;\n let targetHeight;\n if (options.width !== undefined && options.height === undefined) {\n targetWidth = options.width;\n targetHeight = targetWidth / originalAspectRatio;\n } else if (options.width === undefined && options.height !== undefined) {\n targetHeight = options.height;\n targetWidth = targetHeight * originalAspectRatio;\n } else if (options.width !== undefined && options.height !== undefined) {\n targetWidth = options.width;\n targetHeight = options.height;\n } else {\n targetWidth = cropWidth;\n targetHeight = cropHeight;\n }\n targetWidth = roundToMultiple(targetWidth, options.roundDimensionsTo ?? 1);\n targetHeight = roundToMultiple(targetHeight, options.roundDimensionsTo ?? 1);\n const description = {\n width: targetWidth,\n height: targetHeight,\n fit: options.fit ?? \"fill\",\n rotation,\n flip,\n crop: finalCrop ?? {\n left: 0,\n top: 0,\n width: rotatedWidth,\n height: rotatedHeight\n },\n alpha: options.alpha ?? \"keep\"\n };\n for (const transformer of registeredVideoSampleTransformers) {\n let result = transformer(this, description);\n if (isThenable(result))\n result = await result;\n if (result !== null) {\n return result;\n }\n }\n const { canvas, context, isNew } = getTransformationCanvas(description.width, description.height);\n this._drawWithFitAndMipmapping(canvas, context, {\n fit: description.fit,\n rotation: description.rotation,\n flip: description.flip,\n crop: description.crop,\n targetIsFresh: isNew,\n fillBlack: description.alpha === \"discard\"\n });\n return new VideoSample(canvas, {\n timestamp: this.timestamp,\n duration: this.duration,\n rotation: 0,\n flip: false\n });\n }\n setRotation(newRotation) {\n if (![0, 90, 180, 270].includes(newRotation)) {\n throw new TypeError(\"newRotation must be 0, 90, 180, or 270.\");\n }\n this.rotation = newRotation;\n }\n setFlip(newFlip) {\n if (typeof newFlip !== \"boolean\") {\n throw new TypeError(\"newFlip must be a boolean.\");\n }\n this.flip = newFlip;\n }\n setTimestamp(newTimestamp) {\n if (!Number.isFinite(newTimestamp)) {\n throw new TypeError(\"newTimestamp must be a number.\");\n }\n this.timestamp = newTimestamp;\n }\n setDuration(newDuration) {\n if (!Number.isFinite(newDuration) || newDuration < 0) {\n throw new TypeError(\"newDuration must be a non-negative number.\");\n }\n this.duration = newDuration;\n }\n setEncodeOptions(newEncodeOptions) {\n if (!newEncodeOptions || typeof newEncodeOptions !== \"object\") {\n throw new TypeError(\"newEncodeOptions must be an object.\");\n }\n this.encodeOptions = newEncodeOptions;\n }\n [Symbol.dispose]() {\n this.close();\n }\n }\n var registeredVideoSampleTransformers = [];\n var TRANSFORMATION_CANVAS_CACHE_MAX_SIZE = 3;\n var transformationCanvasCache = [];\n var transformationCanvasCacheNextAge = 0;\n var getTransformationCanvas = (width, height) => {\n for (const entry of transformationCanvasCache) {\n if (entry.canvas.width === width && entry.canvas.height === height) {\n entry.age = transformationCanvasCacheNextAge++;\n return { canvas: entry.canvas, context: entry.context, isNew: false };\n }\n }\n let canvas;\n if (typeof OffscreenCanvas !== \"undefined\") {\n canvas = new OffscreenCanvas(width, height);\n } else {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n throw new Error(\"Cannot transform VideoSamples in this environment. Either run in an environment with\" + \" OffscreenCanvas or HTMLCanvasElement, or supply a custom VideoSample transformer using\" + \" registerVideoSampleTransformer().\");\n }\n canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n }\n const context = canvas.getContext(\"2d\", {\n alpha: true,\n willReadFrequently: false\n });\n if (!context) {\n throw new Error(\"The '2d' canvas context is required to transform VideoSamples. Register a custom transformer using\" + \" registerVideoSampleTransformer to work around this limitation.\");\n }\n if (transformationCanvasCache.length >= TRANSFORMATION_CANVAS_CACHE_MAX_SIZE) {\n transformationCanvasCache.splice(arrayArgmin(transformationCanvasCache, (x) => x.age), 1);\n }\n transformationCanvasCache.push({\n canvas,\n context,\n age: transformationCanvasCacheNextAge++\n });\n return { canvas, context, isNew: true };\n };\n\n class VideoSampleColorSpace {\n constructor(init) {\n if (init !== undefined) {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"init.colorSpace, when provided, must be an object.\");\n }\n const primariesValues = Object.keys(COLOR_PRIMARIES_MAP);\n if (init.primaries != null && !primariesValues.includes(init.primaries)) {\n throw new TypeError(`init.colorSpace.primaries, when provided, must be one of ${primariesValues.join(\", \")}.`);\n }\n const transferValues = Object.keys(TRANSFER_CHARACTERISTICS_MAP);\n if (init.transfer != null && !transferValues.includes(init.transfer)) {\n throw new TypeError(`init.colorSpace.transfer, when provided, must be one of ${transferValues.join(\", \")}.`);\n }\n const matrixValues = Object.keys(MATRIX_COEFFICIENTS_MAP);\n if (init.matrix != null && !matrixValues.includes(init.matrix)) {\n throw new TypeError(`init.colorSpace.matrix, when provided, must be one of ${matrixValues.join(\", \")}.`);\n }\n if (init.fullRange != null && typeof init.fullRange !== \"boolean\") {\n throw new TypeError(\"init.colorSpace.fullRange, when provided, must be a boolean.\");\n }\n }\n this.primaries = init?.primaries ?? null;\n this.transfer = init?.transfer ?? null;\n this.matrix = init?.matrix ?? null;\n this.fullRange = init?.fullRange ?? null;\n }\n toJSON() {\n return {\n primaries: this.primaries,\n transfer: this.transfer,\n matrix: this.matrix,\n fullRange: this.fullRange\n };\n }\n }\n var isVideoFrame = (x) => {\n return typeof VideoFrame !== \"undefined\" && x instanceof VideoFrame;\n };\n var clampCropRectangle = (crop, outerWidth, outerHeight) => {\n const left = Math.min(crop.left, outerWidth);\n const top = Math.min(crop.top, outerHeight);\n const width = Math.min(crop.width, outerWidth - left);\n const height = Math.min(crop.height, outerHeight - top);\n assert(width >= 0);\n assert(height >= 0);\n return { left, top, width, height };\n };\n var validateCropRectangle = (crop, prefix) => {\n if (!crop || typeof crop !== \"object\") {\n throw new TypeError(prefix + \"crop, when provided, must be an object.\");\n }\n if (!Number.isInteger(crop.left) || crop.left < 0) {\n throw new TypeError(prefix + \"crop.left must be a non-negative integer.\");\n }\n if (!Number.isInteger(crop.top) || crop.top < 0) {\n throw new TypeError(prefix + \"crop.top must be a non-negative integer.\");\n }\n if (!Number.isInteger(crop.width) || crop.width < 0) {\n throw new TypeError(prefix + \"crop.width must be a non-negative integer.\");\n }\n if (!Number.isInteger(crop.height) || crop.height < 0) {\n throw new TypeError(prefix + \"crop.height must be a non-negative integer.\");\n }\n };\n var validateVideoFrameCopyToOptions = (options) => {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (options.colorSpace !== undefined && ![\"display-p3\", \"srgb\"].includes(options.colorSpace)) {\n throw new TypeError(\"options.colorSpace, when provided, must be 'display-p3' or 'srgb'.\");\n }\n if (options.format !== undefined && typeof options.format !== \"string\") {\n throw new TypeError(\"options.format, when provided, must be a string.\");\n }\n if (options.layout !== undefined) {\n if (!Array.isArray(options.layout)) {\n throw new TypeError(\"options.layout, when provided, must be an array.\");\n }\n for (const plane of options.layout) {\n if (!plane || typeof plane !== \"object\") {\n throw new TypeError(\"Each entry in options.layout must be an object.\");\n }\n if (!Number.isInteger(plane.offset) || plane.offset < 0) {\n throw new TypeError(\"plane.offset must be a non-negative integer.\");\n }\n if (!Number.isInteger(plane.stride) || plane.stride < 0) {\n throw new TypeError(\"plane.stride must be a non-negative integer.\");\n }\n }\n }\n if (options.rect !== undefined) {\n if (!options.rect || typeof options.rect !== \"object\") {\n throw new TypeError(\"options.rect, when provided, must be an object.\");\n }\n if (options.rect.x !== undefined && (!Number.isInteger(options.rect.x) || options.rect.x < 0)) {\n throw new TypeError(\"options.rect.x, when provided, must be a non-negative integer.\");\n }\n if (options.rect.y !== undefined && (!Number.isInteger(options.rect.y) || options.rect.y < 0)) {\n throw new TypeError(\"options.rect.y, when provided, must be a non-negative integer.\");\n }\n if (options.rect.width !== undefined && (!Number.isInteger(options.rect.width) || options.rect.width < 0)) {\n throw new TypeError(\"options.rect.width, when provided, must be a non-negative integer.\");\n }\n if (options.rect.height !== undefined && (!Number.isInteger(options.rect.height) || options.rect.height < 0)) {\n throw new TypeError(\"options.rect.height, when provided, must be a non-negative integer.\");\n }\n }\n };\n var createDefaultPlaneLayout = (format, codedWidth, codedHeight) => {\n const planes = getPlaneConfigs(format);\n const layouts = [];\n let currentOffset = 0;\n for (const plane of planes) {\n const planeWidth = Math.ceil(codedWidth / plane.widthDivisor);\n const planeHeight = Math.ceil(codedHeight / plane.heightDivisor);\n const stride = planeWidth * plane.sampleBytes;\n const planeSize = stride * planeHeight;\n layouts.push({\n offset: currentOffset,\n stride\n });\n currentOffset += planeSize;\n }\n return layouts;\n };\n var getPlaneConfigs = (format) => {\n const yuv = (yBytes, uvBytes, subX, subY, hasAlpha) => {\n const configs = [\n { sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 },\n { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY },\n { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY }\n ];\n if (hasAlpha) {\n configs.push({ sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 });\n }\n return configs;\n };\n switch (format) {\n case \"I420\":\n return yuv(1, 1, 2, 2, false);\n case \"I420P10\":\n case \"I420P12\":\n return yuv(2, 2, 2, 2, false);\n case \"I420A\":\n return yuv(1, 1, 2, 2, true);\n case \"I420AP10\":\n case \"I420AP12\":\n return yuv(2, 2, 2, 2, true);\n case \"I422\":\n return yuv(1, 1, 2, 1, false);\n case \"I422P10\":\n case \"I422P12\":\n return yuv(2, 2, 2, 1, false);\n case \"I422A\":\n return yuv(1, 1, 2, 1, true);\n case \"I422AP10\":\n case \"I422AP12\":\n return yuv(2, 2, 2, 1, true);\n case \"I444\":\n return yuv(1, 1, 1, 1, false);\n case \"I444P10\":\n case \"I444P12\":\n return yuv(2, 2, 1, 1, false);\n case \"I444A\":\n return yuv(1, 1, 1, 1, true);\n case \"I444AP10\":\n case \"I444AP12\":\n return yuv(2, 2, 1, 1, true);\n case \"NV12\":\n return [\n { sampleBytes: 1, widthDivisor: 1, heightDivisor: 1 },\n { sampleBytes: 2, widthDivisor: 2, heightDivisor: 2 }\n ];\n case \"RGBA\":\n case \"RGBX\":\n case \"BGRA\":\n case \"BGRX\":\n return [\n { sampleBytes: 4, widthDivisor: 1, heightDivisor: 1 }\n ];\n default:\n assertNever(format);\n assert(false);\n }\n };\n var ParseVideoFrameCopyToOptions = (sample, options) => {\n const defaultRect = {\n left: 0,\n top: 0,\n width: sample.codedWidth,\n height: sample.codedHeight\n };\n const overrideRect = options.rect;\n const parsedRect = ParseVisibleRect(defaultRect, overrideRect, sample.codedWidth, sample.codedHeight, sample.format);\n const optLayout = options.layout;\n let format;\n if (!options.format || options.format === sample.format) {\n format = sample.format;\n } else if ([\"RGBA\", \"RGBX\", \"BGRA\", \"BGRX\"].includes(options.format)) {\n format = options.format;\n } else {\n throw new Error(\"NotSupportedError: Invalid destination format.\");\n }\n return ComputeLayoutAndAllocationSize(parsedRect, format, optLayout);\n };\n var ParseVisibleRect = (defaultRect, overrideRect, codedWidth, codedHeight, format) => {\n const sourceRect = { ...defaultRect };\n if (overrideRect !== undefined) {\n if (overrideRect.width === 0 || overrideRect.height === 0) {\n throw new TypeError(\"visibleRect dimensions cannot be zero.\");\n }\n if ((overrideRect.x || 0) + (overrideRect.width || 0) > codedWidth) {\n throw new TypeError(\"visibleRect exceeds codedWidth.\");\n }\n if ((overrideRect.y || 0) + (overrideRect.height || 0) > codedHeight) {\n throw new TypeError(\"visibleRect exceeds codedHeight.\");\n }\n sourceRect.x = overrideRect.x || 0;\n sourceRect.y = overrideRect.y || 0;\n sourceRect.width = overrideRect.width || 0;\n sourceRect.height = overrideRect.height || 0;\n }\n const validAlignment = VerifyRectOffsetAlignment(format, sourceRect);\n if (!validAlignment) {\n throw new TypeError(\"visibleRect alignment is invalid for the format.\");\n }\n return sourceRect;\n };\n var VerifyRectOffsetAlignment = (format, rect) => {\n if (format === null)\n return true;\n const planes = getPlaneConfigs(format);\n for (let planeIndex = 0;planeIndex < planes.length; planeIndex++) {\n const plane = planes[planeIndex];\n const sampleWidth = plane.widthDivisor;\n const sampleHeight = plane.heightDivisor;\n if ((rect.x || 0) % sampleWidth !== 0)\n return false;\n if ((rect.y || 0) % sampleHeight !== 0)\n return false;\n }\n return true;\n };\n var ComputeLayoutAndAllocationSize = (parsedRect, format, layout) => {\n const planes = getPlaneConfigs(format);\n const numPlanes = planes.length;\n if (layout !== undefined && layout.length !== numPlanes) {\n throw new TypeError(`Layout must have ${numPlanes} planes.`);\n }\n let minAllocationSize = 0;\n const computedLayouts = [];\n const endOffsets = [];\n for (let planeIndex = 0;planeIndex < numPlanes; planeIndex++) {\n const plane = planes[planeIndex];\n const sampleBytes = plane.sampleBytes;\n const sampleWidth = plane.widthDivisor;\n const sampleHeight = plane.heightDivisor;\n const computedLayout = {\n destinationOffset: 0,\n destinationStride: 0,\n sourceTop: 0,\n sourceHeight: 0,\n sourceLeftBytes: 0,\n sourceWidthBytes: 0\n };\n computedLayout.sourceTop = Math.ceil(Math.trunc(parsedRect.y || 0) / sampleHeight);\n computedLayout.sourceHeight = Math.ceil(Math.trunc(parsedRect.height || 0) / sampleHeight);\n computedLayout.sourceLeftBytes = Math.floor(Math.trunc(parsedRect.x || 0) / sampleWidth) * sampleBytes;\n computedLayout.sourceWidthBytes = Math.floor(Math.trunc(parsedRect.width || 0) / sampleWidth) * sampleBytes;\n if (layout !== undefined) {\n const planeLayout = layout[planeIndex];\n if (planeLayout.stride < computedLayout.sourceWidthBytes) {\n throw new TypeError(`Stride for plane ${planeIndex} is too small.`);\n }\n computedLayout.destinationOffset = planeLayout.offset;\n computedLayout.destinationStride = planeLayout.stride;\n } else {\n computedLayout.destinationOffset = minAllocationSize;\n computedLayout.destinationStride = computedLayout.sourceWidthBytes;\n }\n const planeSize = computedLayout.destinationStride * computedLayout.sourceHeight;\n const planeEnd = planeSize + computedLayout.destinationOffset;\n if (planeEnd > 4294967295) {\n throw new TypeError(\"Allocation size exceeds limit.\");\n }\n endOffsets.push(planeEnd);\n minAllocationSize = Math.max(minAllocationSize, planeEnd);\n for (let earlierPlaneIndex = 0;earlierPlaneIndex < planeIndex; earlierPlaneIndex++) {\n const earlierLayout = computedLayouts[earlierPlaneIndex];\n if (endOffsets[planeIndex] <= earlierLayout.destinationOffset || endOffsets[earlierPlaneIndex] <= computedLayout.destinationOffset) {\n continue;\n }\n throw new TypeError(\"Planes overlap.\");\n }\n computedLayouts.push(computedLayout);\n }\n return {\n allocationSize: minAllocationSize,\n computedLayouts\n };\n };\n var AUDIO_SAMPLE_FORMATS = new Set([\"f32\", \"f32-planar\", \"s16\", \"s16-planar\", \"s32\", \"s32-planar\", \"u8\", \"u8-planar\"]);\n\n class AudioSampleResource {\n constructor() {\n this._referenceCount = 0;\n }\n }\n\n class AudioSample {\n get microsecondTimestamp() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);\n }\n get microsecondDuration() {\n return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);\n }\n constructor(init) {\n this._closed = false;\n if (isAudioData(init)) {\n if (init.format === null) {\n throw new TypeError(\"AudioData with null format is not supported.\");\n }\n this._data = init;\n this.format = init.format;\n this.sampleRate = init.sampleRate;\n this.numberOfFrames = init.numberOfFrames;\n this.numberOfChannels = init.numberOfChannels;\n this.timestamp = init.timestamp / 1e6;\n this.duration = init.numberOfFrames / init.sampleRate;\n } else if (init instanceof AudioSampleResource) {\n this._data = init;\n init._referenceCount++;\n this.format = init.getFormat();\n if (!AUDIO_SAMPLE_FORMATS.has(this.format)) {\n throw new TypeError(\"getFormat() must return an AudioSampleFormat.\");\n }\n this.sampleRate = init.getSampleRate();\n if (!Number.isInteger(this.sampleRate) || this.sampleRate <= 0) {\n throw new TypeError(\"getSampleRate() must return a positive integer.\");\n }\n this.numberOfFrames = init.getNumberOfFrames();\n if (!Number.isInteger(this.numberOfFrames) || this.numberOfFrames < 0) {\n throw new TypeError(\"getNumberOfFrames() must return a non-negative integer.\");\n }\n this.numberOfChannels = init.getNumberOfChannels();\n if (!Number.isInteger(this.numberOfChannels) || this.numberOfChannels <= 0) {\n throw new TypeError(\"getNumberOfChannels() must return a positive integer.\");\n }\n this.timestamp = init.getTimestamp();\n if (!Number.isFinite(this.timestamp)) {\n throw new TypeError(\"getTimestamp() must return a finite number.\");\n }\n this.duration = this.numberOfFrames / this.sampleRate;\n } else {\n if (!init || typeof init !== \"object\") {\n throw new TypeError(\"Invalid AudioDataInit: must be an object.\");\n }\n if (!AUDIO_SAMPLE_FORMATS.has(init.format)) {\n throw new TypeError(\"Invalid AudioDataInit: invalid format.\");\n }\n if (!Number.isFinite(init.sampleRate) || init.sampleRate <= 0) {\n throw new TypeError(\"Invalid AudioDataInit: sampleRate must be > 0.\");\n }\n if (!Number.isInteger(init.numberOfChannels) || init.numberOfChannels === 0) {\n throw new TypeError(\"Invalid AudioDataInit: numberOfChannels must be an integer > 0.\");\n }\n if (!Number.isFinite(init?.timestamp)) {\n throw new TypeError(\"init.timestamp must be a number.\");\n }\n const numberOfFrames = init.data.byteLength / (getBytesPerSample(init.format) * init.numberOfChannels);\n if (!Number.isInteger(numberOfFrames)) {\n throw new TypeError(\"Invalid AudioDataInit: data size is not a multiple of frame size.\");\n }\n this.format = init.format;\n this.sampleRate = init.sampleRate;\n this.numberOfFrames = numberOfFrames;\n this.numberOfChannels = init.numberOfChannels;\n this.timestamp = init.timestamp;\n this.duration = numberOfFrames / init.sampleRate;\n let dataBuffer;\n if (init.data instanceof ArrayBuffer) {\n dataBuffer = new Uint8Array(init.data);\n } else if (ArrayBuffer.isView(init.data)) {\n dataBuffer = new Uint8Array(init.data.buffer, init.data.byteOffset, init.data.byteLength);\n } else {\n throw new TypeError(\"Invalid AudioDataInit: data is not a BufferSource.\");\n }\n const expectedSize = this.numberOfFrames * this.numberOfChannels * getBytesPerSample(this.format);\n if (dataBuffer.byteLength < expectedSize) {\n throw new TypeError(\"Invalid AudioDataInit: insufficient data size.\");\n }\n this._data = dataBuffer;\n }\n finalizationRegistry?.register(this, { type: \"audio\", data: this._data }, this);\n }\n allocationSize(options) {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (!Number.isInteger(options.planeIndex) || options.planeIndex < 0) {\n throw new TypeError(\"planeIndex must be a non-negative integer.\");\n }\n if (options.format !== undefined && !AUDIO_SAMPLE_FORMATS.has(options.format)) {\n throw new TypeError(\"Invalid format.\");\n }\n if (options.frameOffset !== undefined && (!Number.isInteger(options.frameOffset) || options.frameOffset < 0)) {\n throw new TypeError(\"frameOffset must be a non-negative integer.\");\n }\n if (options.frameCount !== undefined && (!Number.isInteger(options.frameCount) || options.frameCount < 0)) {\n throw new TypeError(\"frameCount must be a non-negative integer.\");\n }\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n const destFormat = options.format ?? this.format;\n const frameOffset = options.frameOffset ?? 0;\n if (frameOffset >= this.numberOfFrames) {\n throw new RangeError(\"frameOffset out of range\");\n }\n const copyFrameCount = options.frameCount !== undefined ? options.frameCount : this.numberOfFrames - frameOffset;\n if (copyFrameCount > this.numberOfFrames - frameOffset) {\n throw new RangeError(\"frameCount out of range\");\n }\n const bytesPerSample = getBytesPerSample(destFormat);\n const isPlanar = formatIsPlanar(destFormat);\n if (isPlanar && options.planeIndex >= this.numberOfChannels) {\n throw new RangeError(\"planeIndex out of range\");\n }\n if (!isPlanar && options.planeIndex !== 0) {\n throw new RangeError(\"planeIndex out of range\");\n }\n const elementCount = isPlanar ? copyFrameCount : copyFrameCount * this.numberOfChannels;\n return elementCount * bytesPerSample;\n }\n copyTo(destination, options) {\n if (!isAllowSharedBufferSource(destination)) {\n throw new TypeError(\"destination must be an ArrayBuffer or an ArrayBuffer view.\");\n }\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (!Number.isInteger(options.planeIndex) || options.planeIndex < 0) {\n throw new TypeError(\"planeIndex must be a non-negative integer.\");\n }\n if (options.format !== undefined && !AUDIO_SAMPLE_FORMATS.has(options.format)) {\n throw new TypeError(\"Invalid format.\");\n }\n if (options.frameOffset !== undefined && (!Number.isInteger(options.frameOffset) || options.frameOffset < 0)) {\n throw new TypeError(\"frameOffset must be a non-negative integer.\");\n }\n if (options.frameCount !== undefined && (!Number.isInteger(options.frameCount) || options.frameCount < 0)) {\n throw new TypeError(\"frameCount must be a non-negative integer.\");\n }\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n const { format, frameCount: optFrameCount, frameOffset: optFrameOffset } = options;\n let { planeIndex } = options;\n let srcFormat = this.format;\n const destFormat = format ?? this.format;\n if (!destFormat)\n throw new Error(\"Destination format not determined\");\n const numFrames = this.numberOfFrames;\n const numChannels = this.numberOfChannels;\n const frameOffset = optFrameOffset ?? 0;\n if (frameOffset >= numFrames) {\n throw new RangeError(\"frameOffset out of range\");\n }\n const copyFrameCount = optFrameCount !== undefined ? optFrameCount : numFrames - frameOffset;\n if (copyFrameCount > numFrames - frameOffset) {\n throw new RangeError(\"frameCount out of range\");\n }\n const destBytesPerSample = getBytesPerSample(destFormat);\n const destIsPlanar = formatIsPlanar(destFormat);\n if (destIsPlanar && planeIndex >= numChannels) {\n throw new RangeError(\"planeIndex out of range\");\n }\n if (!destIsPlanar && planeIndex !== 0) {\n throw new RangeError(\"planeIndex out of range\");\n }\n const destElementCount = destIsPlanar ? copyFrameCount : copyFrameCount * numChannels;\n const requiredSize = destElementCount * destBytesPerSample;\n if (destination.byteLength < requiredSize) {\n throw new RangeError(\"Destination buffer is too small\");\n }\n const destView = toDataView(destination);\n const writeFn = getWriteFunction(destFormat);\n if (isAudioData(this._data)) {\n if (isWebKit() && numChannels > 2 && destFormat !== srcFormat) {\n doAudioDataCopyToWebKitWorkaround(this._data, destView, srcFormat, destFormat, numChannels, planeIndex, frameOffset, copyFrameCount);\n return;\n } else {\n try {\n this._data.copyTo(destination, {\n planeIndex,\n frameOffset,\n frameCount: copyFrameCount,\n format: destFormat\n });\n return;\n } catch (error) {\n if (destFormat === \"f32-planar\") {\n throw error;\n }\n srcFormat = \"f32-planar\";\n }\n }\n }\n const readFn = getReadFunction(srcFormat);\n const srcBytesPerSample = getBytesPerSample(srcFormat);\n const srcIsPlanar = formatIsPlanar(srcFormat);\n let uint8Data;\n if (this._data instanceof AudioSampleResource) {\n const getDataPlaneValidated = (index) => {\n const result = this._data.getDataPlane(index);\n if (!(result instanceof Uint8Array)) {\n throw new TypeError(\"getDataPlane() must return a Uint8Array.\");\n }\n const expectedSize = numFrames * srcBytesPerSample * (srcIsPlanar ? 1 : numChannels);\n if (result.byteLength !== expectedSize) {\n throw new TypeError(`Data plane ${index} has invalid size. Expected exactly ${expectedSize} bytes, got` + ` ${result.byteLength} bytes.`);\n }\n return result;\n };\n if (srcIsPlanar) {\n if (destIsPlanar) {\n uint8Data = getDataPlaneValidated(planeIndex);\n planeIndex = 0;\n } else {\n uint8Data = new Uint8Array(numFrames * srcBytesPerSample * numChannels);\n for (let ch = 0;ch < numChannels; ch++) {\n const planeData = getDataPlaneValidated(ch);\n uint8Data.set(planeData, ch * numFrames * srcBytesPerSample);\n }\n }\n } else {\n uint8Data = getDataPlaneValidated(0);\n }\n } else if (this._data instanceof Uint8Array) {\n uint8Data = this._data;\n } else {\n assert(srcFormat === \"f32-planar\");\n if (destIsPlanar) {\n uint8Data = new Uint8Array(this._data.allocationSize({\n format: \"f32-planar\",\n planeIndex\n }));\n this._data.copyTo(uint8Data, {\n format: \"f32-planar\",\n planeIndex\n });\n planeIndex = 0;\n } else {\n uint8Data = new Uint8Array(this._data.allocationSize({\n format: \"f32-planar\",\n planeIndex: 0\n }) * numChannels);\n for (let ch = 0;ch < numChannels; ch++) {\n this._data.copyTo(uint8Data.subarray(ch * numFrames * srcBytesPerSample, (ch + 1) * numFrames * srcBytesPerSample), {\n format: \"f32-planar\",\n planeIndex: ch\n });\n }\n }\n }\n const srcView = toDataView(uint8Data);\n for (let i = 0;i < copyFrameCount; i++) {\n if (destIsPlanar) {\n const destOffset = i * destBytesPerSample;\n let srcOffset;\n if (srcIsPlanar) {\n srcOffset = (planeIndex * numFrames + (i + frameOffset)) * srcBytesPerSample;\n } else {\n srcOffset = ((i + frameOffset) * numChannels + planeIndex) * srcBytesPerSample;\n }\n const normalized = readFn(srcView, srcOffset);\n writeFn(destView, destOffset, normalized);\n } else {\n for (let ch = 0;ch < numChannels; ch++) {\n const destIndex = i * numChannels + ch;\n const destOffset = destIndex * destBytesPerSample;\n let srcOffset;\n if (srcIsPlanar) {\n srcOffset = (ch * numFrames + (i + frameOffset)) * srcBytesPerSample;\n } else {\n srcOffset = ((i + frameOffset) * numChannels + ch) * srcBytesPerSample;\n }\n const normalized = readFn(srcView, srcOffset);\n writeFn(destView, destOffset, normalized);\n }\n }\n }\n }\n clone() {\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n if (this._data instanceof AudioSampleResource) {\n const sample = new AudioSample(this._data);\n sample.setTimestamp(this.timestamp);\n return sample;\n } else if (isAudioData(this._data)) {\n const sample = new AudioSample(this._data.clone());\n sample.setTimestamp(this.timestamp);\n return sample;\n } else {\n return new AudioSample({\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfFrames: this.numberOfFrames,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.timestamp,\n data: this._data\n });\n }\n }\n trim(startFrame, endFrame = this.numberOfFrames) {\n if (!Number.isInteger(startFrame) || startFrame < 0) {\n throw new TypeError(\"startFrame must be a non-negative integer.\");\n }\n if (!Number.isInteger(endFrame) || endFrame < 0) {\n throw new TypeError(\"endFrame must be a non-negative integer.\");\n }\n if (startFrame > this.numberOfFrames) {\n throw new RangeError(\"startFrame out of range.\");\n }\n if (endFrame > this.numberOfFrames) {\n throw new RangeError(\"endFrame out of range.\");\n }\n if (endFrame < startFrame) {\n throw new RangeError(\"endFrame must not be less than startFrame.\");\n }\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n const frameCount = endFrame - startFrame;\n const bytesPerSample = getBytesPerSample(this.format);\n let data;\n if (formatIsPlanar(this.format)) {\n const planeSize = frameCount * bytesPerSample;\n data = new Uint8Array(planeSize * this.numberOfChannels);\n if (frameCount > 0) {\n for (let i = 0;i < this.numberOfChannels; i++) {\n this.copyTo(data.subarray(i * planeSize, (i + 1) * planeSize), {\n planeIndex: i,\n format: this.format,\n frameOffset: startFrame,\n frameCount\n });\n }\n }\n } else {\n data = new Uint8Array(frameCount * this.numberOfChannels * bytesPerSample);\n if (frameCount > 0) {\n this.copyTo(data, {\n planeIndex: 0,\n format: this.format,\n frameOffset: startFrame,\n frameCount\n });\n }\n }\n return new AudioSample({\n data,\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.timestamp + startFrame / this.sampleRate\n });\n }\n close() {\n if (this._closed) {\n return;\n }\n finalizationRegistry?.unregister(this);\n if (this._data instanceof AudioSampleResource) {\n this._data._referenceCount--;\n if (this._data._referenceCount === 0) {\n this._data.close();\n }\n } else if (isAudioData(this._data)) {\n this._data.close();\n } else {\n this._data = new Uint8Array(0);\n }\n this._closed = true;\n }\n toAudioData() {\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n if (this._data instanceof AudioSampleResource) {\n return this._createAudioDataFromData();\n } else if (isAudioData(this._data)) {\n if (this._data.timestamp === this.microsecondTimestamp) {\n return this._data.clone();\n } else {\n return this._createAudioDataFromData();\n }\n } else {\n return new AudioData({\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfFrames: this.numberOfFrames,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.microsecondTimestamp,\n data: this._data.buffer instanceof ArrayBuffer ? this._data.buffer : this._data.slice()\n });\n }\n }\n _createAudioDataFromData() {\n if (formatIsPlanar(this.format)) {\n const size = this.allocationSize({ planeIndex: 0, format: this.format });\n const data = new ArrayBuffer(size * this.numberOfChannels);\n for (let i = 0;i < this.numberOfChannels; i++) {\n this.copyTo(new Uint8Array(data, i * size, size), { planeIndex: i, format: this.format });\n }\n return new AudioData({\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfFrames: this.numberOfFrames,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.microsecondTimestamp,\n data\n });\n } else {\n const data = new ArrayBuffer(this.allocationSize({ planeIndex: 0, format: this.format }));\n this.copyTo(data, { planeIndex: 0, format: this.format });\n return new AudioData({\n format: this.format,\n sampleRate: this.sampleRate,\n numberOfFrames: this.numberOfFrames,\n numberOfChannels: this.numberOfChannels,\n timestamp: this.microsecondTimestamp,\n data\n });\n }\n }\n toAudioBuffer() {\n if (this._closed) {\n throw new Error(\"AudioSample is closed.\");\n }\n const audioBuffer = new AudioBuffer({\n numberOfChannels: this.numberOfChannels,\n length: this.numberOfFrames,\n sampleRate: this.sampleRate\n });\n const dataBytes = new Float32Array(this.allocationSize({ planeIndex: 0, format: \"f32-planar\" }) / 4);\n for (let i = 0;i < this.numberOfChannels; i++) {\n this.copyTo(dataBytes, { planeIndex: i, format: \"f32-planar\" });\n audioBuffer.copyToChannel(dataBytes, i);\n }\n return audioBuffer;\n }\n setTimestamp(newTimestamp) {\n if (!Number.isFinite(newTimestamp)) {\n throw new TypeError(\"newTimestamp must be a number.\");\n }\n this.timestamp = newTimestamp;\n }\n [Symbol.dispose]() {\n this.close();\n }\n static *_fromAudioBuffer(audioBuffer, timestamp) {\n if (!(audioBuffer instanceof AudioBuffer)) {\n throw new TypeError(\"audioBuffer must be an AudioBuffer.\");\n }\n const MAX_FLOAT_COUNT = 48000 * 5;\n const numberOfChannels = audioBuffer.numberOfChannels;\n const sampleRate = audioBuffer.sampleRate;\n const totalFrames = audioBuffer.length;\n const maxFramesPerChunk = Math.floor(MAX_FLOAT_COUNT / numberOfChannels);\n let currentRelativeFrame = 0;\n let remainingFrames = totalFrames;\n while (remainingFrames > 0) {\n const framesToCopy = Math.min(maxFramesPerChunk, remainingFrames);\n const chunkData = new Float32Array(numberOfChannels * framesToCopy);\n for (let channel = 0;channel < numberOfChannels; channel++) {\n audioBuffer.copyFromChannel(chunkData.subarray(channel * framesToCopy, (channel + 1) * framesToCopy), channel, currentRelativeFrame);\n }\n yield new AudioSample({\n format: \"f32-planar\",\n sampleRate,\n numberOfFrames: framesToCopy,\n numberOfChannels,\n timestamp: timestamp + currentRelativeFrame / sampleRate,\n data: chunkData\n });\n currentRelativeFrame += framesToCopy;\n remainingFrames -= framesToCopy;\n }\n }\n static fromAudioBuffer(audioBuffer, timestamp) {\n if (!(audioBuffer instanceof AudioBuffer)) {\n throw new TypeError(\"audioBuffer must be an AudioBuffer.\");\n }\n const MAX_FLOAT_COUNT = 48000 * 5;\n const numberOfChannels = audioBuffer.numberOfChannels;\n const sampleRate = audioBuffer.sampleRate;\n const totalFrames = audioBuffer.length;\n const maxFramesPerChunk = Math.floor(MAX_FLOAT_COUNT / numberOfChannels);\n let currentRelativeFrame = 0;\n let remainingFrames = totalFrames;\n const result = [];\n while (remainingFrames > 0) {\n const framesToCopy = Math.min(maxFramesPerChunk, remainingFrames);\n const chunkData = new Float32Array(numberOfChannels * framesToCopy);\n for (let channel = 0;channel < numberOfChannels; channel++) {\n audioBuffer.copyFromChannel(chunkData.subarray(channel * framesToCopy, (channel + 1) * framesToCopy), channel, currentRelativeFrame);\n }\n const audioSample = new AudioSample({\n format: \"f32-planar\",\n sampleRate,\n numberOfFrames: framesToCopy,\n numberOfChannels,\n timestamp: timestamp + currentRelativeFrame / sampleRate,\n data: chunkData\n });\n result.push(audioSample);\n currentRelativeFrame += framesToCopy;\n remainingFrames -= framesToCopy;\n }\n return result;\n }\n }\n var getBytesPerSample = (format) => {\n switch (format) {\n case \"u8\":\n case \"u8-planar\":\n return 1;\n case \"s16\":\n case \"s16-planar\":\n return 2;\n case \"s32\":\n case \"s32-planar\":\n return 4;\n case \"f32\":\n case \"f32-planar\":\n return 4;\n default:\n throw new Error(\"Unknown AudioSampleFormat\");\n }\n };\n var formatIsPlanar = (format) => {\n switch (format) {\n case \"u8-planar\":\n case \"s16-planar\":\n case \"s32-planar\":\n case \"f32-planar\":\n return true;\n default:\n return false;\n }\n };\n var getReadFunction = (format) => {\n switch (format) {\n case \"u8\":\n case \"u8-planar\":\n return (view, offset) => (view.getUint8(offset) - 128) / 128;\n case \"s16\":\n case \"s16-planar\":\n return (view, offset) => view.getInt16(offset, true) / 32768;\n case \"s32\":\n case \"s32-planar\":\n return (view, offset) => view.getInt32(offset, true) / 2147483648;\n case \"f32\":\n case \"f32-planar\":\n return (view, offset) => view.getFloat32(offset, true);\n }\n };\n var getWriteFunction = (format) => {\n switch (format) {\n case \"u8\":\n case \"u8-planar\":\n return (view, offset, value) => view.setUint8(offset, clamp(Math.round(value * 128) + 128, 0, 255));\n case \"s16\":\n case \"s16-planar\":\n return (view, offset, value) => view.setInt16(offset, clamp(Math.round(value * 32768), -32768, 32767), true);\n case \"s32\":\n case \"s32-planar\":\n return (view, offset, value) => view.setInt32(offset, clamp(Math.round(value * 2147483648), -2147483648, 2147483647), true);\n case \"f32\":\n case \"f32-planar\":\n return (view, offset, value) => view.setFloat32(offset, value, true);\n }\n };\n var isAudioData = (x) => {\n return typeof AudioData !== \"undefined\" && x instanceof AudioData;\n };\n var toInterleavedAudioFormat = (format) => {\n switch (format) {\n case \"u8-planar\":\n return \"u8\";\n case \"s16-planar\":\n return \"s16\";\n case \"s32-planar\":\n return \"s32\";\n case \"f32-planar\":\n return \"f32\";\n default:\n return format;\n }\n };\n var doAudioDataCopyToWebKitWorkaround = (audioData, destView, srcFormat, destFormat, numChannels, planeIndex, frameOffset, copyFrameCount) => {\n const readFn = getReadFunction(srcFormat);\n const writeFn = getWriteFunction(destFormat);\n const srcBytesPerSample = getBytesPerSample(srcFormat);\n const destBytesPerSample = getBytesPerSample(destFormat);\n const srcIsPlanar = formatIsPlanar(srcFormat);\n const destIsPlanar = formatIsPlanar(destFormat);\n if (destIsPlanar) {\n if (srcIsPlanar) {\n const data = new ArrayBuffer(copyFrameCount * srcBytesPerSample);\n const dataView = toDataView(data);\n audioData.copyTo(data, {\n planeIndex,\n frameOffset,\n frameCount: copyFrameCount,\n format: srcFormat\n });\n for (let i = 0;i < copyFrameCount; i++) {\n const srcOffset = i * srcBytesPerSample;\n const destOffset = i * destBytesPerSample;\n const sample = readFn(dataView, srcOffset);\n writeFn(destView, destOffset, sample);\n }\n } else {\n const data = new ArrayBuffer(copyFrameCount * numChannels * srcBytesPerSample);\n const dataView = toDataView(data);\n audioData.copyTo(data, {\n planeIndex: 0,\n frameOffset,\n frameCount: copyFrameCount,\n format: srcFormat\n });\n for (let i = 0;i < copyFrameCount; i++) {\n const srcOffset = (i * numChannels + planeIndex) * srcBytesPerSample;\n const destOffset = i * destBytesPerSample;\n const sample = readFn(dataView, srcOffset);\n writeFn(destView, destOffset, sample);\n }\n }\n } else {\n if (srcIsPlanar) {\n const planeSize = copyFrameCount * srcBytesPerSample;\n const data = new ArrayBuffer(planeSize);\n const dataView = toDataView(data);\n for (let ch = 0;ch < numChannels; ch++) {\n audioData.copyTo(data, {\n planeIndex: ch,\n frameOffset,\n frameCount: copyFrameCount,\n format: srcFormat\n });\n for (let i = 0;i < copyFrameCount; i++) {\n const srcOffset = i * srcBytesPerSample;\n const destOffset = (i * numChannels + ch) * destBytesPerSample;\n const sample = readFn(dataView, srcOffset);\n writeFn(destView, destOffset, sample);\n }\n }\n } else {\n const data = new ArrayBuffer(copyFrameCount * numChannels * srcBytesPerSample);\n const dataView = toDataView(data);\n audioData.copyTo(data, {\n planeIndex: 0,\n frameOffset,\n frameCount: copyFrameCount,\n format: srcFormat\n });\n for (let i = 0;i < copyFrameCount; i++) {\n for (let ch = 0;ch < numChannels; ch++) {\n const idx = i * numChannels + ch;\n const srcOffset = idx * srcBytesPerSample;\n const destOffset = idx * destBytesPerSample;\n const sample = readFn(dataView, srcOffset);\n writeFn(destView, destOffset, sample);\n }\n }\n }\n }\n };\n var audioSampleToInterleavedFormat = (sample, format) => {\n const size = sample.allocationSize({ format, planeIndex: 0 });\n const buffer = new ArrayBuffer(size);\n sample.copyTo(buffer, { format, planeIndex: 0 });\n return new AudioSample({\n data: buffer,\n format,\n numberOfChannels: sample.numberOfChannels,\n sampleRate: sample.sampleRate,\n timestamp: sample.timestamp,\n duration: sample.duration\n });\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/encode.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var canEncodeVideoMemo = new Map;\n var canEncodeAudioMemo = new Map;\n var validateVideoEncodingConfig = (config) => {\n if (!config || typeof config !== \"object\") {\n throw new TypeError(\"Encoding config must be an object.\");\n }\n if (!VIDEO_CODECS.includes(config.codec)) {\n throw new TypeError(`Invalid video codec '${config.codec}'. Must be one of: ${VIDEO_CODECS.join(\", \")}.`);\n }\n const bitrate = config.bitrate;\n if (config.quality === undefined && bitrate === undefined) {\n throw new TypeError(\"config.quality must be provided.\");\n }\n if (config.quality !== undefined && bitrate !== undefined) {\n throw new TypeError(\"config.quality and config.bitrate cannot both be provided.\");\n }\n if (config.quality !== undefined && !(config.quality instanceof Quality)) {\n throw new TypeError(\"config.quality, when provided, must be a Quality.\");\n }\n if (bitrate !== undefined && !(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {\n throw new TypeError(\"config.bitrate, when provided, must be a positive integer or a quality.\");\n }\n if (config.keyFrameInterval !== undefined && (!Number.isFinite(config.keyFrameInterval) || config.keyFrameInterval < 0)) {\n throw new TypeError(\"config.keyFrameInterval, when provided, must be a non-negative number.\");\n }\n if (config.sizeChangeBehavior !== undefined && ![\"deny\", \"passThrough\", \"fill\", \"contain\", \"cover\"].includes(config.sizeChangeBehavior)) {\n throw new TypeError(\"config.sizeChangeBehavior, when provided, must be 'deny', 'passThrough', 'fill', 'contain'\" + \" or 'cover'.\");\n }\n if (config.transform !== undefined) {\n if (typeof config.transform !== \"object\" || !config.transform) {\n throw new TypeError(\"config.transform, when provided, must be an object.\");\n }\n if (config.transform.width !== undefined && (!Number.isInteger(config.transform.width) || config.transform.width <= 0)) {\n throw new TypeError(\"config.transform.width, when provided, must be a positive integer.\");\n }\n if (config.transform.height !== undefined && (!Number.isInteger(config.transform.height) || config.transform.height <= 0)) {\n throw new TypeError(\"config.transform.height, when provided, must be a positive integer.\");\n }\n if (config.transform.fit !== undefined && ![\"fill\", \"contain\", \"cover\"].includes(config.transform.fit)) {\n throw new TypeError('config.transform.fit, when provided, must be one of \"fill\", \"contain\", or \"cover\".');\n }\n if (config.transform.width !== undefined && config.transform.height !== undefined && config.transform.fit === undefined && ![\"fill\", \"contain\", \"cover\"].includes(config.sizeChangeBehavior)) {\n throw new TypeError(\"When both config.transform.width and config.transform.height are provided, config.transform.fit\" + \" must also be provided.\");\n }\n if (config.transform.fit !== undefined && [\"fill\", \"contain\", \"cover\"].includes(config.sizeChangeBehavior) && config.transform.fit !== config.sizeChangeBehavior) {\n throw new TypeError(\"config.transform.fit, when provided, cannot differ from config.sizeChangeBehavior when\" + \" config.sizeChangeBehavior is 'fill', 'contain' or 'cover', as sizeChangeBehavior already\" + \" determines the fitting algorithm.\");\n }\n if (config.transform.rotate !== undefined && ![0, 90, 180, 270].includes(config.transform.rotate)) {\n throw new TypeError(\"config.transform.rotate, when provided, must be 0, 90, 180 or 270.\");\n }\n if (config.transform.flip !== undefined && typeof config.transform.flip !== \"boolean\") {\n throw new TypeError(\"config.transform.flip, when provided, must be a boolean.\");\n }\n if (config.transform.crop !== undefined) {\n validateCropRectangle(config.transform.crop, \"config.transform.\");\n }\n if (config.transform.process !== undefined && typeof config.transform.process !== \"function\") {\n throw new TypeError(\"config.transform.process, when provided, must be a function.\");\n }\n if (config.transform.frameRate !== undefined && (!Number.isFinite(config.transform.frameRate) || config.transform.frameRate <= 0)) {\n throw new TypeError(\"config.transform.frameRate, when provided, must be a finite positive number.\");\n }\n if (config.transform.force !== undefined && typeof config.transform.force !== \"boolean\") {\n throw new TypeError(\"config.transform.force, when provided, must be a boolean.\");\n }\n }\n if (config.onEncodedPacket !== undefined && typeof config.onEncodedPacket !== \"function\") {\n throw new TypeError(\"config.onEncodedPacket, when provided, must be a function.\");\n }\n if (config.onEncoderConfig !== undefined && typeof config.onEncoderConfig !== \"function\") {\n throw new TypeError(\"config.onEncoderConfig, when provided, must be a function.\");\n }\n if (config.onEncodedSample !== undefined && typeof config.onEncodedSample !== \"function\") {\n throw new TypeError(\"config.onEncodedSample, when provided, must be a function.\");\n }\n validateVideoEncodingAdditionalOptions(config.codec, config);\n };\n var validateVideoEncodingAdditionalOptions = (codec, options) => {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"Encoding options must be an object.\");\n }\n if (options.alpha !== undefined && ![\"discard\", \"keep\"].includes(options.alpha)) {\n throw new TypeError(\"options.alpha, when provided, must be 'discard' or 'keep'.\");\n }\n const bitrateMode = options.bitrateMode;\n if (bitrateMode !== undefined && ![\"constant\", \"variable\"].includes(bitrateMode)) {\n throw new TypeError(\"bitrateMode, when provided, must be 'constant' or 'variable'.\");\n }\n if (options.latencyMode !== undefined && ![\"quality\", \"realtime\"].includes(options.latencyMode)) {\n throw new TypeError(\"latencyMode, when provided, must be 'quality' or 'realtime'.\");\n }\n if (options.fullCodecString !== undefined && typeof options.fullCodecString !== \"string\") {\n throw new TypeError(\"fullCodecString, when provided, must be a string.\");\n }\n if (options.fullCodecString !== undefined && inferCodecFromCodecString(options.fullCodecString) !== codec) {\n throw new TypeError(`fullCodecString, when provided, must be a string that matches the specified codec (${codec}).`);\n }\n if (options.hardwareAcceleration !== undefined && ![\"no-preference\", \"prefer-hardware\", \"prefer-software\"].includes(options.hardwareAcceleration)) {\n throw new TypeError(\"hardwareAcceleration, when provided, must be 'no-preference', 'prefer-hardware' or\" + \" 'prefer-software'.\");\n }\n if (options.scalabilityMode !== undefined && typeof options.scalabilityMode !== \"string\") {\n throw new TypeError(\"scalabilityMode, when provided, must be a string.\");\n }\n if (options.contentHint !== undefined && typeof options.contentHint !== \"string\") {\n throw new TypeError(\"contentHint, when provided, must be a string.\");\n }\n };\n var buildVideoEncoderConfigs = (options) => {\n const fallbackBitrateMode = options.bitrateMode;\n const rateControl = options.quality._toVideoRateControl(options.codec, options.width, options.height, fallbackBitrateMode);\n const buildConfig = (bitrate, bitrateMode, bitrateEstimate) => ({\n codec: options.fullCodecString ?? buildVideoCodecString(options.codec, options.width, options.height, bitrateEstimate, options.alpha === \"keep\"),\n width: options.width,\n height: options.height,\n displayWidth: options.squarePixelWidth,\n displayHeight: options.squarePixelHeight,\n bitrate,\n bitrateMode,\n alpha: options.alpha ?? \"discard\",\n framerate: options.framerate,\n latencyMode: options.latencyMode,\n hardwareAcceleration: options.hardwareAcceleration,\n scalabilityMode: options.scalabilityMode,\n contentHint: options.contentHint,\n ...getVideoEncoderConfigExtension(options.codec)\n });\n const candidates = [];\n if (rateControl.quantizer !== null) {\n candidates.push({\n config: buildConfig(undefined, \"quantizer\", rateControl.bitrate),\n quantizer: rateControl.quantizer\n });\n }\n if (rateControl.bitrateMode !== \"quantizer\") {\n candidates.push({\n config: buildConfig(rateControl.bitrate, rateControl.bitrateMode, rateControl.bitrate),\n quantizer: null\n });\n }\n assert(candidates.length > 0);\n return candidates;\n };\n var validateAudioEncodingConfig = (config) => {\n if (!config || typeof config !== \"object\") {\n throw new TypeError(\"Encoding config must be an object.\");\n }\n if (!AUDIO_CODECS.includes(config.codec)) {\n throw new TypeError(`Invalid audio codec '${config.codec}'. Must be one of: ${AUDIO_CODECS.join(\", \")}.`);\n }\n const bitrate = config.bitrate;\n if (config.quality === undefined && bitrate === undefined && !(PCM_AUDIO_CODECS.includes(config.codec) || config.codec === \"flac\")) {\n throw new TypeError(\"config.quality must be provided for compressed audio codecs.\");\n }\n if (config.quality !== undefined && bitrate !== undefined) {\n throw new TypeError(\"config.quality and config.bitrate cannot both be provided.\");\n }\n if (config.quality !== undefined && !(config.quality instanceof Quality)) {\n throw new TypeError(\"config.quality, when provided, must be a Quality.\");\n }\n if (bitrate !== undefined && !(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {\n throw new TypeError(\"config.bitrate, when provided, must be a positive integer or a quality.\");\n }\n if (config.transform !== undefined) {\n if (typeof config.transform !== \"object\" || !config.transform) {\n throw new TypeError(\"config.transform, when provided, must be an object.\");\n }\n if (config.transform.numberOfChannels !== undefined && (!Number.isInteger(config.transform.numberOfChannels) || config.transform.numberOfChannels <= 0)) {\n throw new TypeError(\"config.transform.numberOfChannels, when provided, must be a positive integer.\");\n }\n if (config.transform.sampleRate !== undefined && (!Number.isInteger(config.transform.sampleRate) || config.transform.sampleRate <= 0)) {\n throw new TypeError(\"config.transform.sampleRate, when provided, must be a positive integer.\");\n }\n if (config.transform.sampleFormat !== undefined && ![\"u8\", \"s16\", \"s32\", \"f32\"].includes(config.transform.sampleFormat)) {\n throw new TypeError(\"config.transform.sampleFormat, when provided, must be one of: u8, s16, s32, f32.\");\n }\n if (config.transform.process !== undefined && typeof config.transform.process !== \"function\") {\n throw new TypeError(\"config.transform.process, when provided, must be a function.\");\n }\n }\n if (config.onEncodedPacket !== undefined && typeof config.onEncodedPacket !== \"function\") {\n throw new TypeError(\"config.onEncodedPacket, when provided, must be a function.\");\n }\n if (config.onEncoderConfig !== undefined && typeof config.onEncoderConfig !== \"function\") {\n throw new TypeError(\"config.onEncoderConfig, when provided, must be a function.\");\n }\n if (config.onEncodedSample !== undefined && typeof config.onEncodedSample !== \"function\") {\n throw new TypeError(\"config.onEncodedSample, when provided, must be a function.\");\n }\n validateAudioEncodingAdditionalOptions(config.codec, config);\n };\n var validateAudioEncodingAdditionalOptions = (codec, options) => {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"Encoding options must be an object.\");\n }\n const bitrateMode = options.bitrateMode;\n if (bitrateMode !== undefined && ![\"constant\", \"variable\"].includes(bitrateMode)) {\n throw new TypeError(\"bitrateMode, when provided, must be 'constant' or 'variable'.\");\n }\n if (options.fullCodecString !== undefined && typeof options.fullCodecString !== \"string\") {\n throw new TypeError(\"fullCodecString, when provided, must be a string.\");\n }\n if (options.fullCodecString !== undefined && inferCodecFromCodecString(options.fullCodecString) !== codec) {\n throw new TypeError(`fullCodecString, when provided, must be a string that matches the specified codec (${codec}).`);\n }\n };\n var buildAudioEncoderConfig = (options) => {\n const fallbackBitrateMode = options.bitrateMode;\n return {\n codec: options.fullCodecString ?? buildAudioCodecString(options.codec, options.numberOfChannels, options.sampleRate),\n numberOfChannels: options.numberOfChannels,\n sampleRate: options.sampleRate,\n bitrate: options.quality?._toAudioBitrate(options.codec),\n bitrateMode: options.quality?._bitrateMode ?? fallbackBitrateMode,\n ...getAudioEncoderConfigExtension(options.codec)\n };\n };\n\n class Quality {\n constructor(options) {\n if (typeof options === \"number\" || typeof options === \"string\") {\n options = { quality: options };\n }\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (options.bitrateMode !== undefined && ![\"constant\", \"variable\"].includes(options.bitrateMode)) {\n throw new TypeError(\"options.bitrateMode, when provided, must be 'constant' or 'variable'.\");\n }\n if (\"quality\" in options) {\n if (typeof options.quality === \"string\" ? !(options.quality in QUALITY_LEVELS) : typeof options.quality !== \"number\" || Number.isNaN(options.quality)) {\n throw new TypeError(\"options.quality must be a number, or one of 'very-low', 'low', 'medium', 'high'\" + \" or 'very-high'.\");\n }\n if (options.preferBitrate !== undefined && typeof options.preferBitrate !== \"boolean\") {\n throw new TypeError(\"options.preferBitrate, when provided, must be a boolean.\");\n }\n if (\"bitrate\" in options || \"quantizer\" in options) {\n throw new TypeError(\"options.quality cannot be combined with options.bitrate or options.quantizer.\");\n }\n this._quality = typeof options.quality === \"string\" ? QUALITY_LEVELS[options.quality] : options.quality;\n this._preferBitrate = options.preferBitrate ?? false;\n this._bitrate = undefined;\n this._quantizer = undefined;\n } else {\n if (options.bitrate !== undefined && (!Number.isInteger(options.bitrate) || options.bitrate <= 0)) {\n throw new TypeError(\"options.bitrate, when provided, must be a positive integer.\");\n }\n if (options.quantizer !== undefined && (!Number.isInteger(options.quantizer) || options.quantizer < 0)) {\n throw new TypeError(\"options.quantizer, when provided, must be a non-negative integer.\");\n }\n if (options.bitrate === undefined && options.quantizer === undefined) {\n throw new TypeError(\"At least one of options.bitrate or options.quantizer must be set.\");\n }\n if (\"preferBitrate\" in options) {\n throw new TypeError(\"options.preferBitrate can only be combined with options.quality.\");\n }\n this._quality = undefined;\n this._preferBitrate = false;\n this._bitrate = options.bitrate;\n this._quantizer = options.quantizer;\n }\n this._bitrateMode = options.bitrateMode;\n }\n _toVideoRateControl(codec, width, height, fallbackBitrateMode) {\n const quantizerSupport = VIDEO_CODEC_QUANTIZER_SUPPORT[codec];\n let quantizer = null;\n let bitrateMode = this._bitrateMode ?? fallbackBitrateMode ?? \"variable\";\n if (this._quantizer !== undefined) {\n if (!quantizerSupport) {\n if (this._bitrate === undefined) {\n throw new Error(`Codec '${codec}' does not support quantizer-based encoding. Provide a bitrate in the Quality` + ` to define a fallback.`);\n }\n } else if (this._quantizer < quantizerSupport.min || this._quantizer > quantizerSupport.max) {\n if (this._bitrate === undefined) {\n throw new Error(`Quantizer ${this._quantizer} is out of range for codec '${codec}'; must be between` + ` ${quantizerSupport.min} and ${quantizerSupport.max}.`);\n }\n } else {\n quantizer = this._quantizer;\n if (this._bitrate === undefined) {\n bitrateMode = \"quantizer\";\n }\n }\n } else if (this._bitrate === undefined && quantizerSupport && !this._preferBitrate) {\n assert(this._quality !== undefined);\n quantizer = clamp(Math.round(lerp(quantizerSupport.worst, quantizerSupport.best, this._quality)), quantizerSupport.min, quantizerSupport.max);\n }\n let bitrate;\n if (this._bitrate !== undefined) {\n bitrate = this._bitrate;\n } else {\n let quality = this._quality;\n if (quality === undefined) {\n assert(quantizer !== null && quantizerSupport);\n quality = clamp((quantizer - quantizerSupport.worst) / (quantizerSupport.best - quantizerSupport.worst), 0, 1);\n }\n bitrate = computeVideoBitrate(codec, width, height, qualityToBitrateFactor(quality));\n }\n return { quantizer, bitrate, bitrateMode };\n }\n _toVideoBitrate(codec, width, height) {\n if (this._bitrate !== undefined) {\n return this._bitrate;\n }\n assert(this._quality !== undefined);\n return computeVideoBitrate(codec, width, height, qualityToBitrateFactor(this._quality));\n }\n _toAudioBitrate(codec) {\n if (PCM_AUDIO_CODECS.includes(codec) || codec === \"flac\") {\n return;\n }\n if (this._bitrate !== undefined) {\n return this._bitrate;\n }\n if (this._quality === undefined) {\n throw new Error(\"This Quality defines neither a quality level nor a bitrate and therefore cannot be used for audio\" + \" encoding.\");\n }\n const factor = qualityToBitrateFactor(this._quality);\n const baseRates = {\n aac: 128000,\n opus: 64000,\n mp3: 160000,\n vorbis: 64000,\n ac3: 384000,\n eac3: 192000,\n dts: 768000\n };\n const baseBitrate = baseRates[codec];\n if (!baseBitrate) {\n throw new Error(`Unhandled codec: ${codec}`);\n }\n let finalBitrate = baseBitrate * factor;\n if (codec === \"aac\") {\n const validRates = [96000, 128000, 160000, 192000];\n finalBitrate = validRates.reduce((prev, curr) => Math.abs(curr - finalBitrate) < Math.abs(prev - finalBitrate) ? curr : prev);\n } else if (codec === \"opus\" || codec === \"vorbis\") {\n finalBitrate = Math.max(6000, finalBitrate);\n } else if (codec === \"mp3\") {\n const validRates = [\n 8000,\n 16000,\n 24000,\n 32000,\n 40000,\n 48000,\n 64000,\n 80000,\n 96000,\n 112000,\n 128000,\n 160000,\n 192000,\n 224000,\n 256000,\n 320000\n ];\n finalBitrate = validRates.reduce((prev, curr) => Math.abs(curr - finalBitrate) < Math.abs(prev - finalBitrate) ? curr : prev);\n }\n return Math.round(finalBitrate / 1000) * 1000;\n }\n }\n var QUALITY_LEVELS = {\n \"very-low\": 0,\n low: 0.25,\n medium: 0.5,\n high: 0.75,\n \"very-high\": 1\n };\n var VIDEO_CODEC_QUANTIZER_SUPPORT = {\n avc: { min: 0, max: 51, worst: 41, best: 16 },\n hevc: { min: 0, max: 51, worst: 41, best: 16 },\n vp9: { min: 0, max: 63, worst: 52, best: 20 },\n av1: { min: 0, max: 255, worst: 208, best: 80 }\n };\n var qualityToBitrateFactor = (quality) => 0.3 * Math.exp(2.5538 * quality);\n var computeVideoBitrate = (codec, width, height, factor) => {\n const pixels = width * height;\n const referencePixels = 1920 * 1080;\n const referenceBitrate = 3000000;\n const scaleFactor = Math.pow(pixels / referencePixels, 0.95);\n const baseBitrate = referenceBitrate * scaleFactor;\n const codecEfficiencyFactors = {\n avc: 1,\n hevc: 0.6,\n vp9: 0.6,\n av1: 0.4,\n vp8: 1.2,\n prores: 220000000 / referenceBitrate\n };\n const codecAdjustedBitrate = baseBitrate * codecEfficiencyFactors[codec];\n const finalBitrate = codecAdjustedBitrate * factor;\n return Math.ceil(finalBitrate / 1000) * 1000;\n };\n var buildQuantizerEncodeOptions = (codec, quantizer) => {\n if (codec === \"avc\") {\n return { avc: { quantizer } };\n } else if (codec === \"hevc\") {\n return { hevc: { quantizer } };\n } else if (codec === \"vp9\") {\n return { vp9: { quantizer } };\n } else if (codec === \"av1\") {\n return { av1: { quantizer } };\n }\n assert(false);\n };\n var QUALITY_LOW = /* @__PURE__ */ new Quality(\"low\");\n var QUALITY_MEDIUM = /* @__PURE__ */ new Quality(\"medium\");\n var QUALITY_HIGH = /* @__PURE__ */ new Quality(\"high\");\n var QUALITY_VERY_HIGH = /* @__PURE__ */ new Quality(\"very-high\");\n var canEncodeVideo = async (codec, options = {}) => {\n const {\n width = 1280,\n height = 720,\n quality,\n bitrate,\n frameRate,\n ...restOptions\n } = options;\n if (!VIDEO_CODECS.includes(codec)) {\n return false;\n }\n if (!Number.isInteger(width) || width <= 0) {\n throw new TypeError(\"width must be a positive integer.\");\n }\n if (!Number.isInteger(height) || height <= 0) {\n throw new TypeError(\"height must be a positive integer.\");\n }\n if (quality !== undefined && !(quality instanceof Quality)) {\n throw new TypeError(\"quality, when provided, must be a Quality.\");\n }\n if (quality !== undefined && bitrate !== undefined) {\n throw new TypeError(\"quality and bitrate cannot both be provided.\");\n }\n if (bitrate !== undefined && !(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {\n throw new TypeError(\"bitrate must be a positive integer or a quality.\");\n }\n if (frameRate !== undefined && (!Number.isFinite(frameRate) || frameRate <= 0)) {\n throw new TypeError(\"frameRate, when provided, must be a finite positive number.\");\n }\n validateVideoEncodingAdditionalOptions(codec, restOptions);\n const resolvedQuality = resolveQuality(quality, bitrate) ?? new Quality(\"medium\");\n let candidates;\n try {\n candidates = buildVideoEncoderConfigs({\n codec,\n width,\n height,\n quality: resolvedQuality,\n framerate: frameRate,\n ...restOptions,\n alpha: \"discard\"\n });\n } catch {\n return false;\n }\n const key = JSON.stringify(candidates);\n const memoized = canEncodeVideoMemo.get(key);\n if (memoized) {\n return memoized;\n }\n const promise = (async () => {\n for (const { config } of candidates) {\n if (customVideoEncoders.some((x) => x.supports(codec, config))) {\n return true;\n }\n }\n if (typeof VideoEncoder === \"undefined\") {\n return false;\n }\n const hasOddDimension = width % 2 === 1 || height % 2 === 1;\n if (hasOddDimension && (codec === \"avc\" || codec === \"hevc\")) {\n return false;\n }\n for (const { config, quantizer } of candidates) {\n try {\n const support = await VideoEncoder.isConfigSupported(config);\n if (!support.supported) {\n continue;\n }\n } catch {\n continue;\n }\n if (!isFirefox()) {\n return true;\n }\n const success = await new Promise(async (resolve) => {\n try {\n const encoder = new VideoEncoder({\n output: () => {},\n error: () => resolve(false)\n });\n encoder.configure(config);\n const frameData = new Uint8Array(width * height * 4);\n const frame = new VideoFrame(frameData, {\n format: \"RGBA\",\n codedWidth: width,\n codedHeight: height,\n timestamp: 0\n });\n encoder.encode(frame, quantizer !== null ? buildQuantizerEncodeOptions(codec, quantizer) : undefined);\n frame.close();\n await encoder.flush();\n resolve(true);\n } catch {\n resolve(false);\n }\n });\n if (success) {\n return true;\n }\n }\n return false;\n })();\n canEncodeVideoMemo.set(key, promise);\n return promise;\n };\n var resolveQuality = (quality, bitrate) => {\n if (quality !== undefined) {\n return quality;\n }\n if (bitrate === undefined) {\n return;\n }\n return bitrate instanceof Quality ? bitrate : new Quality({ bitrate });\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/custom-coder.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var customVideoEncoders = [];\n var customAudioEncoders = [];\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/pcm.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var toUlaw = (s16) => {\n const MULAW_MAX = 8191;\n const MULAW_BIAS = 33;\n let number = s16 >> 2;\n let mask = 4096;\n let sign = 0;\n let position = 12;\n let lsb = 0;\n if (number < 0) {\n number = -number;\n sign = 128;\n }\n number += MULAW_BIAS;\n if (number > MULAW_MAX) {\n number = MULAW_MAX;\n }\n while ((number & mask) !== mask && position >= 5) {\n mask >>= 1;\n position--;\n }\n lsb = number >> position - 4 & 15;\n return ~(sign | position - 5 << 4 | lsb) & 255;\n };\n var toAlaw = (s16) => {\n const ALAW_MAX = 4095;\n let mask = 2048;\n let sign = 0;\n let position = 11;\n let lsb = 0;\n let number = s16 >> 3;\n if (number < 0) {\n number = -number - 1;\n } else {\n sign = 128;\n }\n if (number > ALAW_MAX) {\n number = ALAW_MAX;\n }\n while ((number & mask) !== mask && position >= 5) {\n mask >>= 1;\n position--;\n }\n lsb = number >> (position === 4 ? 1 : position - 4) & 15;\n return (sign | position - 4 << 4 | lsb) ^ 85;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/reader.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n class FileSlice {\n constructor(bytes, view, offset, start, end) {\n this.bytes = bytes;\n this.view = view;\n this.offset = offset;\n this.start = start;\n this.end = end;\n this.bufferPos = start - offset;\n }\n static tempFromBytes(bytes) {\n return new FileSlice(bytes, toDataView(bytes), 0, 0, bytes.length);\n }\n get length() {\n return this.end - this.start;\n }\n get filePos() {\n return this.offset + this.bufferPos;\n }\n set filePos(value) {\n this.bufferPos = value - this.offset;\n }\n get remainingLength() {\n return Math.max(this.end - this.filePos, 0);\n }\n skip(byteCount) {\n this.bufferPos += byteCount;\n }\n slice(filePos, length = this.end - filePos) {\n if (filePos < this.start || filePos + length > this.end) {\n throw new RangeError(\"Slicing outside of original slice.\");\n }\n return new FileSlice(this.bytes, this.view, this.offset, filePos, filePos + length);\n }\n }\n var checkIsInRange = (slice, bytesToRead) => {\n if (slice.filePos < slice.start || slice.filePos + bytesToRead > slice.end) {\n throw new RangeError(`Tried reading [${slice.filePos}, ${slice.filePos + bytesToRead}), but slice is` + ` [${slice.start}, ${slice.end}). This is likely an internal error, please report it alongside the file` + ` that caused it.`);\n }\n };\n var readBytes = (slice, length) => {\n checkIsInRange(slice, length);\n const bytes = slice.bytes.subarray(slice.bufferPos, slice.bufferPos + length);\n slice.bufferPos += length;\n return bytes;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/muxer.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class Muxer {\n constructor(output) {\n this.mutex = new AsyncMutex;\n this.trackTimestampInfo = new WeakMap;\n this.output = output;\n }\n onTrackClose(track) {}\n validateTimestamp(track, timestampInSeconds, isKeyPacket) {\n let timestampInfo = this.trackTimestampInfo.get(track);\n if (!timestampInfo) {\n if (!isKeyPacket) {\n throw new Error(\"First packet must be a key packet.\");\n }\n timestampInfo = {\n maxTimestamp: timestampInSeconds,\n maxTimestampBeforeLastKeyPacket: null\n };\n this.trackTimestampInfo.set(track, timestampInfo);\n } else {\n if (isKeyPacket) {\n timestampInfo.maxTimestampBeforeLastKeyPacket = timestampInfo.maxTimestamp;\n }\n if (timestampInfo.maxTimestampBeforeLastKeyPacket !== null && timestampInSeconds < timestampInfo.maxTimestampBeforeLastKeyPacket) {\n throw new Error(`Timestamps cannot be smaller than the largest timestamp of the previous GOP (a GOP begins with a` + ` key packet and ends right before the next key packet). Got ${timestampInSeconds}s, but largest` + ` timestamp is ${timestampInfo.maxTimestampBeforeLastKeyPacket}s.`);\n }\n timestampInfo.maxTimestamp = Math.max(timestampInfo.maxTimestamp, timestampInSeconds);\n }\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/subtitles.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var inlineTimestampRegex = /<(?:(\\d{2}):)?(\\d{2}):(\\d{2}).(\\d{3})>/g;\n var timestampRegex = /(?:(\\d{2}):)?(\\d{2}):(\\d{2}).(\\d{3})/;\n var parseSubtitleTimestamp = (string) => {\n const match = timestampRegex.exec(string);\n if (!match)\n throw new Error(\"Expected match.\");\n return 60 * 60 * 1000 * Number(match[1] || \"0\") + 60 * 1000 * Number(match[2]) + 1000 * Number(match[3]) + Number(match[4]);\n };\n var formatSubtitleTimestamp = (timestamp) => {\n const hours = Math.floor(timestamp / (60 * 60 * 1000));\n const minutes = Math.floor(timestamp % (60 * 60 * 1000) / (60 * 1000));\n const seconds = Math.floor(timestamp % (60 * 1000) / 1000);\n const milliseconds = timestamp % 1000;\n return hours.toString().padStart(2, \"0\") + \":\" + minutes.toString().padStart(2, \"0\") + \":\" + seconds.toString().padStart(2, \"0\") + \".\" + milliseconds.toString().padStart(3, \"0\");\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-boxes.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class IsobmffBoxWriter {\n constructor(writer) {\n this.writer = writer;\n this.helper = new Uint8Array(8);\n this.helperView = new DataView(this.helper.buffer);\n this.offsets = new WeakMap;\n }\n writeU32(value) {\n this.helperView.setUint32(0, value, false);\n this.writer.write(this.helper.subarray(0, 4));\n }\n writeU64(value) {\n this.helperView.setUint32(0, Math.floor(value / 2 ** 32), false);\n this.helperView.setUint32(4, value, false);\n this.writer.write(this.helper.subarray(0, 8));\n }\n writeAscii(text) {\n for (let i = 0;i < text.length; i++) {\n this.helperView.setUint8(i % 8, text.charCodeAt(i));\n if (i % 8 === 7)\n this.writer.write(this.helper);\n }\n if (text.length % 8 !== 0) {\n this.writer.write(this.helper.subarray(0, text.length % 8));\n }\n }\n writeBox(box) {\n this.offsets.set(box, this.writer.getPos());\n if (box.contents && !box.children) {\n this.writeBoxHeader(box, box.size ?? box.contents.byteLength + 8);\n this.writer.write(box.contents);\n } else {\n const startPos = this.writer.getPos();\n this.writeBoxHeader(box, 0);\n if (box.contents)\n this.writer.write(box.contents);\n if (box.children) {\n for (const child of box.children)\n if (child)\n this.writeBox(child);\n }\n const endPos = this.writer.getPos();\n const size = box.size ?? endPos - startPos;\n this.writer.seek(startPos);\n this.writeBoxHeader(box, size);\n this.writer.seek(endPos);\n }\n }\n writeBoxHeader(box, size) {\n this.writeU32(box.largeSize ? 1 : size);\n this.writeAscii(box.type);\n if (box.largeSize)\n this.writeU64(size);\n }\n measureBoxHeader(box) {\n return 8 + (box.largeSize ? 8 : 0);\n }\n patchBox(box) {\n const boxOffset = this.offsets.get(box);\n assert(boxOffset !== undefined);\n const endPos = this.writer.getPos();\n this.writer.seek(boxOffset);\n this.writeBox(box);\n this.writer.seek(endPos);\n }\n measureBox(box) {\n if (box.contents && !box.children) {\n const headerSize = this.measureBoxHeader(box);\n return headerSize + box.contents.byteLength;\n } else {\n let result = this.measureBoxHeader(box);\n if (box.contents)\n result += box.contents.byteLength;\n if (box.children) {\n for (const child of box.children)\n if (child)\n result += this.measureBox(child);\n }\n return result;\n }\n }\n }\n var bytes = /* @__PURE__ */ new Uint8Array(8);\n var view = /* @__PURE__ */ new DataView(bytes.buffer);\n var u8 = (value) => {\n return [(value % 256 + 256) % 256];\n };\n var u16 = (value) => {\n view.setUint16(0, value, false);\n return [bytes[0], bytes[1]];\n };\n var i16 = (value) => {\n view.setInt16(0, value, false);\n return [bytes[0], bytes[1]];\n };\n var u24 = (value) => {\n view.setUint32(0, value, false);\n return [bytes[1], bytes[2], bytes[3]];\n };\n var u32 = (value) => {\n view.setUint32(0, value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3]];\n };\n var i32 = (value) => {\n view.setInt32(0, value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3]];\n };\n var u64 = (value) => {\n view.setUint32(0, Math.floor(value / 2 ** 32), false);\n view.setUint32(4, value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]];\n };\n var i64 = (value) => {\n view.setInt32(0, Math.floor(value / 2 ** 32), false);\n view.setUint32(4, value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]];\n };\n var fixed_8_8 = (value) => {\n view.setInt16(0, 2 ** 8 * value, false);\n return [bytes[0], bytes[1]];\n };\n var fixed_16_16 = (value) => {\n view.setInt32(0, 2 ** 16 * value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3]];\n };\n var fixed_2_30 = (value) => {\n view.setInt32(0, 2 ** 30 * value, false);\n return [bytes[0], bytes[1], bytes[2], bytes[3]];\n };\n var variableUnsignedInt = (value, byteLength) => {\n const bytes = [];\n let remaining = value;\n do {\n let byte = remaining & 127;\n remaining >>= 7;\n if (bytes.length > 0) {\n byte |= 128;\n }\n bytes.push(byte);\n if (byteLength !== undefined) {\n byteLength--;\n }\n } while (remaining > 0 || byteLength);\n return bytes.reverse();\n };\n var ascii = (text, nullTerminated = false) => {\n const bytes = Array(text.length).fill(null).map((_, i) => text.charCodeAt(i));\n if (nullTerminated)\n bytes.push(0);\n return bytes;\n };\n var matrixToBytes = (matrix) => {\n return [\n fixed_16_16(matrix[0]),\n fixed_16_16(matrix[1]),\n fixed_2_30(matrix[2]),\n fixed_16_16(matrix[3]),\n fixed_16_16(matrix[4]),\n fixed_2_30(matrix[5]),\n fixed_16_16(matrix[6]),\n fixed_16_16(matrix[7]),\n fixed_2_30(matrix[8])\n ];\n };\n var box = (type, contents, children) => ({\n type,\n contents: contents && new Uint8Array(contents.flat(10)),\n children\n });\n var fullBox = (type, version, flags, contents, children) => box(type, [u8(version), u24(flags), contents ?? []], children);\n var ftyp = (details) => {\n const minorVersion = 512;\n if (details.isQuickTime) {\n return box(\"ftyp\", [\n ascii(\"qt \"),\n u32(minorVersion),\n ascii(\"qt \")\n ]);\n }\n if (details.fragmented) {\n if (details.cmaf) {\n return box(\"ftyp\", [\n ascii(\"iso5\"),\n u32(minorVersion),\n ascii(\"iso5\"),\n ascii(\"iso6\"),\n ascii(\"mp41\"),\n ascii(\"cmfc\"),\n ascii(\"dash\")\n ]);\n } else {\n return box(\"ftyp\", [\n ascii(\"iso5\"),\n u32(minorVersion),\n ascii(\"iso5\"),\n ascii(\"iso6\"),\n ascii(\"mp41\")\n ]);\n }\n }\n return box(\"ftyp\", [\n ascii(\"isom\"),\n u32(minorVersion),\n ascii(\"isom\"),\n details.holdsAvc ? ascii(\"avc1\") : [],\n ascii(\"mp41\")\n ]);\n };\n var styp = () => box(\"styp\", [\n ascii(\"iso5\"),\n u32(0),\n ascii(\"iso5\"),\n ascii(\"iso6\"),\n ascii(\"mp41\"),\n ascii(\"cmfc\"),\n ascii(\"dash\")\n ]);\n var sidx = (muxer, referencedSize) => {\n const earliestPresentationTime = Math.max(0, muxer.minWrittenTimestamp);\n let duration = Math.max(0, muxer.maxWrittenEndTimestamp - earliestPresentationTime);\n if (!Number.isFinite(duration)) {\n duration = 0;\n }\n return fullBox(\"sidx\", 1, 0, [\n u32(1),\n u32(GLOBAL_TIMESCALE),\n u64(intoTimescale(earliestPresentationTime, GLOBAL_TIMESCALE)),\n u64(0),\n u16(0),\n u16(1),\n u32(referencedSize & 2147483647),\n u32(intoTimescale(duration, GLOBAL_TIMESCALE)),\n u32(0)\n ]);\n };\n var mdat = (reserveLargeSize) => ({ type: \"mdat\", largeSize: reserveLargeSize });\n var free = (size) => ({ type: \"free\", size });\n var moov = (muxer) => {\n return box(\"moov\", undefined, [\n mvhd(muxer.creationTime, muxer.trackDatas),\n ...muxer.trackDatas.map((x) => trak(x, muxer.creationTime)),\n muxer.isFragmented ? mvex(muxer.trackDatas) : null,\n udta(muxer)\n ]);\n };\n var mvhd = (creationTime, trackDatas) => {\n const duration = Math.max(0, ...trackDatas.map((trackData) => Math.max(0, intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(trackData.startTimestampOffset ?? 0, GLOBAL_TIMESCALE))));\n const nextTrackId = Math.max(0, ...trackDatas.map((x) => x.track.id)) + 1;\n const needsU64 = !isU32(creationTime) || !isU32(duration);\n const u32OrU64 = needsU64 ? u64 : u32;\n return fullBox(\"mvhd\", +needsU64, 0, [\n u32OrU64(creationTime),\n u32OrU64(creationTime),\n u32(GLOBAL_TIMESCALE),\n u32OrU64(duration),\n fixed_16_16(1),\n fixed_8_8(1),\n Array(10).fill(0),\n matrixToBytes(IDENTITY_MATRIX),\n Array(24).fill(0),\n u32(nextTrackId)\n ]);\n };\n var trak = (trackData, creationTime) => {\n const trackMetadata = getTrackMetadata(trackData);\n const needsEditList = trackData.startTimestampOffset !== null && trackData.startTimestampOffset !== 0;\n return box(\"trak\", undefined, [\n tkhd(trackData, creationTime),\n needsEditList ? edts(trackData) : null,\n mdia(trackData, creationTime),\n trackMetadata.name !== undefined ? box(\"udta\", undefined, [\n box(\"name\", [\n ...textEncoder.encode(trackMetadata.name)\n ])\n ]) : null\n ]);\n };\n var tkhd = (trackData, creationTime) => {\n const durationInGlobalTimescale = Math.max(0, intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(trackData.startTimestampOffset ?? 0, GLOBAL_TIMESCALE));\n const needsU64 = !isU32(creationTime) || !isU32(durationInGlobalTimescale);\n const u32OrU64 = needsU64 ? u64 : u32;\n let matrix;\n if (trackData.type === \"video\" && trackData.track.metadata.transformationMatrix) {\n matrix = trackData.track.metadata.transformationMatrix;\n } else if (trackData.type === \"video\") {\n const { rotation, flip } = trackData.track.metadata;\n const linear = multiplyMatrices(rotationMatrix(rotation ?? 0), scaleMatrix(flip ? -1 : 1, 1));\n matrix = centeredTransformationMatrix(linear, trackData.info.width, trackData.info.height);\n } else {\n matrix = IDENTITY_MATRIX;\n }\n let flags = 2;\n if (trackData.track.metadata.disposition?.default !== false) {\n flags |= 1;\n }\n const alternateGroup = trackData.type === \"video\" ? 0 : trackData.type === \"audio\" ? 1 : trackData.type === \"subtitle\" ? 2 : assertNever(trackData);\n return fullBox(\"tkhd\", +needsU64, flags, [\n u32OrU64(creationTime),\n u32OrU64(creationTime),\n u32(trackData.track.id),\n u32(0),\n u32OrU64(durationInGlobalTimescale),\n Array(8).fill(0),\n u16(0),\n u16(alternateGroup),\n fixed_8_8(trackData.type === \"audio\" ? 1 : 0),\n u16(0),\n matrixToBytes(matrix),\n fixed_16_16(trackData.type === \"video\" ? trackData.info.width : 0),\n fixed_16_16(trackData.type === \"video\" ? trackData.info.height : 0)\n ]);\n };\n var edts = (trackData) => {\n const offset = trackData.startTimestampOffset;\n assert(offset !== null);\n if (offset > 0) {\n const startOffset = intoTimescale(offset, GLOBAL_TIMESCALE);\n const mediaDuration = intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE);\n const needs64Bits = !isU32(startOffset) || !isU32(mediaDuration);\n const u32OrU64 = needs64Bits ? u64 : u32;\n const i32OrI64 = needs64Bits ? i64 : i32;\n return box(\"edts\", undefined, [\n fullBox(\"elst\", needs64Bits ? 1 : 0, 0, [\n u32(2),\n u32OrU64(startOffset),\n i32OrI64(-1),\n fixed_16_16(1),\n u32OrU64(mediaDuration),\n i32OrI64(0),\n fixed_16_16(1)\n ])\n ]);\n } else {\n const mediaTime = intoTimescale(-offset, trackData.timescale);\n const mediaDuration = Math.max(0, intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(offset, GLOBAL_TIMESCALE));\n const needs64Bits = !isI32(mediaTime) || !isU32(mediaDuration);\n const u32OrU64 = needs64Bits ? u64 : u32;\n const i32OrI64 = needs64Bits ? i64 : i32;\n return box(\"edts\", undefined, [\n fullBox(\"elst\", needs64Bits ? 1 : 0, 0, [\n u32(1),\n u32OrU64(mediaDuration),\n i32OrI64(mediaTime),\n fixed_16_16(1)\n ])\n ]);\n }\n };\n var mdia = (trackData, creationTime) => box(\"mdia\", undefined, [\n mdhd(trackData, creationTime),\n hdlr(true, TRACK_TYPE_TO_COMPONENT_SUBTYPE[trackData.type], TRACK_TYPE_TO_HANDLER_NAME[trackData.type]),\n minf(trackData)\n ]);\n var mdhd = (trackData, creationTime) => {\n const localDuration = intoTimescale(presentationSpan(trackData), trackData.timescale);\n const needsU64 = !isU32(creationTime) || !isU32(localDuration);\n const u32OrU64 = needsU64 ? u64 : u32;\n return fullBox(\"mdhd\", +needsU64, 0, [\n u32OrU64(creationTime),\n u32OrU64(creationTime),\n u32(trackData.timescale),\n u32OrU64(localDuration),\n u16(getLanguageCodeInt(trackData.track.metadata.languageCode ?? UNDETERMINED_LANGUAGE)),\n u16(0)\n ]);\n };\n var TRACK_TYPE_TO_COMPONENT_SUBTYPE = {\n video: \"vide\",\n audio: \"soun\",\n subtitle: \"text\"\n };\n var TRACK_TYPE_TO_HANDLER_NAME = {\n video: \"MediabunnyVideoHandler\",\n audio: \"MediabunnySoundHandler\",\n subtitle: \"MediabunnyTextHandler\"\n };\n var hdlr = (hasComponentType, handlerType, name, manufacturer = \"\\x00\\x00\\x00\\x00\") => fullBox(\"hdlr\", 0, 0, [\n hasComponentType ? ascii(\"mhlr\") : u32(0),\n ascii(handlerType),\n ascii(manufacturer),\n u32(0),\n u32(0),\n ascii(name, true)\n ]);\n var minf = (trackData) => box(\"minf\", undefined, [\n TRACK_TYPE_TO_HEADER_BOX[trackData.type](),\n dinf(),\n stbl(trackData)\n ]);\n var vmhd = () => fullBox(\"vmhd\", 0, 1, [\n u16(0),\n u16(0),\n u16(0),\n u16(0)\n ]);\n var smhd = () => fullBox(\"smhd\", 0, 0, [\n u16(0),\n u16(0)\n ]);\n var nmhd = () => fullBox(\"nmhd\", 0, 0);\n var TRACK_TYPE_TO_HEADER_BOX = {\n video: vmhd,\n audio: smhd,\n subtitle: nmhd\n };\n var dinf = () => box(\"dinf\", undefined, [\n dref()\n ]);\n var dref = () => fullBox(\"dref\", 0, 0, [\n u32(1)\n ], [\n url()\n ]);\n var url = () => fullBox(\"url \", 0, 1);\n var stbl = (trackData) => {\n const needsCtts = trackData.compositionTimeOffsetTable.length > 1 || trackData.compositionTimeOffsetTable.some((x) => x.sampleCompositionTimeOffset !== 0);\n return box(\"stbl\", undefined, [\n stsd(trackData),\n stts(trackData),\n needsCtts ? ctts(trackData) : null,\n needsCtts ? cslg(trackData) : null,\n stsc(trackData),\n stsz(trackData),\n stco(trackData),\n stss(trackData)\n ]);\n };\n var stsd = (trackData) => {\n let sampleDescription;\n if (trackData.type === \"video\") {\n sampleDescription = videoSampleDescription(videoCodecToBoxName(trackData.track.source._codec, trackData.info.decoderConfig.codec), trackData);\n } else if (trackData.type === \"audio\") {\n const boxName = audioCodecToBoxName(trackData.track.source._codec, trackData.info.decoderConfig.codec, trackData.muxer.isQuickTime);\n assert(boxName);\n sampleDescription = soundSampleDescription(boxName, trackData);\n } else if (trackData.type === \"subtitle\") {\n sampleDescription = subtitleSampleDescription(SUBTITLE_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData);\n }\n assert(sampleDescription);\n return fullBox(\"stsd\", 0, 0, [\n u32(1)\n ], [\n sampleDescription\n ]);\n };\n var videoSampleDescription = (compressionType, trackData) => box(compressionType, [\n Array(6).fill(0),\n u16(1),\n u16(0),\n u16(0),\n Array(12).fill(0),\n u16(trackData.info.width),\n u16(trackData.info.height),\n u32(4718592),\n u32(4718592),\n u32(0),\n u16(1),\n u8(VERSIONED_NAME.length),\n ascii(VERSIONED_NAME),\n Array(31 - VERSIONED_NAME.length).fill(0),\n u16(trackData.info.hasAlphaChannel ? 32 : 24),\n i16(65535)\n ], [\n VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec]?.(trackData) ?? null,\n pasp(trackData),\n colorSpaceIsEmpty(trackData.info.decoderConfig.colorSpace) ? null : colr(trackData),\n btrt(trackData)\n ]);\n var btrt = (trackData) => {\n if (trackData.avgBitrate === 0 && trackData.maxBitrate === 0) {\n return null;\n }\n return box(\"btrt\", [\n u32(0),\n u32(trackData.maxBitrate),\n u32(trackData.avgBitrate)\n ]);\n };\n var pasp = (trackData) => {\n if (trackData.info.pixelAspectRatio.num === trackData.info.pixelAspectRatio.den) {\n return null;\n }\n return box(\"pasp\", [\n u32(trackData.info.pixelAspectRatio.num),\n u32(trackData.info.pixelAspectRatio.den)\n ]);\n };\n var colr = (trackData) => {\n const colorSpace = trackData.info.decoderConfig.colorSpace;\n return box(\"colr\", [\n ascii(trackData.muxer.isQuickTime ? \"nclc\" : \"nclx\"),\n u16(colorSpace?.primaries != null ? COLOR_PRIMARIES_MAP[colorSpace.primaries] : 2),\n u16(colorSpace?.transfer != null ? TRANSFER_CHARACTERISTICS_MAP[colorSpace.transfer] : 2),\n u16(colorSpace?.matrix != null ? MATRIX_COEFFICIENTS_MAP[colorSpace.matrix] : 2),\n trackData.muxer.isQuickTime ? [] : u8((colorSpace?.fullRange ? 1 : 0) << 7)\n ]);\n };\n var avcC = (trackData) => trackData.info.decoderConfig && box(\"avcC\", [\n ...toUint8Array(trackData.info.decoderConfig.description)\n ]);\n var hvcC = (trackData) => trackData.info.decoderConfig && box(\"hvcC\", [\n ...toUint8Array(trackData.info.decoderConfig.description)\n ]);\n var vpcC = (trackData) => {\n if (!trackData.info.decoderConfig) {\n return null;\n }\n const decoderConfig = trackData.info.decoderConfig;\n const parts = decoderConfig.codec.split(\".\");\n const profile = Number(parts[1]);\n const level = Number(parts[2]);\n const bitDepth = Number(parts[3]);\n const chromaSubsampling = parts[4] ? Number(parts[4]) : 1;\n const videoFullRangeFlag = parts[8] ? Number(parts[8]) : Number(decoderConfig.colorSpace?.fullRange ?? 0);\n const thirdByte = (bitDepth << 4) + (chromaSubsampling << 1) + videoFullRangeFlag;\n const colourPrimaries = parts[5] ? Number(parts[5]) : decoderConfig.colorSpace?.primaries ? COLOR_PRIMARIES_MAP[decoderConfig.colorSpace.primaries] : 1;\n const transferCharacteristics = parts[6] ? Number(parts[6]) : decoderConfig.colorSpace?.transfer ? TRANSFER_CHARACTERISTICS_MAP[decoderConfig.colorSpace.transfer] : 1;\n const matrixCoefficients = parts[7] ? Number(parts[7]) : decoderConfig.colorSpace?.matrix ? MATRIX_COEFFICIENTS_MAP[decoderConfig.colorSpace.matrix] : 1;\n return fullBox(\"vpcC\", 1, 0, [\n u8(profile),\n u8(level),\n u8(thirdByte),\n u8(colourPrimaries),\n u8(transferCharacteristics),\n u8(matrixCoefficients),\n u16(0)\n ]);\n };\n var av1C = (trackData) => {\n return box(\"av1C\", generateAv1CodecConfigurationFromCodecString(trackData.info.decoderConfig.codec));\n };\n var soundSampleDescription = (compressionType, trackData) => {\n let version = 0;\n let contents;\n let sampleSizeInBits = 16;\n const isPcmCodec = PCM_AUDIO_CODECS.includes(trackData.track.source._codec);\n if (isPcmCodec) {\n const codec = trackData.track.source._codec;\n const { sampleSize } = parsePcmCodec(codec);\n sampleSizeInBits = 8 * sampleSize;\n if (sampleSizeInBits > 16) {\n version = 1;\n }\n }\n if (trackData.muxer.isQuickTime) {\n version = 1;\n }\n if (version === 0) {\n contents = [\n Array(6).fill(0),\n u16(1),\n u16(version),\n u16(0),\n u32(0),\n u16(trackData.info.numberOfChannels),\n u16(sampleSizeInBits),\n u16(0),\n u16(0),\n u16(trackData.info.sampleRate < 2 ** 16 ? trackData.info.sampleRate : 0),\n u16(0)\n ];\n } else {\n const compressionId = isPcmCodec ? 0 : -2;\n contents = [\n Array(6).fill(0),\n u16(1),\n u16(version),\n u16(0),\n u32(0),\n u16(trackData.info.numberOfChannels),\n u16(Math.min(sampleSizeInBits, 16)),\n i16(compressionId),\n u16(0),\n u16(trackData.info.sampleRate < 2 ** 16 ? trackData.info.sampleRate : 0),\n u16(0),\n isPcmCodec ? [\n u32(1),\n u32(sampleSizeInBits / 8),\n u32(trackData.info.numberOfChannels * sampleSizeInBits / 8)\n ] : [\n u32(0),\n u32(0),\n u32(0)\n ],\n u32(2)\n ];\n }\n return box(compressionType, contents, [\n audioCodecToConfigurationBox(trackData.track.source._codec, trackData.muxer.isQuickTime)?.(trackData) ?? null,\n btrt(trackData)\n ]);\n };\n var esds = (trackData) => {\n let objectTypeIndication;\n switch (trackData.track.source._codec) {\n case \"aac\":\n {\n objectTypeIndication = 64;\n }\n ;\n break;\n case \"mp3\":\n {\n objectTypeIndication = 107;\n }\n ;\n break;\n case \"vorbis\":\n {\n objectTypeIndication = 221;\n }\n ;\n break;\n default:\n throw new Error(`Unhandled audio codec: ${trackData.track.source._codec}`);\n }\n let bytes = [\n ...u8(objectTypeIndication),\n ...u8(21),\n ...u24(0),\n ...u32(trackData.maxBitrate),\n ...u32(trackData.avgBitrate)\n ];\n if (trackData.info.decoderConfig.description) {\n const description = toUint8Array(trackData.info.decoderConfig.description);\n bytes = [\n ...bytes,\n ...u8(5),\n ...variableUnsignedInt(description.byteLength),\n ...description\n ];\n }\n bytes = [\n ...u16(1),\n ...u8(0),\n ...u8(4),\n ...variableUnsignedInt(bytes.length),\n ...bytes,\n ...u8(6),\n ...u8(1),\n ...u8(2)\n ];\n bytes = [\n ...u8(3),\n ...variableUnsignedInt(bytes.length),\n ...bytes\n ];\n return fullBox(\"esds\", 0, 0, bytes);\n };\n var wave = (trackData) => {\n return box(\"wave\", undefined, [\n frma(trackData),\n enda(trackData),\n box(\"\\x00\\x00\\x00\\x00\")\n ]);\n };\n var frma = (trackData) => {\n return box(\"frma\", [\n ascii(audioCodecToBoxName(trackData.track.source._codec, trackData.info.decoderConfig.codec, trackData.muxer.isQuickTime))\n ]);\n };\n var enda = (trackData) => {\n const { littleEndian } = parsePcmCodec(trackData.track.source._codec);\n return box(\"enda\", [\n u16(+littleEndian)\n ]);\n };\n var dOps = (trackData) => {\n let outputChannelCount = trackData.info.numberOfChannels;\n let preSkip = 3840;\n let inputSampleRate = trackData.info.sampleRate;\n let outputGain = 0;\n let channelMappingFamily = 0;\n let channelMappingTable = new Uint8Array(0);\n const description = trackData.info.decoderConfig?.description;\n if (description) {\n assert(description.byteLength >= 18);\n const bytes = toUint8Array(description);\n const header = parseOpusIdentificationHeader(bytes);\n outputChannelCount = header.outputChannelCount;\n preSkip = header.preSkip;\n inputSampleRate = header.inputSampleRate;\n outputGain = header.outputGain;\n channelMappingFamily = header.channelMappingFamily;\n if (header.channelMappingTable) {\n channelMappingTable = header.channelMappingTable;\n }\n }\n return box(\"dOps\", [\n u8(0),\n u8(outputChannelCount),\n u16(preSkip),\n u32(inputSampleRate),\n i16(outputGain),\n u8(channelMappingFamily),\n ...channelMappingTable\n ]);\n };\n var dfLa = (trackData) => {\n const description = trackData.info.decoderConfig?.description;\n assert(description);\n const bytes = toUint8Array(description);\n return fullBox(\"dfLa\", 0, 0, [\n ...bytes.subarray(4)\n ]);\n };\n var pcmC = (trackData) => {\n const { littleEndian, sampleSize } = parsePcmCodec(trackData.track.source._codec);\n const formatFlags = +littleEndian;\n return fullBox(\"pcmC\", 0, 0, [\n u8(formatFlags),\n u8(8 * sampleSize)\n ]);\n };\n var dac3 = (trackData) => {\n assert(trackData.info.primingPacket);\n const frameInfo = parseAc3SyncFrame(trackData.info.primingPacket.data);\n if (!frameInfo) {\n throw new Error(\"Couldn't extract AC-3 frame info from the audio packet. \" + \"Ensure the packets contain valid AC-3 sync frames (as specified in ETSI TS 102 366).\");\n }\n const bytes = new Uint8Array(3);\n const bitstream = new Bitstream(bytes);\n bitstream.writeBits(2, frameInfo.fscod);\n bitstream.writeBits(5, frameInfo.bsid);\n bitstream.writeBits(3, frameInfo.bsmod);\n bitstream.writeBits(3, frameInfo.acmod);\n bitstream.writeBits(1, frameInfo.lfeon);\n bitstream.writeBits(5, frameInfo.bitRateCode);\n bitstream.writeBits(5, 0);\n return box(\"dac3\", [...bytes]);\n };\n var dec3 = (trackData) => {\n assert(trackData.info.primingPacket);\n const frameInfo = parseEac3SyncFrame(trackData.info.primingPacket.data);\n if (!frameInfo) {\n throw new Error(\"Couldn't extract E-AC-3 frame info from the audio packet. \" + \"Ensure the packets contain valid E-AC-3 sync frames (as specified in ETSI TS 102 366).\");\n }\n let totalBits = 16;\n for (const sub of frameInfo.substreams) {\n totalBits += 23;\n if (sub.numDepSub > 0) {\n totalBits += 9;\n } else {\n totalBits += 1;\n }\n }\n const size = Math.ceil(totalBits / 8);\n const bytes = new Uint8Array(size);\n const bitstream = new Bitstream(bytes);\n bitstream.writeBits(13, frameInfo.dataRate);\n bitstream.writeBits(3, frameInfo.substreams.length - 1);\n for (const sub of frameInfo.substreams) {\n bitstream.writeBits(2, sub.fscod);\n bitstream.writeBits(5, sub.bsid);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(1, 0);\n bitstream.writeBits(3, sub.bsmod);\n bitstream.writeBits(3, sub.acmod);\n bitstream.writeBits(1, sub.lfeon);\n bitstream.writeBits(3, 0);\n bitstream.writeBits(4, sub.numDepSub);\n if (sub.numDepSub > 0) {\n bitstream.writeBits(9, sub.chanLoc);\n } else {\n bitstream.writeBits(1, 0);\n }\n }\n return box(\"dec3\", [...bytes]);\n };\n var ddts = (trackData) => {\n assert(trackData.info.primingPacket);\n const frameInfo = parseDtsFrame(trackData.info.primingPacket.data);\n if (!frameInfo) {\n throw new Error(\"Couldn't extract DTS frame info from the audio packet. \" + \"Ensure the packets contain valid DTS frames as specified in ETSI TS 102 114.\");\n }\n return box(\"ddts\", [...buildDtsSpecificBox(frameInfo)]);\n };\n var subtitleSampleDescription = (compressionType, trackData) => box(compressionType, [\n Array(6).fill(0),\n u16(1)\n ], [\n SUBTITLE_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData),\n btrt(trackData)\n ]);\n var vttC = (trackData) => box(\"vttC\", [\n ...textEncoder.encode(trackData.info.config.description)\n ]);\n var stts = (trackData) => {\n return fullBox(\"stts\", 0, 0, [\n u32(trackData.timeToSampleTable.length),\n trackData.timeToSampleTable.map((x) => [\n u32(x.sampleCount),\n u32(x.sampleDelta)\n ])\n ]);\n };\n var stss = (trackData) => {\n if (trackData.samples.every((x) => x.type === \"key\"))\n return null;\n const keySamples = [...trackData.samples.entries()].filter(([, sample]) => sample.type === \"key\");\n return fullBox(\"stss\", 0, 0, [\n u32(keySamples.length),\n keySamples.map(([index]) => u32(index + 1))\n ]);\n };\n var stsc = (trackData) => {\n return fullBox(\"stsc\", 0, 0, [\n u32(trackData.compactlyCodedChunkTable.length),\n trackData.compactlyCodedChunkTable.map((x) => [\n u32(x.firstChunk),\n u32(x.samplesPerChunk),\n u32(1)\n ])\n ]);\n };\n var stsz = (trackData) => {\n if (trackData.type === \"audio\" && trackData.info.requiresPcmTransformation) {\n const { sampleSize } = parsePcmCodec(trackData.track.source._codec);\n return fullBox(\"stsz\", 0, 0, [\n u32(sampleSize * trackData.info.numberOfChannels),\n u32(trackData.samples.reduce((acc, x) => acc + intoTimescale(x.duration, trackData.timescale), 0))\n ]);\n }\n return fullBox(\"stsz\", 0, 0, [\n u32(0),\n u32(trackData.samples.length),\n trackData.samples.map((x) => u32(x.size))\n ]);\n };\n var stco = (trackData) => {\n if (trackData.finalizedChunks.length > 0 && last(trackData.finalizedChunks).offset >= 2 ** 32) {\n return fullBox(\"co64\", 0, 0, [\n u32(trackData.finalizedChunks.length),\n trackData.finalizedChunks.map((x) => u64(x.offset))\n ]);\n }\n return fullBox(\"stco\", 0, 0, [\n u32(trackData.finalizedChunks.length),\n trackData.finalizedChunks.map((x) => u32(x.offset))\n ]);\n };\n var ctts = (trackData) => {\n return fullBox(\"ctts\", 1, 0, [\n u32(trackData.compositionTimeOffsetTable.length),\n trackData.compositionTimeOffsetTable.map((x) => [\n u32(x.sampleCount),\n i32(x.sampleCompositionTimeOffset)\n ])\n ]);\n };\n var cslg = (trackData) => {\n let leastDecodeToDisplayDelta = Infinity;\n let greatestDecodeToDisplayDelta = -Infinity;\n let compositionStartTime = Infinity;\n let compositionEndTime = -Infinity;\n assert(trackData.compositionTimeOffsetTable.length > 0);\n assert(trackData.samples.length > 0);\n for (let i = 0;i < trackData.compositionTimeOffsetTable.length; i++) {\n const entry = trackData.compositionTimeOffsetTable[i];\n leastDecodeToDisplayDelta = Math.min(leastDecodeToDisplayDelta, entry.sampleCompositionTimeOffset);\n greatestDecodeToDisplayDelta = Math.max(greatestDecodeToDisplayDelta, entry.sampleCompositionTimeOffset);\n }\n for (let i = 0;i < trackData.samples.length; i++) {\n const sample = trackData.samples[i];\n compositionStartTime = Math.min(compositionStartTime, intoTimescale(sample.timestamp, trackData.timescale));\n compositionEndTime = Math.max(compositionEndTime, intoTimescale(sample.timestamp + sample.duration, trackData.timescale));\n }\n const compositionToDtsShift = Math.max(-leastDecodeToDisplayDelta, 0);\n if (compositionEndTime >= 2 ** 31) {\n return null;\n }\n return fullBox(\"cslg\", 0, 0, [\n i32(compositionToDtsShift),\n i32(leastDecodeToDisplayDelta),\n i32(greatestDecodeToDisplayDelta),\n i32(compositionStartTime),\n i32(compositionEndTime)\n ]);\n };\n var mvex = (trackDatas) => {\n return box(\"mvex\", undefined, trackDatas.map(trex));\n };\n var trex = (trackData) => {\n return fullBox(\"trex\", 0, 0, [\n u32(trackData.track.id),\n u32(1),\n u32(0),\n u32(0),\n u32(0)\n ]);\n };\n var moof = (sequenceNumber, trackDatas) => {\n return box(\"moof\", undefined, [\n mfhd(sequenceNumber),\n ...trackDatas.map(traf)\n ]);\n };\n var mfhd = (sequenceNumber) => {\n return fullBox(\"mfhd\", 0, 0, [\n u32(sequenceNumber)\n ]);\n };\n var fragmentSampleFlags = (sample) => {\n let byte1 = 0;\n let byte2 = 0;\n const byte3 = 0;\n const byte4 = 0;\n const sampleIsDifferenceSample = sample.type === \"delta\";\n byte2 |= +sampleIsDifferenceSample;\n if (sampleIsDifferenceSample) {\n byte1 |= 1;\n } else {\n byte1 |= 2;\n }\n return byte1 << 24 | byte2 << 16 | byte3 << 8 | byte4;\n };\n var traf = (trackData) => {\n return box(\"traf\", undefined, [\n tfhd(trackData),\n tfdt(trackData),\n trun(trackData)\n ]);\n };\n var tfhd = (trackData) => {\n assert(trackData.currentChunk);\n let tfFlags = 0;\n tfFlags |= 8;\n tfFlags |= 16;\n tfFlags |= 32;\n tfFlags |= 131072;\n const referenceSample = trackData.currentChunk.samples[1] ?? trackData.currentChunk.samples[0];\n const referenceSampleInfo = {\n duration: referenceSample.timescaleUnitsToNextSample,\n size: referenceSample.size,\n flags: fragmentSampleFlags(referenceSample)\n };\n return fullBox(\"tfhd\", 0, tfFlags, [\n u32(trackData.track.id),\n u32(referenceSampleInfo.duration),\n u32(referenceSampleInfo.size),\n u32(referenceSampleInfo.flags)\n ]);\n };\n var tfdt = (trackData) => {\n assert(trackData.currentChunk);\n return fullBox(\"tfdt\", 1, 0, [\n u64(intoTimescale(trackData.currentChunk.startTimestamp, trackData.timescale))\n ]);\n };\n var trun = (trackData) => {\n assert(trackData.currentChunk);\n const allSampleDurations = trackData.currentChunk.samples.map((x) => x.timescaleUnitsToNextSample);\n const allSampleSizes = trackData.currentChunk.samples.map((x) => x.size);\n const allSampleFlags = trackData.currentChunk.samples.map(fragmentSampleFlags);\n const allSampleCompositionTimeOffsets = trackData.currentChunk.samples.map((x) => intoTimescale(x.timestamp - x.decodeTimestamp, trackData.timescale));\n const uniqueSampleDurations = new Set(allSampleDurations);\n const uniqueSampleSizes = new Set(allSampleSizes);\n const uniqueSampleFlags = new Set(allSampleFlags);\n const uniqueSampleCompositionTimeOffsets = new Set(allSampleCompositionTimeOffsets);\n const firstSampleFlagsPresent = uniqueSampleFlags.size === 2 && allSampleFlags[0] !== allSampleFlags[1];\n const sampleDurationPresent = uniqueSampleDurations.size > 1;\n const sampleSizePresent = uniqueSampleSizes.size > 1;\n const sampleFlagsPresent = !firstSampleFlagsPresent && uniqueSampleFlags.size > 1;\n const sampleCompositionTimeOffsetsPresent = uniqueSampleCompositionTimeOffsets.size > 1 || [...uniqueSampleCompositionTimeOffsets].some((x) => x !== 0);\n let flags = 0;\n flags |= 1;\n flags |= 4 * +firstSampleFlagsPresent;\n flags |= 256 * +sampleDurationPresent;\n flags |= 512 * +sampleSizePresent;\n flags |= 1024 * +sampleFlagsPresent;\n flags |= 2048 * +sampleCompositionTimeOffsetsPresent;\n return fullBox(\"trun\", 1, flags, [\n u32(trackData.currentChunk.samples.length),\n u32(trackData.currentChunk.offset - trackData.currentChunk.moofOffset || 0),\n firstSampleFlagsPresent ? u32(allSampleFlags[0]) : [],\n trackData.currentChunk.samples.map((_, i) => [\n sampleDurationPresent ? u32(allSampleDurations[i]) : [],\n sampleSizePresent ? u32(allSampleSizes[i]) : [],\n sampleFlagsPresent ? u32(allSampleFlags[i]) : [],\n sampleCompositionTimeOffsetsPresent ? i32(allSampleCompositionTimeOffsets[i]) : []\n ])\n ]);\n };\n var mfra = (trackDatas) => {\n return box(\"mfra\", undefined, [\n ...trackDatas.map(tfra),\n mfro()\n ]);\n };\n var tfra = (trackData) => {\n const version = 1;\n return fullBox(\"tfra\", version, 0, [\n u32(trackData.track.id),\n u32(63),\n u32(trackData.finalizedChunks.length),\n trackData.finalizedChunks.map((chunk) => [\n u64(intoTimescale(chunk.samples[0].timestamp, trackData.timescale)),\n u64(chunk.moofOffset),\n u32(chunk.trafIndex + 1),\n u32(1),\n u32(1)\n ])\n ]);\n };\n var mfro = () => {\n return fullBox(\"mfro\", 0, 0, [\n u32(0)\n ]);\n };\n var vtte = () => box(\"vtte\");\n var vttc = (payload, timestamp, identifier, settings, sourceId) => box(\"vttc\", undefined, [\n sourceId !== null ? box(\"vsid\", [i32(sourceId)]) : null,\n identifier !== null ? box(\"iden\", [...textEncoder.encode(identifier)]) : null,\n timestamp !== null ? box(\"ctim\", [...textEncoder.encode(formatSubtitleTimestamp(timestamp))]) : null,\n settings !== null ? box(\"sttg\", [...textEncoder.encode(settings)]) : null,\n box(\"payl\", [...textEncoder.encode(payload)])\n ]);\n var vtta = (notes) => box(\"vtta\", [...textEncoder.encode(notes)]);\n var udta = (muxer) => {\n const boxes = [];\n const metadataFormat = muxer.format._options.metadataFormat ?? \"auto\";\n const metadataTags = muxer.output._metadataTags;\n if (metadataFormat === \"mdir\" || metadataFormat === \"auto\" && !muxer.isQuickTime) {\n const metaBox = metaMdir(metadataTags);\n if (metaBox)\n boxes.push(metaBox);\n } else if (metadataFormat === \"mdta\") {\n const metaBox = metaMdta(metadataTags);\n if (metaBox)\n boxes.push(metaBox);\n } else if (metadataFormat === \"udta\" || metadataFormat === \"auto\" && muxer.isQuickTime) {\n addQuickTimeMetadataTagBoxes(boxes, muxer.output._metadataTags);\n }\n if (boxes.length === 0) {\n return null;\n }\n return box(\"udta\", undefined, boxes);\n };\n var addQuickTimeMetadataTagBoxes = (boxes, tags) => {\n for (const { key, value } of keyValueIterator(tags)) {\n switch (key) {\n case \"title\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9nam\", value));\n }\n ;\n break;\n case \"description\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9des\", value));\n }\n ;\n break;\n case \"artist\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9ART\", value));\n }\n ;\n break;\n case \"album\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9alb\", value));\n }\n ;\n break;\n case \"albumArtist\":\n {\n boxes.push(metadataTagStringBoxShort(\"albr\", value));\n }\n ;\n break;\n case \"genre\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9gen\", value));\n }\n ;\n break;\n case \"date\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9day\", value.toISOString().slice(0, 10)));\n }\n ;\n break;\n case \"comment\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9cmt\", value));\n }\n ;\n break;\n case \"lyrics\":\n {\n boxes.push(metadataTagStringBoxShort(\"\u00A9lyr\", value));\n }\n ;\n break;\n case \"raw\":\n {}\n ;\n break;\n case \"discNumber\":\n case \"discsTotal\":\n case \"trackNumber\":\n case \"tracksTotal\":\n case \"beatsPerMinute\":\n case \"images\":\n {}\n ;\n break;\n default:\n assertNever(key);\n }\n }\n if (tags.raw) {\n for (const key in tags.raw) {\n const value = tags.raw[key];\n if (value == null || key.length !== 4 || boxes.some((x) => x.type === key)) {\n continue;\n }\n if (typeof value === \"string\") {\n boxes.push(metadataTagStringBoxShort(key, value));\n } else if (value instanceof Uint8Array) {\n boxes.push(box(key, Array.from(value)));\n }\n }\n }\n };\n var metadataTagStringBoxShort = (name, value) => {\n const encoded = textEncoder.encode(value);\n return box(name, [\n u16(encoded.length),\n u16(getLanguageCodeInt(\"und\")),\n Array.from(encoded)\n ]);\n };\n var DATA_BOX_MIME_TYPE_MAP = {\n \"image/jpeg\": 13,\n \"image/png\": 14,\n \"image/bmp\": 27\n };\n var generateMetadataPairs = (tags, isMdta) => {\n const pairs = [];\n for (const { key, value } of keyValueIterator(tags)) {\n switch (key) {\n case \"title\":\n {\n pairs.push({ key: isMdta ? \"title\" : \"\u00A9nam\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"description\":\n {\n pairs.push({ key: isMdta ? \"description\" : \"\u00A9des\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"artist\":\n {\n pairs.push({ key: isMdta ? \"artist\" : \"\u00A9ART\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"album\":\n {\n pairs.push({ key: isMdta ? \"album\" : \"\u00A9alb\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"albumArtist\":\n {\n pairs.push({ key: isMdta ? \"album_artist\" : \"aART\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"comment\":\n {\n pairs.push({ key: isMdta ? \"comment\" : \"\u00A9cmt\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"genre\":\n {\n pairs.push({ key: isMdta ? \"genre\" : \"\u00A9gen\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"beatsPerMinute\":\n {\n if (!isMdta) {\n pairs.push({ key: \"tmpo\", value: box(\"data\", [\n u32(21),\n u32(0),\n u16(value)\n ]) });\n }\n }\n ;\n break;\n case \"lyrics\":\n {\n pairs.push({ key: isMdta ? \"lyrics\" : \"\u00A9lyr\", value: dataStringBoxLong(value) });\n }\n ;\n break;\n case \"date\":\n {\n pairs.push({\n key: isMdta ? \"date\" : \"\u00A9day\",\n value: dataStringBoxLong(value.toISOString().slice(0, 10))\n });\n }\n ;\n break;\n case \"images\":\n {\n for (const image of value) {\n if (image.kind !== \"coverFront\") {\n continue;\n }\n pairs.push({ key: \"covr\", value: box(\"data\", [\n u32(DATA_BOX_MIME_TYPE_MAP[image.mimeType] ?? 0),\n u32(0),\n Array.from(image.data)\n ]) });\n }\n }\n ;\n break;\n case \"trackNumber\":\n {\n if (isMdta) {\n const string = tags.tracksTotal !== undefined ? `${value}/${tags.tracksTotal}` : value.toString();\n pairs.push({ key: \"track\", value: dataStringBoxLong(string) });\n } else {\n pairs.push({ key: \"trkn\", value: box(\"data\", [\n u32(0),\n u32(0),\n u16(0),\n u16(value),\n u16(tags.tracksTotal ?? 0),\n u16(0)\n ]) });\n }\n }\n ;\n break;\n case \"discNumber\":\n {\n if (!isMdta) {\n pairs.push({ key: \"disc\", value: box(\"data\", [\n u32(0),\n u32(0),\n u16(0),\n u16(value),\n u16(tags.discsTotal ?? 0),\n u16(0)\n ]) });\n }\n }\n ;\n break;\n case \"tracksTotal\":\n case \"discsTotal\":\n {}\n ;\n break;\n case \"raw\":\n {}\n ;\n break;\n default:\n assertNever(key);\n }\n }\n if (tags.raw) {\n for (const key in tags.raw) {\n const value = tags.raw[key];\n if (value == null || !isMdta && key.length !== 4 || pairs.some((x) => x.key === key)) {\n continue;\n }\n if (typeof value === \"string\") {\n pairs.push({ key, value: dataStringBoxLong(value) });\n } else if (value instanceof Uint8Array) {\n pairs.push({ key, value: box(\"data\", [\n u32(0),\n u32(0),\n Array.from(value)\n ]) });\n } else if (value instanceof RichImageData) {\n pairs.push({ key, value: box(\"data\", [\n u32(DATA_BOX_MIME_TYPE_MAP[value.mimeType] ?? 0),\n u32(0),\n Array.from(value.data)\n ]) });\n }\n }\n }\n return pairs;\n };\n var metaMdir = (tags) => {\n const pairs = generateMetadataPairs(tags, false);\n if (pairs.length === 0) {\n return null;\n }\n return fullBox(\"meta\", 0, 0, undefined, [\n hdlr(false, \"mdir\", \"\", \"appl\"),\n box(\"ilst\", undefined, pairs.map((pair) => box(pair.key, undefined, [pair.value])))\n ]);\n };\n var metaMdta = (tags) => {\n const pairs = generateMetadataPairs(tags, true);\n if (pairs.length === 0) {\n return null;\n }\n return box(\"meta\", undefined, [\n hdlr(false, \"mdta\", \"\"),\n fullBox(\"keys\", 0, 0, [\n u32(pairs.length)\n ], pairs.map((pair) => box(\"mdta\", [\n ...textEncoder.encode(pair.key)\n ]))),\n box(\"ilst\", undefined, pairs.map((pair, i) => {\n const boxName = String.fromCharCode(...u32(i + 1));\n return box(boxName, undefined, [pair.value]);\n }))\n ]);\n };\n var dataStringBoxLong = (value) => {\n return box(\"data\", [\n u32(1),\n u32(0),\n ...textEncoder.encode(value)\n ]);\n };\n var videoCodecToBoxName = (codec, fullCodecString) => {\n switch (codec) {\n case \"avc\":\n return fullCodecString.startsWith(\"avc3\") ? \"avc3\" : \"avc1\";\n case \"hevc\":\n return \"hvc1\";\n case \"vp8\":\n return \"vp08\";\n case \"vp9\":\n return \"vp09\";\n case \"av1\":\n return \"av01\";\n case \"prores\":\n return fullCodecString;\n }\n };\n var VIDEO_CODEC_TO_CONFIGURATION_BOX = {\n avc: avcC,\n hevc: hvcC,\n vp8: vpcC,\n vp9: vpcC,\n av1: av1C,\n prores: null\n };\n var audioCodecToBoxName = (codec, fullCodecString, isQuickTime) => {\n switch (codec) {\n case \"aac\":\n return \"mp4a\";\n case \"mp3\":\n return \"mp4a\";\n case \"opus\":\n return \"Opus\";\n case \"vorbis\":\n return \"mp4a\";\n case \"flac\":\n return \"fLaC\";\n case \"ulaw\":\n return \"ulaw\";\n case \"alaw\":\n return \"alaw\";\n case \"pcm-u8\":\n return \"raw \";\n case \"pcm-s8\":\n return \"sowt\";\n case \"ac3\":\n return \"ac-3\";\n case \"eac3\":\n return \"ec-3\";\n case \"dts\":\n return fullCodecString;\n }\n if (isQuickTime) {\n switch (codec) {\n case \"pcm-s16\":\n return \"sowt\";\n case \"pcm-s16be\":\n return \"twos\";\n case \"pcm-s24\":\n return \"in24\";\n case \"pcm-s24be\":\n return \"in24\";\n case \"pcm-s32\":\n return \"in32\";\n case \"pcm-s32be\":\n return \"in32\";\n case \"pcm-f32\":\n return \"fl32\";\n case \"pcm-f32be\":\n return \"fl32\";\n case \"pcm-f64\":\n return \"fl64\";\n case \"pcm-f64be\":\n return \"fl64\";\n }\n } else {\n switch (codec) {\n case \"pcm-s16\":\n return \"ipcm\";\n case \"pcm-s16be\":\n return \"ipcm\";\n case \"pcm-s24\":\n return \"ipcm\";\n case \"pcm-s24be\":\n return \"ipcm\";\n case \"pcm-s32\":\n return \"ipcm\";\n case \"pcm-s32be\":\n return \"ipcm\";\n case \"pcm-f32\":\n return \"fpcm\";\n case \"pcm-f32be\":\n return \"fpcm\";\n case \"pcm-f64\":\n return \"fpcm\";\n case \"pcm-f64be\":\n return \"fpcm\";\n }\n }\n };\n var audioCodecToConfigurationBox = (codec, isQuickTime) => {\n switch (codec) {\n case \"aac\":\n return esds;\n case \"mp3\":\n return esds;\n case \"opus\":\n return dOps;\n case \"vorbis\":\n return esds;\n case \"flac\":\n return dfLa;\n case \"ac3\":\n return dac3;\n case \"eac3\":\n return dec3;\n case \"dts\":\n return ddts;\n }\n if (isQuickTime) {\n switch (codec) {\n case \"pcm-s24\":\n return wave;\n case \"pcm-s24be\":\n return wave;\n case \"pcm-s32\":\n return wave;\n case \"pcm-s32be\":\n return wave;\n case \"pcm-f32\":\n return wave;\n case \"pcm-f32be\":\n return wave;\n case \"pcm-f64\":\n return wave;\n case \"pcm-f64be\":\n return wave;\n }\n } else {\n switch (codec) {\n case \"pcm-s16\":\n return pcmC;\n case \"pcm-s16be\":\n return pcmC;\n case \"pcm-s24\":\n return pcmC;\n case \"pcm-s24be\":\n return pcmC;\n case \"pcm-s32\":\n return pcmC;\n case \"pcm-s32be\":\n return pcmC;\n case \"pcm-f32\":\n return pcmC;\n case \"pcm-f32be\":\n return pcmC;\n case \"pcm-f64\":\n return pcmC;\n case \"pcm-f64be\":\n return pcmC;\n }\n }\n return null;\n };\n var SUBTITLE_CODEC_TO_BOX_NAME = {\n webvtt: \"wvtt\"\n };\n var SUBTITLE_CODEC_TO_CONFIGURATION_BOX = {\n webvtt: vttC\n };\n var getLanguageCodeInt = (code) => {\n assert(code.length === 3);\n let language = 0;\n for (let i = 0;i < 3; i++) {\n language <<= 5;\n language += code.charCodeAt(i) - 96;\n }\n return language;\n };\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/writer.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class Writer {\n constructor(target, isMonotonic) {\n this.finalized = false;\n this.started = false;\n this.pos = 0;\n this.trackedWrites = null;\n this.trackedStart = -1;\n this.trackedEnd = -1;\n if (target._writerAcquired) {\n throw new Error(\"Can't have multiple Writers for the same Target.\");\n }\n this.target = target;\n target._setMonotonicity(isMonotonic);\n target._writerAcquired = true;\n }\n start() {\n assert(!this.started);\n this.target._start();\n this.started = true;\n }\n write(data) {\n assert(this.started && !this.finalized);\n this.maybeTrackWrites(data);\n this.target._write(data, this.pos);\n this.pos += data.byteLength;\n }\n seek(newPos) {\n this.pos = newPos;\n }\n getPos() {\n return this.pos;\n }\n async flush() {\n assert(this.started && !this.finalized);\n return this.target._flush();\n }\n async finalize() {\n assert(this.started && !this.finalized);\n await this.target._finalize();\n this.finalized = true;\n }\n maybeTrackWrites(data) {\n if (!this.trackedWrites) {\n return;\n }\n let pos = this.getPos();\n if (pos < this.trackedStart) {\n if (pos + data.byteLength <= this.trackedStart) {\n return;\n }\n data = data.subarray(this.trackedStart - pos);\n pos = 0;\n }\n const neededSize = pos + data.byteLength - this.trackedStart;\n let newLength = this.trackedWrites.byteLength;\n while (newLength < neededSize) {\n newLength *= 2;\n }\n if (newLength !== this.trackedWrites.byteLength) {\n const copy = new Uint8Array(newLength);\n copy.set(this.trackedWrites, 0);\n this.trackedWrites = copy;\n }\n this.trackedWrites.set(data, pos - this.trackedStart);\n this.trackedEnd = Math.max(this.trackedEnd, pos + data.byteLength);\n }\n startTrackingWrites() {\n this.trackedWrites = new Uint8Array(2 ** 10);\n this.trackedStart = this.getPos();\n this.trackedEnd = this.trackedStart;\n }\n stopTrackingWrites() {\n if (!this.trackedWrites) {\n throw new Error(\"Internal error: Can't get tracked writes since nothing was tracked.\");\n }\n const slice = this.trackedWrites.subarray(0, this.trackedEnd - this.trackedStart);\n const result = {\n data: slice,\n start: this.trackedStart,\n end: this.trackedEnd\n };\n this.trackedWrites = null;\n return result;\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/target.js\n var nodeAlias = (() => ({}));\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n class Target extends EventEmitter {\n constructor() {\n super(...arguments);\n this._writerAcquired = false;\n this._monotonicity = null;\n this.onwrite = null;\n }\n _setMonotonicity(monotonicity) {\n if (this._monotonicity !== false) {\n this._monotonicity = monotonicity;\n }\n }\n _dispatchWrite(start, end) {\n this.onwrite?.(start, end);\n this._emit(\"write\", { start, end });\n }\n slice(offset) {\n if (!Number.isInteger(offset) || offset < 0) {\n throw new TypeError(\"offset must be a non-negative integer.\");\n }\n return new RangedTarget(this, offset);\n }\n }\n var ARRAY_BUFFER_INITIAL_SIZE = 2 ** 16;\n var ARRAY_BUFFER_MAX_SIZE = 2 ** 32;\n\n class BufferTarget extends Target {\n constructor(options = {}) {\n super();\n this.buffer = null;\n this._maxPos = 0;\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"BufferTarget options, when provided, must be an object.\");\n }\n if (options.onFinalize !== undefined && typeof options.onFinalize !== \"function\") {\n throw new TypeError(\"options.onFinalize, when provided, must be a function.\");\n }\n this._options = options;\n this._supportsResize = \"resize\" in new ArrayBuffer(0);\n if (this._supportsResize) {\n try {\n this._buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE, { maxByteLength: ARRAY_BUFFER_MAX_SIZE });\n } catch {\n this._buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE);\n this._supportsResize = false;\n }\n } else {\n this._buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE);\n }\n this._bytes = new Uint8Array(this._buffer);\n }\n _ensureSize(size) {\n let newLength = this._buffer.byteLength;\n while (newLength < size)\n newLength *= 2;\n if (newLength === this._buffer.byteLength)\n return;\n if (newLength > ARRAY_BUFFER_MAX_SIZE) {\n throw new Error(`ArrayBuffer exceeded maximum size of ${ARRAY_BUFFER_MAX_SIZE} bytes. Please consider using another` + ` target.`);\n }\n if (this._supportsResize) {\n this._buffer.resize(newLength);\n } else {\n const newBuffer = new ArrayBuffer(newLength);\n const newBytes = new Uint8Array(newBuffer);\n newBytes.set(this._bytes, 0);\n this._buffer = newBuffer;\n this._bytes = newBytes;\n }\n }\n _start() {}\n _write(data, pos) {\n this._ensureSize(pos + data.byteLength);\n this._bytes.set(data, pos);\n this._maxPos = Math.max(this._maxPos, pos + data.byteLength);\n this._dispatchWrite(pos, pos + data.byteLength);\n }\n async _flush() {}\n async _finalize() {\n this.buffer = this._buffer.slice(0, this._maxPos);\n if (this._options.onFinalize) {\n await this._options.onFinalize(this.buffer);\n }\n this._emit(\"finalized\");\n }\n async _close() {}\n _getSlice(start, end) {\n return this._bytes.slice(start, end);\n }\n }\n var DEFAULT_CHUNK_SIZE = 2 ** 24;\n var MAX_CHUNKS_AT_ONCE = 2;\n\n class StreamTarget extends Target {\n constructor(writable, options = {}) {\n super();\n this._sections = [];\n this._lastWriteEnd = 0;\n this._lastFlushEnd = 0;\n this._streamWriter = null;\n this._writeError = null;\n this._chunks = [];\n if (!(writable instanceof WritableStream)) {\n throw new TypeError(\"StreamTarget requires a WritableStream instance.\");\n }\n if (options != null && typeof options !== \"object\") {\n throw new TypeError(\"StreamTarget options, when provided, must be an object.\");\n }\n if (options.chunked !== undefined && typeof options.chunked !== \"boolean\") {\n throw new TypeError(\"options.chunked, when provided, must be a boolean.\");\n }\n if (options.chunkSize !== undefined && (!Number.isInteger(options.chunkSize) || options.chunkSize < 1024)) {\n throw new TypeError(\"options.chunkSize, when provided, must be an integer and not smaller than 1024.\");\n }\n this._writable = writable;\n this._options = options;\n this._chunked = options.chunked ?? false;\n this._chunkSize = options.chunkSize ?? DEFAULT_CHUNK_SIZE;\n }\n _start() {\n this._streamWriter = this._writable.getWriter();\n }\n _write(data, pos) {\n if (pos > this._lastWriteEnd) {\n const paddingBytesNeeded = pos - this._lastWriteEnd;\n this._write(new Uint8Array(paddingBytesNeeded), this._lastWriteEnd);\n }\n this._sections.push({\n data: data.slice(),\n start: pos\n });\n this._lastWriteEnd = Math.max(this._lastWriteEnd, pos + data.byteLength);\n this._dispatchWrite(pos, pos + data.byteLength);\n }\n async _flush() {\n if (this._writeError !== null) {\n throw this._writeError;\n }\n assert(this._streamWriter);\n if (this._sections.length === 0) {\n return;\n }\n const chunks = [];\n const sorted = [...this._sections].sort((a, b) => a.start - b.start);\n chunks.push({\n start: sorted[0].start,\n size: sorted[0].data.byteLength\n });\n for (let i = 1;i < sorted.length; i++) {\n const lastChunk = chunks[chunks.length - 1];\n const section = sorted[i];\n if (section.start <= lastChunk.start + lastChunk.size) {\n lastChunk.size = Math.max(lastChunk.size, section.start + section.data.byteLength - lastChunk.start);\n } else {\n chunks.push({\n start: section.start,\n size: section.data.byteLength\n });\n }\n }\n for (const chunk of chunks) {\n chunk.data = new Uint8Array(chunk.size);\n for (const section of this._sections) {\n if (chunk.start <= section.start && section.start < chunk.start + chunk.size) {\n chunk.data.set(section.data, section.start - chunk.start);\n }\n }\n if (this._streamWriter.desiredSize !== null && this._streamWriter.desiredSize <= 0) {\n await this._streamWriter.ready;\n }\n if (this._chunked) {\n this._writeDataIntoChunks(chunk.data, chunk.start);\n this._tryToFlushChunks();\n } else {\n if (this._monotonicity === true && chunk.start !== this._lastFlushEnd) {\n throw new Error(\"Internal error: Monotonicity violation.\");\n }\n this._streamWriter.write({\n type: \"write\",\n data: chunk.data,\n position: chunk.start\n }).catch((error) => {\n this._writeError ??= error;\n });\n this._lastFlushEnd = chunk.start + chunk.data.byteLength;\n }\n }\n this._sections.length = 0;\n }\n _writeDataIntoChunks(data, position) {\n let chunkIndex = this._chunks.findIndex((x) => x.start <= position && position < x.start + this._chunkSize);\n if (chunkIndex === -1)\n chunkIndex = this._createChunk(position);\n const chunk = this._chunks[chunkIndex];\n const relativePosition = position - chunk.start;\n const toWrite = data.subarray(0, Math.min(this._chunkSize - relativePosition, data.byteLength));\n chunk.data.set(toWrite, relativePosition);\n const section = {\n start: relativePosition,\n end: relativePosition + toWrite.byteLength\n };\n this._insertSectionIntoChunk(chunk, section);\n if (chunk.written[0].start === 0 && chunk.written[0].end === this._chunkSize) {\n chunk.shouldFlush = true;\n }\n if (this._chunks.length > MAX_CHUNKS_AT_ONCE) {\n for (let i = 0;i < this._chunks.length - 1; i++) {\n this._chunks[i].shouldFlush = true;\n }\n this._tryToFlushChunks();\n }\n if (toWrite.byteLength < data.byteLength) {\n this._writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength);\n }\n }\n _insertSectionIntoChunk(chunk, section) {\n let low = 0;\n let high = chunk.written.length - 1;\n let index = -1;\n while (low <= high) {\n const mid = Math.floor(low + (high - low + 1) / 2);\n if (chunk.written[mid].start <= section.start) {\n low = mid + 1;\n index = mid;\n } else {\n high = mid - 1;\n }\n }\n chunk.written.splice(index + 1, 0, section);\n if (index === -1 || chunk.written[index].end < section.start)\n index++;\n while (index < chunk.written.length - 1 && chunk.written[index].end >= chunk.written[index + 1].start) {\n chunk.written[index].end = Math.max(chunk.written[index].end, chunk.written[index + 1].end);\n chunk.written.splice(index + 1, 1);\n }\n }\n _createChunk(includesPosition) {\n const start = Math.floor(includesPosition / this._chunkSize) * this._chunkSize;\n const chunk = {\n start,\n data: new Uint8Array(this._chunkSize),\n written: [],\n shouldFlush: false\n };\n this._chunks.push(chunk);\n this._chunks.sort((a, b) => a.start - b.start);\n return this._chunks.indexOf(chunk);\n }\n _tryToFlushChunks(force = false) {\n assert(this._streamWriter);\n for (let i = 0;i < this._chunks.length; i++) {\n const chunk = this._chunks[i];\n if (!chunk.shouldFlush && !force)\n continue;\n for (const section of chunk.written) {\n const position = chunk.start + section.start;\n if (this._monotonicity === true && position !== this._lastFlushEnd) {\n throw new Error(\"Internal error: Monotonicity violation.\");\n }\n const isPartialView = section.start !== 0 || section.end !== chunk.data.byteLength;\n let data;\n if (isPartialView && isWebKit()) {\n data = chunk.data.slice(section.start, section.end);\n } else {\n data = chunk.data.subarray(section.start, section.end);\n }\n this._streamWriter.write({\n type: \"write\",\n data,\n position\n }).catch((error) => {\n this._writeError ??= error;\n });\n this._lastFlushEnd = chunk.start + section.end;\n }\n this._chunks.splice(i--, 1);\n }\n }\n async _finalize() {\n if (this._chunked) {\n this._tryToFlushChunks(true);\n }\n if (this._writeError !== null) {\n throw this._writeError;\n }\n assert(this._streamWriter);\n await this._streamWriter.ready;\n await this._streamWriter.close();\n this._emit(\"finalized\");\n }\n async _close() {\n return this._streamWriter?.close();\n }\n }\n class RangedTarget extends Target {\n constructor(baseTarget, offset) {\n super();\n this._baseTarget = baseTarget;\n this._offset = offset;\n }\n _start() {}\n _write(data, pos) {\n this._baseTarget._write(data, this._offset + pos);\n this._dispatchWrite(pos, pos + data.byteLength);\n }\n _flush() {\n return this._baseTarget._flush();\n }\n async _finalize() {\n this._emit(\"finalized\");\n }\n async _close() {}\n _setMonotonicity(monotonicity) {\n super._setMonotonicity(monotonicity);\n this._baseTarget._setMonotonicity(monotonicity);\n }\n }\n\n class PathedTarget {\n constructor(rootPath, getTarget) {\n this.rootPath = rootPath;\n this.getTarget = getTarget;\n if (typeof rootPath !== \"string\") {\n throw new TypeError(\"rootPath must be a string.\");\n }\n if (typeof getTarget !== \"function\") {\n throw new TypeError(\"getTarget must be a function.\");\n }\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-muxer.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var GLOBAL_TIMESCALE = 57600;\n var TIMESTAMP_OFFSET = 2082844800;\n var getTrackMetadata = (trackData) => {\n const metadata = {};\n const track = trackData.track;\n if (track.metadata.name !== undefined) {\n metadata.name = track.metadata.name;\n }\n return metadata;\n };\n var intoTimescale = (timeInSeconds, timescale, round = true) => {\n const value = timeInSeconds * timescale;\n return round ? Math.round(value) : value;\n };\n var presentationSpan = (trackData) => {\n if (trackData.samples.length === 0) {\n return 0;\n }\n let minTimestamp = Infinity;\n let maxEndTimestamp = -Infinity;\n for (let i = 0;i < trackData.samples.length; i++) {\n const sample = trackData.samples[i];\n if (sample.timestamp < minTimestamp) {\n minTimestamp = sample.timestamp;\n }\n if (sample.timestamp + sample.duration > maxEndTimestamp) {\n maxEndTimestamp = sample.timestamp + sample.duration;\n }\n }\n if (minTimestamp === Infinity) {\n return 0;\n }\n return maxEndTimestamp - minTimestamp;\n };\n\n class IsobmffMuxer extends Muxer {\n constructor(output, format) {\n super(output);\n this.writer = null;\n this.boxWriter = null;\n this.initWriter = null;\n this.initBoxWriter = null;\n this.auxTarget = new BufferTarget;\n this.auxWriter = new Writer(this.auxTarget, false);\n this.auxBoxWriter = new IsobmffBoxWriter(this.auxWriter);\n this.mdat = null;\n this.ftypSize = null;\n this.trackDatas = [];\n this.allTracksKnown = promiseWithResolvers();\n this.creationTime = Math.floor(Date.now() / 1000) + TIMESTAMP_OFFSET;\n this.finalizedChunks = [];\n this.wroteFragmentedHeader = false;\n this.nextFragmentNumber = 1;\n this.maxWrittenTimestamp = -Infinity;\n this.minWrittenTimestamp = Infinity;\n this.maxWrittenEndTimestamp = -Infinity;\n this.segmentHeaderSize = null;\n this.format = format;\n this.formatOptions = { ...format._options };\n this.isQuickTime = format instanceof MovOutputFormat;\n this.isCmaf = format instanceof CmafOutputFormat;\n this.minimumFragmentDuration = this.formatOptions.minimumFragmentDuration ?? (format instanceof CmafOutputFormat ? Infinity : 1);\n this.auxWriter.start();\n }\n async start() {\n const release = await this.mutex.acquire();\n if (!this.isCmaf) {\n this.writer = await this.output._getRootWriter((target) => this.formatOptions.fastStart !== undefined ? this.formatOptions.fastStart === \"fragmented\" : target instanceof BufferTarget);\n this.boxWriter = new IsobmffBoxWriter(this.writer);\n this.fastStart = this.formatOptions.fastStart ?? (this.writer.target instanceof BufferTarget ? \"in-memory\" : false);\n this.isFragmented = this.fastStart === \"fragmented\";\n } else {\n this.fastStart = \"fragmented\";\n this.isFragmented = true;\n }\n if (this.isCmaf) {\n if (!this.output._hasInitTarget()) {\n throw new Error(`CMAF outputs require the initTarget field in OutputOptions to be set; the init segment` + ` will be written to it.`);\n }\n const initTarget = await this.output._getInitTarget();\n const initWriter = new Writer(initTarget, true);\n initWriter.start();\n this.initWriter = initWriter;\n this.initBoxWriter = new IsobmffBoxWriter(initWriter);\n }\n const holdsAvc = this.output.tracks.some((x) => x.isVideoTrack() && x.source._codec === \"avc\");\n {\n const boxWriter = this.initBoxWriter ?? this.boxWriter;\n assert(boxWriter);\n if (this.formatOptions.onFtyp) {\n boxWriter.writer.startTrackingWrites();\n }\n boxWriter.writeBox(ftyp({\n isQuickTime: this.isQuickTime,\n holdsAvc,\n fragmented: this.isFragmented,\n cmaf: this.isCmaf\n }));\n if (this.formatOptions.onFtyp) {\n const { data, start } = boxWriter.writer.stopTrackingWrites();\n this.formatOptions.onFtyp(data, start);\n }\n this.ftypSize = boxWriter.writer.getPos();\n if (this.isCmaf) {\n await this.initWriter.flush();\n }\n }\n if (this.fastStart === \"in-memory\") {} else if (this.fastStart === \"reserve\") {\n for (const track of this.output.tracks) {\n if (track.metadata.maximumPacketCount === undefined) {\n throw new Error(\"All tracks must specify maximumPacketCount in their metadata when using\" + \" fastStart: 'reserve'.\");\n }\n }\n } else if (this.isFragmented) {} else {\n assert(this.writer);\n assert(this.boxWriter);\n if (this.formatOptions.onMdat) {\n this.writer.startTrackingWrites();\n }\n this.mdat = mdat(true);\n this.boxWriter.writeBox(this.mdat);\n }\n await this.writer?.flush();\n for (const track of this.output.tracks) {\n if (track.isVideoTrack() && track.metadata.decoderConfig) {\n this.getVideoTrackData(track, track.metadata.primingPacket ?? null, { decoderConfig: track.metadata.decoderConfig });\n } else if (track.isAudioTrack() && track.metadata.decoderConfig) {\n this.getAudioTrackData(track, track.metadata.primingPacket ?? null, { decoderConfig: track.metadata.decoderConfig });\n }\n }\n release();\n }\n allTracksAreKnown() {\n for (const track of this.output.tracks) {\n if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) {\n return false;\n }\n }\n return true;\n }\n async getMimeType() {\n await this.allTracksKnown.promise;\n const codecStrings = this.trackDatas.map((trackData) => {\n if (trackData.type === \"video\") {\n return trackData.info.decoderConfig.codec;\n } else if (trackData.type === \"audio\") {\n return trackData.info.decoderConfig.codec;\n } else {\n const map = {\n webvtt: \"wvtt\"\n };\n return map[trackData.track.source._codec];\n }\n });\n return buildIsobmffMimeType({\n isQuickTime: this.isQuickTime,\n hasVideo: this.trackDatas.some((x) => x.type === \"video\"),\n hasAudio: this.trackDatas.some((x) => x.type === \"audio\"),\n codecStrings\n });\n }\n getVideoTrackData(track, packet, meta) {\n const existingTrackData = this.trackDatas.find((x) => x.track === track);\n if (existingTrackData) {\n return existingTrackData;\n }\n validateVideoChunkMetadata(meta, track.source._codec);\n assert(meta);\n assert(meta.decoderConfig);\n const decoderConfig = { ...meta.decoderConfig };\n assert(decoderConfig.codedWidth !== undefined);\n assert(decoderConfig.codedHeight !== undefined);\n let requiresAnnexBTransformation = false;\n if (track.source._codec === \"avc\" && !decoderConfig.description) {\n if (!packet) {\n throw new Error(\"No AVC description provided; you must therefore provide a priming packet.\");\n }\n const decoderConfigurationRecord = extractAvcDecoderConfigurationRecord(packet.data);\n if (!decoderConfigurationRecord) {\n throw new Error(\"Couldn't extract an AVCDecoderConfigurationRecord from the AVC packet. Make sure the packets are\" + \" in Annex B format (as specified in ITU-T-REC-H.264) when not providing a description, or\" + \" provide a description (must be an AVCDecoderConfigurationRecord as specified in ISO 14496-15)\" + \" and ensure the packets are in AVCC format.\");\n }\n decoderConfig.description = serializeAvcDecoderConfigurationRecord(decoderConfigurationRecord);\n requiresAnnexBTransformation = true;\n } else if (track.source._codec === \"hevc\" && !decoderConfig.description) {\n if (!packet) {\n throw new Error(\"No HEVC description provided; you must therefore provide a priming packet.\");\n }\n const decoderConfigurationRecord = extractHevcDecoderConfigurationRecord(packet.data);\n if (!decoderConfigurationRecord) {\n throw new Error(\"Couldn't extract an HEVCDecoderConfigurationRecord from the HEVC packet. Make sure the packets\" + \" are in Annex B format (as specified in ITU-T-REC-H.265) when not providing a description, or\" + \" provide a description (must be an HEVCDecoderConfigurationRecord as specified in ISO 14496-15)\" + \" and ensure the packets are in HEVC format.\");\n }\n decoderConfig.description = serializeHevcDecoderConfigurationRecord(decoderConfigurationRecord);\n requiresAnnexBTransformation = true;\n }\n const timescale = computeRationalApproximation(1 / (track.metadata.frameRate ?? GLOBAL_TIMESCALE), 1e6).den;\n const displayAspectWidth = decoderConfig.displayAspectWidth;\n const displayAspectHeight = decoderConfig.displayAspectHeight;\n const pixelAspectRatio = displayAspectWidth === undefined || displayAspectHeight === undefined ? { num: 1, den: 1 } : simplifyRational({\n num: displayAspectWidth * decoderConfig.codedHeight,\n den: displayAspectHeight * decoderConfig.codedWidth\n });\n const hasAlphaChannel = decoderConfig.codec === \"ap4h\" || decoderConfig.codec === \"ap4x\";\n const newTrackData = {\n muxer: this,\n track,\n type: \"video\",\n info: {\n width: decoderConfig.codedWidth,\n height: decoderConfig.codedHeight,\n pixelAspectRatio,\n decoderConfig,\n requiresAnnexBTransformation,\n hasAlphaChannel\n },\n timescale,\n samples: [],\n sampleQueue: [],\n timestampProcessingQueue: [],\n timeToSampleTable: [],\n compositionTimeOffsetTable: [],\n lastTimescaleUnits: null,\n lastSample: null,\n startTimestampOffset: null,\n finalizedChunks: [],\n currentChunk: null,\n compactlyCodedChunkTable: [],\n closed: false,\n avgBitrate: track.source._nominalBitrate ?? track.metadata.averageBitrate ?? 0,\n maxBitrate: track.source._nominalBitrate ?? track.metadata.bitrate ?? 0\n };\n this.trackDatas.push(newTrackData);\n this.trackDatas.sort((a, b) => a.track.id - b.track.id);\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n return newTrackData;\n }\n getAudioTrackData(track, packet, meta) {\n const existingTrackData = this.trackDatas.find((x) => x.track === track);\n if (existingTrackData) {\n return existingTrackData;\n }\n validateAudioChunkMetadata(meta, track.source._codec);\n assert(meta);\n assert(meta.decoderConfig);\n const decoderConfig = { ...meta.decoderConfig };\n let requiresAdtsStripping = false;\n if (track.source._codec === \"aac\" && !decoderConfig.description) {\n if (!packet) {\n throw new Error(\"No AAC description provided; you must therefore provide a priming packet.\");\n }\n const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packet.data));\n if (!adtsFrame) {\n throw new Error(\"Couldn't parse ADTS header from the AAC packet. Make sure the packets are in ADTS format\" + \" (as specified in ISO 13818-7) when not providing a description, or provide a description\" + \" (must be an AudioSpecificConfig as specified in ISO 14496-3) and ensure the packets\" + \" are raw AAC data.\");\n }\n const sampleRate = aacFrequencyTable[adtsFrame.samplingFrequencyIndex];\n const numberOfChannels = aacChannelMap[adtsFrame.channelConfiguration];\n if (sampleRate === undefined || numberOfChannels === undefined) {\n throw new Error(\"Invalid ADTS frame header.\");\n }\n decoderConfig.description = buildAacAudioSpecificConfig({\n objectType: adtsFrame.objectType,\n outputSampleRate: sampleRate,\n outputNumberOfChannels: numberOfChannels\n });\n requiresAdtsStripping = true;\n }\n if (!packet) {\n if (track.source._codec === \"ac3\" || track.source._codec === \"eac3\") {\n throw new Error(\"AC-3/E-AC-3 require a priming packet.\");\n }\n if (track.source._codec === \"dts\") {\n throw new Error(\"DTS requires a priming packet.\");\n }\n }\n const newTrackData = {\n muxer: this,\n track,\n type: \"audio\",\n info: {\n numberOfChannels: meta.decoderConfig.numberOfChannels,\n sampleRate: meta.decoderConfig.sampleRate,\n decoderConfig,\n requiresPcmTransformation: !this.isFragmented && PCM_AUDIO_CODECS.includes(track.source._codec),\n expectedNextPcmPacketTimestamp: null,\n requiresAdtsStripping,\n primingPacket: packet\n },\n timescale: decoderConfig.sampleRate,\n samples: [],\n sampleQueue: [],\n timestampProcessingQueue: [],\n timeToSampleTable: [],\n compositionTimeOffsetTable: [],\n lastTimescaleUnits: null,\n lastSample: null,\n startTimestampOffset: null,\n finalizedChunks: [],\n currentChunk: null,\n compactlyCodedChunkTable: [],\n closed: false,\n avgBitrate: track.source._nominalBitrate ?? track.metadata.averageBitrate ?? 0,\n maxBitrate: track.source._nominalBitrate ?? track.metadata.bitrate ?? 0\n };\n this.trackDatas.push(newTrackData);\n this.trackDatas.sort((a, b) => a.track.id - b.track.id);\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n return newTrackData;\n }\n getSubtitleTrackData(track, meta) {\n const existingTrackData = this.trackDatas.find((x) => x.track === track);\n if (existingTrackData) {\n return existingTrackData;\n }\n validateSubtitleMetadata(meta);\n assert(meta);\n assert(meta.config);\n const newTrackData = {\n muxer: this,\n track,\n type: \"subtitle\",\n info: {\n config: meta.config\n },\n timescale: 1000,\n samples: [],\n sampleQueue: [],\n timestampProcessingQueue: [],\n timeToSampleTable: [],\n compositionTimeOffsetTable: [],\n lastTimescaleUnits: null,\n lastSample: null,\n startTimestampOffset: null,\n finalizedChunks: [],\n currentChunk: null,\n compactlyCodedChunkTable: [],\n closed: false,\n avgBitrate: track.source._nominalBitrate ?? track.metadata.averageBitrate ?? 0,\n maxBitrate: track.source._nominalBitrate ?? track.metadata.bitrate ?? 0,\n lastCueEndTimestamp: null,\n cueQueue: [],\n nextSourceId: 0,\n cueToSourceId: new WeakMap\n };\n this.trackDatas.push(newTrackData);\n this.trackDatas.sort((a, b) => a.track.id - b.track.id);\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n return newTrackData;\n }\n async addEncodedVideoPacket(track, packet, meta) {\n const release = await this.mutex.acquire();\n try {\n const trackData = this.getVideoTrackData(track, packet, meta);\n let packetData = packet.data;\n if (trackData.info.requiresAnnexBTransformation) {\n const nalUnits = [...iterateNalUnitsInAnnexB(packetData)].map((loc) => packetData.subarray(loc.offset, loc.offset + loc.length));\n if (nalUnits.length === 0) {\n throw new Error(\"Failed to transform packet data. Make sure all packets are provided in Annex B format, as\" + \" specified in ITU-T-REC-H.264 and ITU-T-REC-H.265.\");\n }\n packetData = concatNalUnitsInLengthPrefixed(nalUnits, 4);\n }\n this.validateTimestamp(trackData.track, packet.timestamp, packet.type === \"key\");\n const internalSample = this.createSampleForTrack(trackData, packetData, packet.timestamp, packet.duration, packet.type);\n await this.registerSample(trackData, internalSample);\n } finally {\n release();\n }\n }\n async addEncodedAudioPacket(track, packet, meta) {\n const release = await this.mutex.acquire();\n try {\n const trackData = this.getAudioTrackData(track, packet, meta);\n let packetData = packet.data;\n if (trackData.info.requiresAdtsStripping) {\n const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packetData));\n if (!adtsFrame) {\n throw new Error(\"Expected ADTS frame, didn't get one.\");\n }\n const headerLength = adtsFrame.crcCheck === null ? MIN_ADTS_FRAME_HEADER_SIZE : MAX_ADTS_FRAME_HEADER_SIZE;\n packetData = packetData.subarray(headerLength);\n }\n this.validateTimestamp(trackData.track, packet.timestamp, packet.type === \"key\");\n let timestamp = packet.timestamp;\n let duration = packet.duration;\n if (trackData.info.requiresPcmTransformation) {\n const pcmInfo = parsePcmCodec(trackData.info.decoderConfig.codec);\n const frameSize = pcmInfo.sampleSize * trackData.info.numberOfChannels;\n duration = packetData.byteLength / frameSize / trackData.info.sampleRate;\n if (trackData.info.expectedNextPcmPacketTimestamp !== null) {\n const diff = timestamp - trackData.info.expectedNextPcmPacketTimestamp;\n if (diff < 0.01) {\n timestamp = trackData.info.expectedNextPcmPacketTimestamp;\n } else {\n const paddedDuration = await this.padWithSilence(trackData, trackData.info.expectedNextPcmPacketTimestamp, diff);\n timestamp = trackData.info.expectedNextPcmPacketTimestamp + paddedDuration;\n }\n }\n trackData.info.expectedNextPcmPacketTimestamp = timestamp + duration;\n }\n const internalSample = this.createSampleForTrack(trackData, packetData, timestamp, duration, packet.type);\n await this.registerSample(trackData, internalSample);\n } finally {\n release();\n }\n }\n async padWithSilence(trackData, timestamp, duration) {\n const deltaInTimescale = intoTimescale(duration, trackData.timescale);\n duration = deltaInTimescale / trackData.timescale;\n if (deltaInTimescale > 0) {\n const { sampleSize, silentValue } = parsePcmCodec(trackData.info.decoderConfig.codec);\n const samplesNeeded = deltaInTimescale * trackData.info.numberOfChannels;\n const data = new Uint8Array(sampleSize * samplesNeeded).fill(silentValue);\n const paddingSample = this.createSampleForTrack(trackData, new Uint8Array(data.buffer), timestamp, duration, \"key\");\n await this.registerSample(trackData, paddingSample);\n }\n return duration;\n }\n async addSubtitleCue(track, cue, meta) {\n const release = await this.mutex.acquire();\n try {\n const trackData = this.getSubtitleTrackData(track, meta);\n this.validateTimestamp(trackData.track, cue.timestamp, true);\n if (track.source._codec === \"webvtt\") {\n trackData.cueQueue.push(cue);\n await this.processWebVTTCues(trackData, cue.timestamp);\n }\n } finally {\n release();\n }\n }\n async processWebVTTCues(trackData, until) {\n while (trackData.cueQueue.length > 0) {\n trackData.lastCueEndTimestamp ??= Math.min(0, trackData.cueQueue[0].timestamp);\n const timestamps = new Set([]);\n for (const cue of trackData.cueQueue) {\n assert(cue.timestamp <= until);\n assert(trackData.lastCueEndTimestamp <= cue.timestamp + cue.duration);\n timestamps.add(Math.max(cue.timestamp, trackData.lastCueEndTimestamp));\n timestamps.add(cue.timestamp + cue.duration);\n }\n const sortedTimestamps = [...timestamps].sort((a, b) => a - b);\n const sampleStart = sortedTimestamps[0];\n const sampleEnd = sortedTimestamps[1] ?? sampleStart;\n if (until < sampleEnd) {\n break;\n }\n if (trackData.lastCueEndTimestamp < sampleStart) {\n this.auxWriter.seek(0);\n const box = vtte();\n this.auxBoxWriter.writeBox(box);\n const body = this.auxTarget._getSlice(0, this.auxWriter.getPos());\n const sample = this.createSampleForTrack(trackData, body, trackData.lastCueEndTimestamp, sampleStart - trackData.lastCueEndTimestamp, \"key\");\n await this.registerSample(trackData, sample);\n trackData.lastCueEndTimestamp = sampleStart;\n }\n this.auxWriter.seek(0);\n for (let i = 0;i < trackData.cueQueue.length; i++) {\n const cue = trackData.cueQueue[i];\n if (cue.timestamp >= sampleEnd) {\n break;\n }\n inlineTimestampRegex.lastIndex = 0;\n const containsTimestamp = inlineTimestampRegex.test(cue.text);\n const endTimestamp = cue.timestamp + cue.duration;\n let sourceId = trackData.cueToSourceId.get(cue);\n if (sourceId === undefined && sampleEnd < endTimestamp) {\n sourceId = trackData.nextSourceId++;\n trackData.cueToSourceId.set(cue, sourceId);\n }\n if (cue.notes) {\n const box = vtta(cue.notes);\n this.auxBoxWriter.writeBox(box);\n }\n const box = vttc(cue.text, containsTimestamp ? sampleStart : null, cue.identifier ?? null, cue.settings ?? null, sourceId ?? null);\n this.auxBoxWriter.writeBox(box);\n if (endTimestamp === sampleEnd) {\n trackData.cueQueue.splice(i--, 1);\n }\n }\n const body = this.auxTarget._getSlice(0, this.auxWriter.getPos());\n const sample = this.createSampleForTrack(trackData, body, sampleStart, sampleEnd - sampleStart, \"key\");\n await this.registerSample(trackData, sample);\n trackData.lastCueEndTimestamp = sampleEnd;\n }\n }\n createSampleForTrack(trackData, data, timestamp, duration, type) {\n const sample = {\n timestamp,\n decodeTimestamp: timestamp,\n duration,\n data,\n size: data.byteLength,\n type,\n timescaleUnitsToNextSample: intoTimescale(duration, trackData.timescale)\n };\n return sample;\n }\n processTimestamps(trackData, nextSample) {\n if (trackData.timestampProcessingQueue.length === 0) {\n return;\n }\n if (trackData.type === \"audio\" && trackData.info.requiresPcmTransformation) {\n assert(!this.isFragmented);\n trackData.startTimestampOffset ??= trackData.timestampProcessingQueue[0].timestamp;\n let totalDuration = 0;\n for (let i = 0;i < trackData.timestampProcessingQueue.length; i++) {\n const sample = trackData.timestampProcessingQueue[i];\n const duration = intoTimescale(sample.duration, trackData.timescale);\n totalDuration += duration;\n }\n if (trackData.timeToSampleTable.length === 0) {\n trackData.timeToSampleTable.push({\n sampleCount: totalDuration,\n sampleDelta: 1\n });\n } else {\n const lastEntry = last(trackData.timeToSampleTable);\n lastEntry.sampleCount += totalDuration;\n }\n trackData.timestampProcessingQueue.length = 0;\n return;\n }\n const sortedTimestamps = trackData.timestampProcessingQueue.map((x) => x.timestamp).sort((a, b) => a - b);\n if (this.isFragmented) {\n trackData.startTimestampOffset ??= Math.min(sortedTimestamps[0], 0);\n } else {\n trackData.startTimestampOffset ??= sortedTimestamps[0];\n }\n for (let i = 0;i < trackData.timestampProcessingQueue.length; i++) {\n const sample = trackData.timestampProcessingQueue[i];\n sample.decodeTimestamp = sortedTimestamps[i];\n const sampleCompositionTimeOffset = intoTimescale(sample.timestamp - sample.decodeTimestamp, trackData.timescale);\n const durationInTimescale = intoTimescale(sample.duration, trackData.timescale);\n if (trackData.lastTimescaleUnits !== null) {\n assert(trackData.lastSample);\n const timescaleUnits = intoTimescale(sample.decodeTimestamp, trackData.timescale, false);\n const delta = Math.round(timescaleUnits - trackData.lastTimescaleUnits);\n assert(delta >= 0);\n trackData.lastTimescaleUnits += delta;\n trackData.lastSample.timescaleUnitsToNextSample = delta;\n if (!this.isFragmented) {\n let lastTableEntry = last(trackData.timeToSampleTable);\n assert(lastTableEntry);\n if (lastTableEntry.sampleCount === 1) {\n lastTableEntry.sampleDelta = delta;\n const entryBefore = trackData.timeToSampleTable[trackData.timeToSampleTable.length - 2];\n if (entryBefore && entryBefore.sampleDelta === delta) {\n entryBefore.sampleCount++;\n trackData.timeToSampleTable.pop();\n lastTableEntry = entryBefore;\n }\n } else if (lastTableEntry.sampleDelta !== delta) {\n lastTableEntry.sampleCount--;\n trackData.timeToSampleTable.push(lastTableEntry = {\n sampleCount: 1,\n sampleDelta: delta\n });\n }\n if (lastTableEntry.sampleDelta === durationInTimescale) {\n lastTableEntry.sampleCount++;\n } else {\n trackData.timeToSampleTable.push({\n sampleCount: 1,\n sampleDelta: durationInTimescale\n });\n }\n const lastCompositionTimeOffsetTableEntry = last(trackData.compositionTimeOffsetTable);\n assert(lastCompositionTimeOffsetTableEntry);\n if (lastCompositionTimeOffsetTableEntry.sampleCompositionTimeOffset === sampleCompositionTimeOffset) {\n lastCompositionTimeOffsetTableEntry.sampleCount++;\n } else {\n trackData.compositionTimeOffsetTable.push({\n sampleCount: 1,\n sampleCompositionTimeOffset\n });\n }\n }\n } else {\n trackData.lastTimescaleUnits = intoTimescale(sample.decodeTimestamp, trackData.timescale, false);\n if (!this.isFragmented) {\n trackData.timeToSampleTable.push({\n sampleCount: 1,\n sampleDelta: durationInTimescale\n });\n trackData.compositionTimeOffsetTable.push({\n sampleCount: 1,\n sampleCompositionTimeOffset\n });\n }\n }\n trackData.lastSample = sample;\n }\n trackData.timestampProcessingQueue.length = 0;\n assert(trackData.lastSample);\n assert(trackData.lastTimescaleUnits !== null);\n if (nextSample !== undefined && trackData.lastSample.timescaleUnitsToNextSample === 0) {\n assert(nextSample.type === \"key\");\n const timescaleUnits = intoTimescale(nextSample.timestamp, trackData.timescale, false);\n const delta = Math.round(timescaleUnits - trackData.lastTimescaleUnits);\n trackData.lastSample.timescaleUnitsToNextSample = delta;\n }\n }\n async registerSample(trackData, sample) {\n if (sample.type === \"key\") {\n this.processTimestamps(trackData, sample);\n }\n trackData.timestampProcessingQueue.push(sample);\n if (this.isFragmented) {\n trackData.sampleQueue.push(sample);\n await this.interleaveSamples();\n } else if (this.fastStart === \"reserve\") {\n await this.registerSampleFastStartReserve(trackData, sample);\n } else {\n await this.addSampleToTrack(trackData, sample);\n }\n }\n async addSampleToTrack(trackData, sample) {\n if (!this.isFragmented) {\n trackData.samples.push(sample);\n if (this.fastStart === \"reserve\") {\n const maximumPacketCount = trackData.track.metadata.maximumPacketCount;\n assert(maximumPacketCount !== undefined);\n if (trackData.samples.length > maximumPacketCount) {\n throw new Error(`Track #${trackData.track.id} has already reached the maximum packet count` + ` (${maximumPacketCount}). Either add less packets or increase the maximum packet count.`);\n }\n }\n }\n let beginNewChunk = false;\n if (!trackData.currentChunk) {\n beginNewChunk = true;\n } else {\n trackData.currentChunk.startTimestamp = Math.min(trackData.currentChunk.startTimestamp, sample.timestamp);\n const currentChunkDuration = sample.timestamp - trackData.currentChunk.startTimestamp;\n if (this.isFragmented) {\n const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => {\n if (trackData === otherTrackData) {\n return sample.type === \"key\";\n }\n const firstQueuedSample = otherTrackData.sampleQueue[0];\n if (firstQueuedSample) {\n return firstQueuedSample.type === \"key\";\n }\n return otherTrackData.closed;\n });\n if (currentChunkDuration >= this.minimumFragmentDuration && keyFrameQueuedEverywhere && sample.timestamp > this.maxWrittenTimestamp) {\n beginNewChunk = true;\n await this.finalizeFragment();\n }\n } else {\n beginNewChunk = currentChunkDuration >= 0.5;\n }\n }\n if (beginNewChunk) {\n if (trackData.currentChunk) {\n await this.finalizeCurrentChunk(trackData);\n }\n trackData.currentChunk = {\n startTimestamp: sample.timestamp,\n samples: [],\n offset: null,\n moofOffset: null,\n trafIndex: null\n };\n }\n assert(trackData.currentChunk);\n trackData.currentChunk.samples.push(sample);\n if (this.isFragmented) {\n this.maxWrittenTimestamp = Math.max(this.maxWrittenTimestamp, sample.timestamp);\n this.maxWrittenEndTimestamp = Math.max(this.maxWrittenEndTimestamp, sample.timestamp + sample.duration);\n this.minWrittenTimestamp = Math.min(this.minWrittenTimestamp, sample.timestamp);\n }\n }\n async finalizeCurrentChunk(trackData) {\n assert(!this.isFragmented);\n assert(this.writer);\n if (!trackData.currentChunk)\n return;\n trackData.finalizedChunks.push(trackData.currentChunk);\n this.finalizedChunks.push(trackData.currentChunk);\n let sampleCount = trackData.currentChunk.samples.length;\n if (trackData.type === \"audio\" && trackData.info.requiresPcmTransformation) {\n sampleCount = trackData.currentChunk.samples.reduce((acc, sample) => acc + intoTimescale(sample.duration, trackData.timescale), 0);\n }\n if (trackData.compactlyCodedChunkTable.length === 0 || last(trackData.compactlyCodedChunkTable).samplesPerChunk !== sampleCount) {\n trackData.compactlyCodedChunkTable.push({\n firstChunk: trackData.finalizedChunks.length,\n samplesPerChunk: sampleCount\n });\n }\n if (this.fastStart === \"in-memory\") {\n trackData.currentChunk.offset = 0;\n return;\n }\n trackData.currentChunk.offset = this.writer.getPos();\n for (const sample of trackData.currentChunk.samples) {\n assert(sample.data);\n this.writer.write(sample.data);\n sample.data = null;\n }\n await this.writer.flush();\n }\n async interleaveSamples(isFinalCall = false) {\n assert(this.isFragmented);\n if (!isFinalCall && !this.allTracksAreKnown()) {\n return;\n }\n outer:\n while (true) {\n let trackWithMinTimestamp = null;\n let minTimestamp = Infinity;\n for (const trackData of this.trackDatas) {\n if (!isFinalCall && trackData.sampleQueue.length === 0 && !trackData.closed) {\n break outer;\n }\n if (trackData.sampleQueue.length > 0 && trackData.sampleQueue[0].timestamp < minTimestamp) {\n trackWithMinTimestamp = trackData;\n minTimestamp = trackData.sampleQueue[0].timestamp;\n }\n }\n if (!trackWithMinTimestamp) {\n break;\n }\n const sample = trackWithMinTimestamp.sampleQueue.shift();\n await this.addSampleToTrack(trackWithMinTimestamp, sample);\n }\n }\n async finalizeFragment(flushWriter = !this.isCmaf) {\n assert(this.isFragmented);\n if (!this.wroteFragmentedHeader) {\n this.wroteFragmentedHeader = true;\n const boxWriter = this.initBoxWriter ?? this.boxWriter;\n assert(boxWriter);\n if (this.formatOptions.onMoov) {\n boxWriter.writer.startTrackingWrites();\n }\n this.ensureOneEnabledTrack();\n const movieBox = moov(this);\n boxWriter.writeBox(movieBox);\n if (this.formatOptions.onMoov) {\n const { data, start } = boxWriter.writer.stopTrackingWrites();\n this.formatOptions.onMoov(data, start);\n }\n if (this.isCmaf) {\n assert(this.initWriter);\n await this.initWriter.flush();\n await this.initWriter.finalize();\n this.writer = await this.output._getRootWriter(true);\n this.boxWriter = new IsobmffBoxWriter(this.writer);\n const stypSize = this.boxWriter.measureBox(styp());\n const sidxSize = this.boxWriter.measureBox(sidx(this, 0));\n this.segmentHeaderSize = stypSize + sidxSize;\n this.writer.seek(this.segmentHeaderSize);\n }\n }\n assert(this.writer);\n assert(this.boxWriter);\n const tracksInFragment = this.trackDatas.filter((x) => x.currentChunk);\n if (tracksInFragment.length === 0) {\n if (flushWriter) {\n await this.writer.flush();\n }\n return;\n }\n const fragmentNumber = this.nextFragmentNumber++;\n const moofBox = moof(fragmentNumber, tracksInFragment);\n const moofOffset = this.writer.getPos();\n const mdatStartPos = moofOffset + this.boxWriter.measureBox(moofBox);\n let currentPos = mdatStartPos + MIN_BOX_HEADER_SIZE;\n let fragmentStartTimestamp = Infinity;\n for (let i = 0;i < tracksInFragment.length; i++) {\n const trackData = tracksInFragment[i];\n assert(trackData.currentChunk);\n assert(trackData.startTimestampOffset !== null);\n trackData.currentChunk.offset = currentPos;\n trackData.currentChunk.moofOffset = moofOffset;\n trackData.currentChunk.trafIndex = i;\n trackData.currentChunk.startTimestamp -= trackData.startTimestampOffset;\n for (const sample of trackData.currentChunk.samples) {\n currentPos += sample.size;\n sample.timestamp -= trackData.startTimestampOffset;\n sample.decodeTimestamp -= trackData.startTimestampOffset;\n }\n fragmentStartTimestamp = Math.min(fragmentStartTimestamp, trackData.currentChunk.startTimestamp);\n }\n const mdatSize = currentPos - mdatStartPos;\n const needsLargeMdatSize = mdatSize >= 2 ** 32;\n if (needsLargeMdatSize) {\n for (const trackData of tracksInFragment) {\n trackData.currentChunk.offset += MAX_BOX_HEADER_SIZE - MIN_BOX_HEADER_SIZE;\n }\n }\n if (this.formatOptions.onMoof) {\n this.writer.startTrackingWrites();\n }\n const newMoofBox = moof(fragmentNumber, tracksInFragment);\n this.boxWriter.writeBox(newMoofBox);\n if (this.formatOptions.onMoof) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMoof(data, start, fragmentStartTimestamp);\n }\n assert(this.writer.getPos() === mdatStartPos);\n if (this.formatOptions.onMdat) {\n this.writer.startTrackingWrites();\n }\n const mdatBox = mdat(needsLargeMdatSize);\n mdatBox.size = mdatSize;\n this.boxWriter.writeBox(mdatBox);\n this.writer.seek(mdatStartPos + (needsLargeMdatSize ? MAX_BOX_HEADER_SIZE : MIN_BOX_HEADER_SIZE));\n for (const trackData of tracksInFragment) {\n for (const sample of trackData.currentChunk.samples) {\n this.writer.write(sample.data);\n sample.data = null;\n }\n }\n if (this.formatOptions.onMdat) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMdat(data, start);\n }\n for (const trackData of tracksInFragment) {\n trackData.finalizedChunks.push(trackData.currentChunk);\n this.finalizedChunks.push(trackData.currentChunk);\n trackData.currentChunk = null;\n }\n if (flushWriter) {\n await this.writer.flush();\n }\n }\n async registerSampleFastStartReserve(trackData, sample) {\n if (this.allTracksAreKnown()) {\n if (!this.mdat) {\n await this.createFastStartReserveMdat();\n }\n await this.addSampleToTrack(trackData, sample);\n } else {\n trackData.sampleQueue.push(sample);\n }\n }\n async createFastStartReserveMdat() {\n assert(this.writer);\n assert(this.boxWriter);\n this.ensureOneEnabledTrack();\n const moovBox = moov(this);\n const moovSize = this.boxWriter.measureBox(moovBox);\n const reservedSize = moovSize + this.computeSampleTableSizeUpperBound() + 4096;\n assert(this.ftypSize !== null);\n this.writer.seek(this.ftypSize + reservedSize);\n if (this.formatOptions.onMdat) {\n this.writer.startTrackingWrites();\n }\n this.mdat = mdat(true);\n this.boxWriter.writeBox(this.mdat);\n for (const trackData of this.trackDatas) {\n for (const sample of trackData.sampleQueue) {\n await this.addSampleToTrack(trackData, sample);\n }\n trackData.sampleQueue.length = 0;\n }\n }\n computeSampleTableSizeUpperBound() {\n assert(this.fastStart === \"reserve\");\n let upperBound = 0;\n for (const trackData of this.trackDatas) {\n const n = trackData.track.metadata.maximumPacketCount;\n assert(n !== undefined);\n upperBound += (4 + 4) * Math.ceil(2 / 3 * n);\n upperBound += 4 * n;\n upperBound += (4 + 4) * Math.ceil(2 / 3 * n);\n upperBound += (4 + 4 + 4) * Math.ceil(2 / 3 * n);\n upperBound += 4 * n;\n upperBound += 8 * n;\n }\n return upperBound;\n }\n async onTrackClose(track) {\n const release = await this.mutex.acquire();\n const trackData = this.trackDatas.find((x) => x.track === track);\n if (trackData) {\n trackData.closed = true;\n if (trackData.type === \"subtitle\" && track.source._codec === \"webvtt\") {\n await this.processWebVTTCues(trackData, Infinity);\n }\n this.processTimestamps(trackData);\n }\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n if (this.isFragmented) {\n await this.interleaveSamples();\n }\n release();\n }\n ensureOneEnabledTrack() {\n for (const type of [\"video\", \"audio\", \"subtitle\"]) {\n const tracks = this.trackDatas.filter((t) => t.type === type);\n if (tracks.length === 0) {\n continue;\n }\n const hasEnabled = tracks.some((t) => t.track.metadata.disposition?.default !== false);\n if (!hasEnabled) {\n const firstTrack = tracks[0];\n firstTrack.track.metadata.disposition = {\n ...firstTrack.track.metadata.disposition,\n default: true\n };\n }\n }\n }\n async forceFragmentFinalization() {\n assert(this.isFragmented);\n const release = await this.mutex.acquire();\n try {\n for (const trackData of this.trackDatas) {\n if (trackData.type === \"subtitle\" && trackData.track.source._codec === \"webvtt\") {\n await this.processWebVTTCues(trackData, Infinity);\n }\n this.processTimestamps(trackData);\n }\n await this.interleaveSamples(true);\n await this.finalizeFragment();\n } finally {\n release();\n }\n }\n async finalize() {\n const release = await this.mutex.acquire();\n this.allTracksKnown.resolve();\n this.ensureOneEnabledTrack();\n if (!this.mdat && this.fastStart === \"reserve\") {\n await this.createFastStartReserveMdat();\n }\n for (const trackData of this.trackDatas) {\n trackData.closed = true;\n if (trackData.type === \"subtitle\" && trackData.track.source._codec === \"webvtt\") {\n await this.processWebVTTCues(trackData, Infinity);\n }\n this.processTimestamps(trackData);\n }\n if (this.isFragmented) {\n await this.interleaveSamples(true);\n await this.finalizeFragment(false);\n } else {\n for (const trackData of this.trackDatas) {\n await this.finalizeCurrentChunk(trackData);\n const span = presentationSpan(trackData);\n if (span > 0) {\n let totalBytes = 0;\n for (const sample of trackData.samples) {\n totalBytes += sample.size;\n }\n trackData.avgBitrate = Math.round(8 * totalBytes / span);\n } else {\n trackData.avgBitrate = 0;\n }\n let windowStart = 0;\n let windowBytes = 0;\n let maxWindowBytes = 0;\n for (let i = 0;i < trackData.samples.length; i++) {\n const sample = trackData.samples[i];\n windowBytes += sample.size;\n while (sample.decodeTimestamp - trackData.samples[windowStart].decodeTimestamp >= 1) {\n windowBytes -= trackData.samples[windowStart].size;\n windowStart++;\n }\n maxWindowBytes = Math.max(maxWindowBytes, windowBytes);\n }\n trackData.maxBitrate = 8 * maxWindowBytes;\n if (trackData.startTimestampOffset !== null) {\n for (let i = 0;i < trackData.samples.length; i++) {\n const sample = trackData.samples[i];\n sample.timestamp -= trackData.startTimestampOffset;\n sample.decodeTimestamp -= trackData.startTimestampOffset;\n }\n }\n }\n }\n assert(this.writer);\n assert(this.boxWriter);\n if (this.fastStart === \"in-memory\") {\n this.mdat = mdat(false);\n let mdatSize;\n for (let i = 0;i < 2; i++) {\n const movieBox = moov(this);\n const movieBoxSize = this.boxWriter.measureBox(movieBox);\n mdatSize = this.boxWriter.measureBox(this.mdat);\n let currentChunkPos = this.writer.getPos() + movieBoxSize + mdatSize;\n for (const chunk of this.finalizedChunks) {\n chunk.offset = currentChunkPos;\n for (const { data } of chunk.samples) {\n assert(data);\n currentChunkPos += data.byteLength;\n mdatSize += data.byteLength;\n }\n }\n if (currentChunkPos < 2 ** 32)\n break;\n if (mdatSize >= 2 ** 32)\n this.mdat.largeSize = true;\n }\n if (this.formatOptions.onMoov) {\n this.writer.startTrackingWrites();\n }\n const movieBox = moov(this);\n this.boxWriter.writeBox(movieBox);\n if (this.formatOptions.onMoov) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMoov(data, start);\n }\n if (this.formatOptions.onMdat) {\n this.writer.startTrackingWrites();\n }\n this.mdat.size = mdatSize;\n this.boxWriter.writeBox(this.mdat);\n for (const chunk of this.finalizedChunks) {\n for (const sample of chunk.samples) {\n assert(sample.data);\n this.writer.write(sample.data);\n sample.data = null;\n }\n }\n if (this.formatOptions.onMdat) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMdat(data, start);\n }\n } else if (this.isFragmented) {\n if (this.isCmaf) {\n const contentSize = this.segmentHeaderSize !== null ? this.writer.getPos() - this.segmentHeaderSize : 0;\n this.writer.seek(0);\n this.boxWriter.writeBox(styp());\n this.boxWriter.writeBox(sidx(this, contentSize));\n } else {\n const startPos = this.writer.getPos();\n const mfraBox = mfra(this.trackDatas);\n this.boxWriter.writeBox(mfraBox);\n const mfraBoxSize = this.writer.getPos() - startPos;\n this.writer.seek(this.writer.getPos() - 4);\n this.boxWriter.writeU32(mfraBoxSize);\n }\n } else {\n assert(this.mdat);\n const mdatPos = this.boxWriter.offsets.get(this.mdat);\n assert(mdatPos !== undefined);\n const mdatSize = this.writer.getPos() - mdatPos;\n this.mdat.size = mdatSize;\n this.mdat.largeSize = mdatSize >= 2 ** 32;\n this.boxWriter.patchBox(this.mdat);\n if (this.formatOptions.onMdat) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMdat(data, start);\n }\n const movieBox = moov(this);\n if (this.fastStart === \"reserve\") {\n assert(this.ftypSize !== null);\n this.writer.seek(this.ftypSize);\n if (this.formatOptions.onMoov) {\n this.writer.startTrackingWrites();\n }\n this.boxWriter.writeBox(movieBox);\n const remainingSpace = this.boxWriter.offsets.get(this.mdat) - this.writer.getPos();\n this.boxWriter.writeBox(free(remainingSpace));\n } else {\n if (this.formatOptions.onMoov) {\n this.writer.startTrackingWrites();\n }\n this.boxWriter.writeBox(movieBox);\n }\n if (this.formatOptions.onMoov) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.formatOptions.onMoov(data, start);\n }\n }\n release();\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/matroska/matroska-muxer.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var MIN_CLUSTER_TIMESTAMP_MS = -(2 ** 15);\n var MAX_CLUSTER_TIMESTAMP_MS = 2 ** 15 - 1;\n var SEGMENT_SIZE_BYTES = 6;\n var CLUSTER_SIZE_BYTES = 5;\n var TRACK_TYPE_MAP = {\n video: 1,\n audio: 2,\n subtitle: 17\n };\n\n class MatroskaMuxer extends Muxer {\n constructor(output, format) {\n super(output);\n this.trackDatas = [];\n this.allTracksKnown = promiseWithResolvers();\n this.segment = null;\n this.segmentInfo = null;\n this.seekHead = null;\n this.tracksElement = null;\n this.tagsElement = null;\n this.attachmentsElement = null;\n this.segmentDuration = null;\n this.cues = null;\n this.currentCluster = null;\n this.currentClusterStartMsTimestamp = null;\n this.currentClusterMaxMsTimestamp = null;\n this.trackDatasInCurrentCluster = new Map;\n this.startTimestamp = Infinity;\n this.endTimestamp = -Infinity;\n this.warnedAboutTooNegativeTimestamp = false;\n this.format = format;\n }\n async start() {\n const release = await this.mutex.acquire();\n this.writer = await this.output._getRootWriter(!!this.format._options.appendOnly);\n this.ebmlWriter = new EBMLWriter(this.writer);\n this.writeEBMLHeader();\n this.createSegmentInfo();\n this.createCues();\n await this.writer.flush();\n for (const track of this.output.tracks) {\n if (track.isVideoTrack() && track.metadata.decoderConfig) {\n this.getVideoTrackData(track, track.metadata.primingPacket ?? null, { decoderConfig: track.metadata.decoderConfig });\n } else if (track.isAudioTrack() && track.metadata.decoderConfig) {\n this.getAudioTrackData(track, track.metadata.primingPacket ?? null, { decoderConfig: track.metadata.decoderConfig });\n }\n }\n release();\n }\n writeEBMLHeader() {\n if (this.format._options.onEbmlHeader) {\n this.writer.startTrackingWrites();\n }\n const ebmlHeader = { id: EBMLId.EBML, data: [\n { id: EBMLId.EBMLVersion, data: 1 },\n { id: EBMLId.EBMLReadVersion, data: 1 },\n { id: EBMLId.EBMLMaxIDLength, data: 4 },\n { id: EBMLId.EBMLMaxSizeLength, data: 8 },\n { id: EBMLId.DocType, data: this.format instanceof WebMOutputFormat ? \"webm\" : \"matroska\" },\n { id: EBMLId.DocTypeVersion, data: 4 },\n { id: EBMLId.DocTypeReadVersion, data: 2 }\n ] };\n this.ebmlWriter.writeEBML(ebmlHeader);\n if (this.format._options.onEbmlHeader) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.format._options.onEbmlHeader(data, start);\n }\n }\n maybeCreateSeekHead(writeOffsets) {\n if (this.format._options.appendOnly) {\n return;\n }\n const kaxCues = new Uint8Array([28, 83, 187, 107]);\n const kaxInfo = new Uint8Array([21, 73, 169, 102]);\n const kaxTracks = new Uint8Array([22, 84, 174, 107]);\n const kaxAttachments = new Uint8Array([25, 65, 164, 105]);\n const kaxTags = new Uint8Array([18, 84, 195, 103]);\n const seekHead = { id: EBMLId.SeekHead, data: [\n { id: EBMLId.Seek, data: [\n { id: EBMLId.SeekID, data: kaxInfo },\n {\n id: EBMLId.SeekPosition,\n size: 5,\n data: writeOffsets ? this.ebmlWriter.offsets.get(this.segmentInfo) - this.segmentDataOffset : 0\n }\n ] },\n { id: EBMLId.Seek, data: [\n { id: EBMLId.SeekID, data: kaxTracks },\n {\n id: EBMLId.SeekPosition,\n size: 5,\n data: writeOffsets ? this.ebmlWriter.offsets.get(this.tracksElement) - this.segmentDataOffset : 0\n }\n ] },\n this.attachmentsElement ? { id: EBMLId.Seek, data: [\n { id: EBMLId.SeekID, data: kaxAttachments },\n {\n id: EBMLId.SeekPosition,\n size: 5,\n data: writeOffsets ? this.ebmlWriter.offsets.get(this.attachmentsElement) - this.segmentDataOffset : 0\n }\n ] } : null,\n this.tagsElement ? { id: EBMLId.Seek, data: [\n { id: EBMLId.SeekID, data: kaxTags },\n {\n id: EBMLId.SeekPosition,\n size: 5,\n data: writeOffsets ? this.ebmlWriter.offsets.get(this.tagsElement) - this.segmentDataOffset : 0\n }\n ] } : null,\n { id: EBMLId.Seek, data: [\n { id: EBMLId.SeekID, data: kaxCues },\n {\n id: EBMLId.SeekPosition,\n size: 5,\n data: writeOffsets ? this.ebmlWriter.offsets.get(this.cues) - this.segmentDataOffset : 0\n }\n ] }\n ] };\n this.seekHead = seekHead;\n }\n createSegmentInfo() {\n const segmentDuration = { id: EBMLId.Duration, data: new EBMLFloat64(0) };\n this.segmentDuration = segmentDuration;\n const segmentInfo = { id: EBMLId.Info, data: [\n { id: EBMLId.TimestampScale, data: 1e6 },\n { id: EBMLId.MuxingApp, data: VERSIONED_NAME },\n { id: EBMLId.WritingApp, data: VERSIONED_NAME },\n !this.format._options.appendOnly ? segmentDuration : null\n ] };\n this.segmentInfo = segmentInfo;\n }\n createTracks() {\n const tracksElement = { id: EBMLId.Tracks, data: [] };\n this.tracksElement = tracksElement;\n for (const trackData of this.trackDatas) {\n let codecId = CODEC_STRING_MAP[trackData.track.source._codec];\n assert(codecId);\n if (trackData.type === \"audio\" && trackData.track.source._codec === \"dts\") {\n if (trackData.info.decoderConfig.codec === \"dtse\") {\n codecId = \"A_DTS/EXPRESS\";\n } else if (trackData.info.decoderConfig.codec === \"dtsl\") {\n codecId = \"A_DTS/LOSSLESS\";\n }\n }\n let seekPreRollNs = 0;\n if (trackData.type === \"audio\" && trackData.track.source._codec === \"opus\") {\n seekPreRollNs = 1e6 * 80;\n const description = trackData.info.decoderConfig.description;\n if (description) {\n const bytes = toUint8Array(description);\n const header = parseOpusIdentificationHeader(bytes);\n seekPreRollNs = Math.round(1e9 * (header.preSkip / OPUS_SAMPLE_RATE));\n }\n }\n tracksElement.data.push({ id: EBMLId.TrackEntry, data: [\n { id: EBMLId.TrackNumber, data: trackData.track.id },\n { id: EBMLId.TrackUID, data: trackData.track.id },\n { id: EBMLId.TrackType, data: TRACK_TYPE_MAP[trackData.type] },\n trackData.track.metadata.disposition?.default === false ? { id: EBMLId.FlagDefault, data: 0 } : null,\n trackData.track.metadata.disposition?.forced ? { id: EBMLId.FlagForced, data: 1 } : null,\n trackData.track.metadata.disposition?.hearingImpaired ? { id: EBMLId.FlagHearingImpaired, data: 1 } : null,\n trackData.track.metadata.disposition?.visuallyImpaired ? { id: EBMLId.FlagVisualImpaired, data: 1 } : null,\n trackData.track.metadata.disposition?.original ? { id: EBMLId.FlagOriginal, data: 1 } : null,\n trackData.track.metadata.disposition?.commentary ? { id: EBMLId.FlagCommentary, data: 1 } : null,\n { id: EBMLId.FlagLacing, data: 0 },\n { id: EBMLId.Language, data: trackData.track.metadata.languageCode ?? UNDETERMINED_LANGUAGE },\n { id: EBMLId.CodecID, data: codecId },\n trackData.codecPrivate ? { id: EBMLId.CodecPrivate, data: toUint8Array(trackData.codecPrivate) } : null,\n seekPreRollNs > 0 ? { id: EBMLId.SeekPreRoll, data: seekPreRollNs } : null,\n trackData.track.metadata.name !== undefined ? { id: EBMLId.Name, data: new EBMLUnicodeString(trackData.track.metadata.name) } : null,\n trackData.type === \"video\" ? this.videoSpecificTrackInfo(trackData) : null,\n trackData.type === \"audio\" ? this.audioSpecificTrackInfo(trackData) : null,\n trackData.type === \"subtitle\" ? this.subtitleSpecificTrackInfo(trackData) : null\n ] });\n }\n }\n videoSpecificTrackInfo(trackData) {\n const { frameRate, transformationMatrix } = trackData.track.metadata;\n const elements = [\n frameRate ? {\n id: EBMLId.DefaultDuration,\n data: 1e9 / frameRate\n } : null\n ];\n let rotation;\n let horizontalScale;\n let verticalScale;\n if (transformationMatrix) {\n rotation = extractRotationFromMatrix(transformationMatrix);\n const unrotated = multiplyMatrices(rotationMatrix(-rotation), transformationMatrix);\n horizontalScale = Math.sign(unrotated[0]);\n verticalScale = Math.sign(unrotated[4]);\n if (verticalScale === -1) {\n verticalScale = 1;\n horizontalScale = -horizontalScale;\n rotation = normalizeRotation(rotation + 180);\n }\n } else {\n rotation = trackData.track.metadata.rotation ?? 0;\n horizontalScale = trackData.track.metadata.flip ? -1 : 1;\n verticalScale = 1;\n }\n const roll = rotation ? normalizeRotation(horizontalScale === -1 ? rotation : -rotation) : 0;\n const yaw = Math.round(Math.acos(horizontalScale) * RAD_TO_DEG);\n const pitch = Math.round(Math.acos(verticalScale) * RAD_TO_DEG);\n const hasNonSquarePixelAspectRatio = !!trackData.info.aspectRatio && trackData.info.aspectRatio.num * trackData.info.height !== trackData.info.aspectRatio.den * trackData.info.width;\n const colorSpace = trackData.info.decoderConfig.colorSpace;\n const videoElement = { id: EBMLId.Video, data: [\n { id: EBMLId.PixelWidth, data: trackData.info.width },\n { id: EBMLId.PixelHeight, data: trackData.info.height },\n hasNonSquarePixelAspectRatio ? { id: EBMLId.DisplayWidth, data: trackData.info.aspectRatio.num } : null,\n hasNonSquarePixelAspectRatio ? { id: EBMLId.DisplayHeight, data: trackData.info.aspectRatio.den } : null,\n hasNonSquarePixelAspectRatio ? { id: EBMLId.DisplayUnit, data: 3 } : null,\n trackData.info.alphaMode ? { id: EBMLId.AlphaMode, data: 1 } : null,\n colorSpaceIsEmpty(colorSpace) ? null : {\n id: EBMLId.Colour,\n data: [\n {\n id: EBMLId.MatrixCoefficients,\n data: colorSpace?.matrix != null ? MATRIX_COEFFICIENTS_MAP[colorSpace.matrix] : 2\n },\n {\n id: EBMLId.TransferCharacteristics,\n data: colorSpace?.transfer != null ? TRANSFER_CHARACTERISTICS_MAP[colorSpace.transfer] : 2\n },\n {\n id: EBMLId.Primaries,\n data: colorSpace?.primaries != null ? COLOR_PRIMARIES_MAP[colorSpace.primaries] : 2\n },\n {\n id: EBMLId.Range,\n data: colorSpace?.fullRange != null ? colorSpace.fullRange ? 2 : 1 : 0\n }\n ]\n },\n roll || yaw || pitch ? {\n id: EBMLId.Projection,\n data: [\n {\n id: EBMLId.ProjectionType,\n data: 0\n },\n yaw ? {\n id: EBMLId.ProjectionPoseYaw,\n data: new EBMLFloat32(yaw)\n } : null,\n pitch ? {\n id: EBMLId.ProjectionPosePitch,\n data: new EBMLFloat32(pitch)\n } : null,\n roll ? {\n id: EBMLId.ProjectionPoseRoll,\n data: new EBMLFloat32((roll + 180) % 360 - 180)\n } : null\n ]\n } : null\n ] };\n elements.push(videoElement);\n return elements;\n }\n audioSpecificTrackInfo(trackData) {\n const pcmInfo = PCM_AUDIO_CODECS.includes(trackData.track.source._codec) ? parsePcmCodec(trackData.track.source._codec) : null;\n return [\n { id: EBMLId.Audio, data: [\n { id: EBMLId.SamplingFrequency, data: new EBMLFloat32(trackData.info.sampleRate) },\n { id: EBMLId.Channels, data: trackData.info.numberOfChannels },\n pcmInfo ? { id: EBMLId.BitDepth, data: 8 * pcmInfo.sampleSize } : null\n ] }\n ];\n }\n subtitleSpecificTrackInfo(trackData) {\n return [];\n }\n maybeCreateTags() {\n const simpleTags = [];\n const addSimpleTag = (key, value) => {\n simpleTags.push({ id: EBMLId.SimpleTag, data: [\n { id: EBMLId.TagName, data: new EBMLUnicodeString(key) },\n typeof value === \"string\" ? { id: EBMLId.TagString, data: new EBMLUnicodeString(value) } : { id: EBMLId.TagBinary, data: value }\n ] });\n };\n const metadataTags = this.output._metadataTags;\n const writtenTags = new Set;\n for (const { key, value } of keyValueIterator(metadataTags)) {\n switch (key) {\n case \"title\":\n {\n addSimpleTag(\"TITLE\", value);\n writtenTags.add(\"TITLE\");\n }\n ;\n break;\n case \"description\":\n {\n addSimpleTag(\"DESCRIPTION\", value);\n writtenTags.add(\"DESCRIPTION\");\n }\n ;\n break;\n case \"artist\":\n {\n addSimpleTag(\"ARTIST\", value);\n writtenTags.add(\"ARTIST\");\n }\n ;\n break;\n case \"album\":\n {\n addSimpleTag(\"ALBUM\", value);\n writtenTags.add(\"ALBUM\");\n }\n ;\n break;\n case \"albumArtist\":\n {\n addSimpleTag(\"ALBUM_ARTIST\", value);\n writtenTags.add(\"ALBUM_ARTIST\");\n }\n ;\n break;\n case \"genre\":\n {\n addSimpleTag(\"GENRE\", value);\n writtenTags.add(\"GENRE\");\n }\n ;\n break;\n case \"beatsPerMinute\":\n {\n addSimpleTag(\"BPM\", value.toString());\n writtenTags.add(\"BPM\");\n }\n ;\n break;\n case \"comment\":\n {\n addSimpleTag(\"COMMENT\", value);\n writtenTags.add(\"COMMENT\");\n }\n ;\n break;\n case \"lyrics\":\n {\n addSimpleTag(\"LYRICS\", value);\n writtenTags.add(\"LYRICS\");\n }\n ;\n break;\n case \"date\":\n {\n addSimpleTag(\"DATE\", value.toISOString().slice(0, 10));\n writtenTags.add(\"DATE\");\n }\n ;\n break;\n case \"trackNumber\":\n {\n const string = metadataTags.tracksTotal !== undefined ? `${value}/${metadataTags.tracksTotal}` : value.toString();\n addSimpleTag(\"PART_NUMBER\", string);\n writtenTags.add(\"PART_NUMBER\");\n }\n ;\n break;\n case \"discNumber\":\n {\n const string = metadataTags.discsTotal !== undefined ? `${value}/${metadataTags.discsTotal}` : value.toString();\n addSimpleTag(\"DISC\", string);\n writtenTags.add(\"DISC\");\n }\n ;\n break;\n case \"tracksTotal\":\n case \"discsTotal\":\n {}\n ;\n break;\n case \"images\":\n case \"raw\":\n {}\n ;\n break;\n default:\n assertNever(key);\n }\n }\n if (metadataTags.raw) {\n for (const key in metadataTags.raw) {\n const value = metadataTags.raw[key];\n if (value == null || writtenTags.has(key)) {\n continue;\n }\n if (typeof value === \"string\" || value instanceof Uint8Array) {\n addSimpleTag(key, value);\n }\n }\n }\n if (simpleTags.length === 0) {\n return;\n }\n this.tagsElement = {\n id: EBMLId.Tags,\n data: [{ id: EBMLId.Tag, data: [\n { id: EBMLId.Targets, data: [\n { id: EBMLId.TargetTypeValue, data: 50 },\n { id: EBMLId.TargetType, data: \"MOVIE\" }\n ] },\n ...simpleTags\n ] }]\n };\n }\n maybeCreateAttachments() {\n const metadataTags = this.output._metadataTags;\n const elements = [];\n const existingFileUids = new Set;\n const images = metadataTags.images ?? [];\n for (const image of images) {\n let imageName = image.name;\n if (imageName === undefined) {\n const baseName = image.kind === \"coverFront\" ? \"cover\" : image.kind === \"coverBack\" ? \"back\" : \"image\";\n imageName = baseName + (imageMimeTypeToExtension(image.mimeType) ?? \"\");\n }\n let fileUid;\n while (true) {\n fileUid = 0n;\n for (let i = 0;i < 8; i++) {\n fileUid <<= 8n;\n fileUid |= BigInt(Math.floor(Math.random() * 256));\n }\n if (fileUid !== 0n && !existingFileUids.has(fileUid)) {\n break;\n }\n }\n existingFileUids.add(fileUid);\n elements.push({\n id: EBMLId.AttachedFile,\n data: [\n image.description !== undefined ? { id: EBMLId.FileDescription, data: new EBMLUnicodeString(image.description) } : null,\n { id: EBMLId.FileName, data: new EBMLUnicodeString(imageName) },\n { id: EBMLId.FileMediaType, data: image.mimeType },\n { id: EBMLId.FileData, data: image.data },\n { id: EBMLId.FileUID, data: fileUid }\n ]\n });\n }\n for (const [key, value] of Object.entries(metadataTags.raw ?? {})) {\n if (!(value instanceof AttachedFile)) {\n continue;\n }\n const keyIsNumeric = /^\\d+$/.test(key);\n if (!keyIsNumeric) {\n continue;\n }\n if (images.find((x) => x.mimeType === value.mimeType && uint8ArraysAreEqual(x.data, value.data))) {\n continue;\n }\n elements.push({\n id: EBMLId.AttachedFile,\n data: [\n value.description !== undefined ? { id: EBMLId.FileDescription, data: new EBMLUnicodeString(value.description) } : null,\n { id: EBMLId.FileName, data: new EBMLUnicodeString(value.name ?? \"\") },\n { id: EBMLId.FileMediaType, data: value.mimeType ?? \"\" },\n { id: EBMLId.FileData, data: value.data },\n { id: EBMLId.FileUID, data: BigInt(key) }\n ]\n });\n }\n if (elements.length === 0) {\n return;\n }\n this.attachmentsElement = { id: EBMLId.Attachments, data: elements };\n }\n createSegment() {\n this.createTracks();\n this.maybeCreateTags();\n this.maybeCreateAttachments();\n this.maybeCreateSeekHead(false);\n const segment = {\n id: EBMLId.Segment,\n size: this.format._options.appendOnly ? -1 : SEGMENT_SIZE_BYTES,\n data: [\n this.seekHead,\n this.segmentInfo,\n this.tracksElement,\n this.attachmentsElement,\n this.tagsElement\n ]\n };\n this.segment = segment;\n if (this.format._options.onSegmentHeader) {\n this.writer.startTrackingWrites();\n }\n this.ebmlWriter.writeEBML(segment);\n if (this.format._options.onSegmentHeader) {\n const { data, start } = this.writer.stopTrackingWrites();\n this.format._options.onSegmentHeader(data, start);\n }\n }\n createCues() {\n this.cues = { id: EBMLId.Cues, data: [] };\n }\n get segmentDataOffset() {\n assert(this.segment);\n return this.ebmlWriter.dataOffsets.get(this.segment);\n }\n allTracksAreKnown() {\n for (const track of this.output.tracks) {\n if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) {\n return false;\n }\n }\n return true;\n }\n async getMimeType() {\n await this.allTracksKnown.promise;\n const codecStrings = this.trackDatas.map((trackData) => {\n if (trackData.type === \"video\") {\n return trackData.info.decoderConfig.codec;\n } else if (trackData.type === \"audio\") {\n return trackData.info.decoderConfig.codec;\n } else {\n const map = {\n webvtt: \"wvtt\"\n };\n return map[trackData.track.source._codec];\n }\n });\n return buildMatroskaMimeType({\n isWebM: this.format instanceof WebMOutputFormat,\n hasVideo: this.trackDatas.some((x) => x.type === \"video\"),\n hasAudio: this.trackDatas.some((x) => x.type === \"audio\"),\n codecStrings\n });\n }\n getVideoTrackData(track, packet, meta) {\n const existingTrackData = this.trackDatas.find((x) => x.track === track);\n if (existingTrackData) {\n return existingTrackData;\n }\n validateVideoChunkMetadata(meta, track.source._codec);\n assert(meta);\n assert(meta.decoderConfig);\n assert(meta.decoderConfig.codedWidth !== undefined);\n assert(meta.decoderConfig.codedHeight !== undefined);\n const displayAspectWidth = meta.decoderConfig.displayAspectWidth;\n const displayAspectHeight = meta.decoderConfig.displayAspectHeight;\n const aspectRatio = displayAspectWidth === undefined || displayAspectHeight === undefined ? null : simplifyRational({\n num: displayAspectWidth,\n den: displayAspectHeight\n });\n const newTrackData = {\n track,\n type: \"video\",\n info: {\n width: meta.decoderConfig.codedWidth,\n height: meta.decoderConfig.codedHeight,\n aspectRatio,\n decoderConfig: meta.decoderConfig,\n alphaMode: track.metadata.canBeTransparent || (packet ? !!packet.sideData.alpha : null)\n },\n chunkQueue: [],\n lastWrittenMsTimestamp: null,\n codecPrivate: meta.decoderConfig.description ?? null,\n closed: false\n };\n if (track.source._codec === \"vp9\") {\n newTrackData.codecPrivate = new Uint8Array(generateVp9CodecConfigurationFromCodecString(newTrackData.info.decoderConfig.codec));\n } else if (track.source._codec === \"av1\") {\n newTrackData.codecPrivate = new Uint8Array(generateAv1CodecConfigurationFromCodecString(newTrackData.info.decoderConfig.codec));\n } else if (track.source._codec === \"prores\") {\n newTrackData.codecPrivate = textEncoder.encode(meta.decoderConfig.codec);\n }\n this.trackDatas.push(newTrackData);\n this.trackDatas.sort((a, b) => a.track.id - b.track.id);\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n return newTrackData;\n }\n getAudioTrackData(track, packet, meta) {\n const existingTrackData = this.trackDatas.find((x) => x.track === track);\n if (existingTrackData) {\n return existingTrackData;\n }\n validateAudioChunkMetadata(meta, track.source._codec);\n assert(meta);\n assert(meta.decoderConfig);\n const decoderConfig = { ...meta.decoderConfig };\n let requiresAdtsStripping = false;\n if (track.source._codec === \"aac\" && !decoderConfig.description) {\n if (!packet) {\n throw new Error(\"No AAC description provided; you must therefore provide a priming packet.\");\n }\n const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packet.data));\n if (!adtsFrame) {\n throw new Error(\"Couldn't parse ADTS header from the AAC packet. Make sure the packets are in ADTS format\" + \" (as specified in ISO 13818-7) when not providing a description, or provide a description\" + \" (must be an AudioSpecificConfig as specified in ISO 14496-3) and ensure the packets\" + \" are raw AAC data.\");\n }\n const sampleRate = aacFrequencyTable[adtsFrame.samplingFrequencyIndex];\n const numberOfChannels = aacChannelMap[adtsFrame.channelConfiguration];\n if (sampleRate === undefined || numberOfChannels === undefined) {\n throw new Error(\"Invalid ADTS frame header.\");\n }\n decoderConfig.description = buildAacAudioSpecificConfig({\n objectType: adtsFrame.objectType,\n outputSampleRate: sampleRate,\n outputNumberOfChannels: numberOfChannels\n });\n requiresAdtsStripping = true;\n }\n const newTrackData = {\n track,\n type: \"audio\",\n info: {\n numberOfChannels: meta.decoderConfig.numberOfChannels,\n sampleRate: meta.decoderConfig.sampleRate,\n decoderConfig,\n requiresAdtsStripping\n },\n chunkQueue: [],\n lastWrittenMsTimestamp: null,\n codecPrivate: decoderConfig.description ?? null,\n closed: false\n };\n this.trackDatas.push(newTrackData);\n this.trackDatas.sort((a, b) => a.track.id - b.track.id);\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n return newTrackData;\n }\n getSubtitleTrackData(track, meta) {\n const existingTrackData = this.trackDatas.find((x) => x.track === track);\n if (existingTrackData) {\n return existingTrackData;\n }\n validateSubtitleMetadata(meta);\n assert(meta);\n assert(meta.config);\n const newTrackData = {\n track,\n type: \"subtitle\",\n info: {\n config: meta.config\n },\n chunkQueue: [],\n lastWrittenMsTimestamp: null,\n codecPrivate: textEncoder.encode(meta.config.description),\n closed: false\n };\n this.trackDatas.push(newTrackData);\n this.trackDatas.sort((a, b) => a.track.id - b.track.id);\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n return newTrackData;\n }\n async addEncodedVideoPacket(track, packet, meta) {\n const release = await this.mutex.acquire();\n try {\n const trackData = this.getVideoTrackData(track, packet, meta);\n trackData.info.alphaMode ??= !!packet.sideData.alpha;\n let packetData = packet.data;\n if (track.source._codec === \"prores\") {\n if (packetData.byteLength < 8) {\n throw new Error(\"ProRes packet too small, expected at least 8 bytes.\");\n }\n packetData = packetData.subarray(8);\n }\n const isKeyFrame = packet.type === \"key\";\n this.validateTimestamp(trackData.track, packet.timestamp, isKeyFrame);\n let timestamp = packet.timestamp;\n let duration = packet.duration;\n if (track.metadata.frameRate !== undefined) {\n timestamp = roundToDivisor(timestamp, track.metadata.frameRate);\n duration = roundToDivisor(duration, track.metadata.frameRate);\n }\n const additions = trackData.info.alphaMode ? packet.sideData.alpha ?? null : null;\n const videoChunk = this.createInternalChunk(packetData, timestamp, duration, packet.type, additions);\n if (track.source._codec === \"vp9\")\n this.fixVP9ColorSpace(trackData, videoChunk);\n trackData.chunkQueue.push(videoChunk);\n await this.interleaveChunks();\n } finally {\n release();\n }\n }\n async addEncodedAudioPacket(track, packet, meta) {\n const release = await this.mutex.acquire();\n try {\n const trackData = this.getAudioTrackData(track, packet, meta);\n let packetData = packet.data;\n if (trackData.info.requiresAdtsStripping) {\n const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packetData));\n if (!adtsFrame) {\n throw new Error(\"Expected ADTS frame, didn't get one.\");\n }\n const headerLength = adtsFrame.crcCheck === null ? MIN_ADTS_FRAME_HEADER_SIZE : MAX_ADTS_FRAME_HEADER_SIZE;\n packetData = packetData.subarray(headerLength);\n }\n const isKeyFrame = packet.type === \"key\";\n this.validateTimestamp(trackData.track, packet.timestamp, isKeyFrame);\n const audioChunk = this.createInternalChunk(packetData, packet.timestamp, packet.duration, packet.type);\n trackData.chunkQueue.push(audioChunk);\n await this.interleaveChunks();\n } finally {\n release();\n }\n }\n async addSubtitleCue(track, cue, meta) {\n const release = await this.mutex.acquire();\n try {\n const trackData = this.getSubtitleTrackData(track, meta);\n this.validateTimestamp(trackData.track, cue.timestamp, true);\n let bodyText = cue.text;\n const timestampMs = Math.round(cue.timestamp * 1000);\n inlineTimestampRegex.lastIndex = 0;\n bodyText = bodyText.replace(inlineTimestampRegex, (match) => {\n const time = parseSubtitleTimestamp(match.slice(1, -1));\n const offsetTime = time - timestampMs;\n return `<${formatSubtitleTimestamp(offsetTime)}>`;\n });\n const body = textEncoder.encode(bodyText);\n const additions = `${cue.settings ?? \"\"}\n${cue.identifier ?? \"\"}\n${cue.notes ?? \"\"}`;\n const subtitleChunk = this.createInternalChunk(body, cue.timestamp, cue.duration, \"key\", additions.trim() ? textEncoder.encode(additions) : null);\n trackData.chunkQueue.push(subtitleChunk);\n await this.interleaveChunks();\n } finally {\n release();\n }\n }\n async interleaveChunks(isFinalCall = false) {\n if (!isFinalCall && !this.allTracksAreKnown()) {\n return;\n }\n outer:\n while (true) {\n let trackWithMinTimestamp = null;\n let minTimestamp = Infinity;\n for (const trackData of this.trackDatas) {\n if (!isFinalCall && trackData.chunkQueue.length === 0 && !trackData.closed) {\n break outer;\n }\n if (trackData.chunkQueue.length > 0 && trackData.chunkQueue[0].timestamp < minTimestamp) {\n trackWithMinTimestamp = trackData;\n minTimestamp = trackData.chunkQueue[0].timestamp;\n }\n }\n if (!trackWithMinTimestamp) {\n break;\n }\n const chunk = trackWithMinTimestamp.chunkQueue.shift();\n this.writeBlock(trackWithMinTimestamp, chunk);\n }\n if (!isFinalCall) {\n await this.writer.flush();\n }\n }\n fixVP9ColorSpace(trackData, chunk) {\n if (chunk.type !== \"key\")\n return;\n if (!trackData.info.decoderConfig.colorSpace || !trackData.info.decoderConfig.colorSpace.matrix)\n return;\n const bitstream = new Bitstream(chunk.data);\n bitstream.skipBits(2);\n const profileLowBit = bitstream.readBits(1);\n const profileHighBit = bitstream.readBits(1);\n const profile = (profileHighBit << 1) + profileLowBit;\n if (profile === 3)\n bitstream.skipBits(1);\n const showExistingFrame = bitstream.readBits(1);\n if (showExistingFrame)\n return;\n const frameType = bitstream.readBits(1);\n if (frameType !== 0)\n return;\n bitstream.skipBits(2);\n const syncCode = bitstream.readBits(24);\n if (syncCode !== 4817730)\n return;\n if (profile >= 2)\n bitstream.skipBits(1);\n const colorSpaceID = {\n rgb: 7,\n bt709: 2,\n bt470bg: 1,\n smpte170m: 3\n }[trackData.info.decoderConfig.colorSpace.matrix];\n writeBits(chunk.data, bitstream.pos, bitstream.pos + 3, colorSpaceID);\n }\n createInternalChunk(data, timestamp, duration, type, additions = null) {\n const internalChunk = {\n data,\n type,\n timestamp,\n duration,\n additions\n };\n return internalChunk;\n }\n writeBlock(trackData, chunk) {\n if (!this.segment) {\n this.createSegment();\n }\n const msTimestamp = Math.round(1000 * chunk.timestamp);\n const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => {\n if (trackData === otherTrackData) {\n return chunk.type === \"key\";\n }\n const firstQueuedSample = otherTrackData.chunkQueue[0];\n if (firstQueuedSample) {\n return firstQueuedSample.type === \"key\";\n }\n return otherTrackData.closed;\n });\n let shouldCreateNewCluster = false;\n if (!this.currentCluster) {\n shouldCreateNewCluster = true;\n } else {\n assert(this.currentClusterStartMsTimestamp !== null);\n assert(this.currentClusterMaxMsTimestamp !== null);\n const relativeTimestamp = msTimestamp - this.currentClusterStartMsTimestamp;\n shouldCreateNewCluster = keyFrameQueuedEverywhere && msTimestamp > this.currentClusterMaxMsTimestamp && relativeTimestamp >= 1000 * (this.format._options.minimumClusterDuration ?? 1) || relativeTimestamp > MAX_CLUSTER_TIMESTAMP_MS;\n }\n if (shouldCreateNewCluster) {\n this.createNewCluster(msTimestamp);\n }\n const relativeTimestamp = msTimestamp - this.currentClusterStartMsTimestamp;\n if (relativeTimestamp < MIN_CLUSTER_TIMESTAMP_MS) {\n if (!this.warnedAboutTooNegativeTimestamp) {\n const formatName = this.format instanceof WebMOutputFormat ? \"WebM\" : \"Matroska\";\n Logging._warn(`Packets had to be discarded because their timestamp is too negative to represent in` + ` ${formatName}.`);\n this.warnedAboutTooNegativeTimestamp = true;\n }\n return;\n }\n const prelude = new Uint8Array(4);\n const view = new DataView(prelude.buffer);\n view.setUint8(0, 128 | trackData.track.id);\n view.setInt16(1, relativeTimestamp, false);\n const msDuration = Math.round(1000 * chunk.duration);\n const needsBlockGroup = !!chunk.additions || trackData.type === \"subtitle\";\n if (!needsBlockGroup) {\n view.setUint8(3, Number(chunk.type === \"key\") << 7);\n const simpleBlock = { id: EBMLId.SimpleBlock, data: [\n prelude,\n chunk.data\n ] };\n this.ebmlWriter.writeEBML(simpleBlock);\n } else {\n const blockGroup = { id: EBMLId.BlockGroup, data: [\n { id: EBMLId.Block, data: [\n prelude,\n chunk.data\n ] },\n chunk.type === \"delta\" ? {\n id: EBMLId.ReferenceBlock,\n data: new EBMLSignedInt(trackData.lastWrittenMsTimestamp - msTimestamp)\n } : null,\n chunk.additions ? { id: EBMLId.BlockAdditions, data: [\n { id: EBMLId.BlockMore, data: [\n { id: EBMLId.BlockAddID, data: 1 },\n { id: EBMLId.BlockAdditional, data: chunk.additions }\n ] }\n ] } : null,\n msDuration > 0 ? { id: EBMLId.BlockDuration, data: msDuration } : null\n ] };\n this.ebmlWriter.writeEBML(blockGroup);\n }\n this.startTimestamp = Math.min(this.startTimestamp, msTimestamp);\n this.endTimestamp = Math.max(this.endTimestamp, msTimestamp + msDuration);\n trackData.lastWrittenMsTimestamp = msTimestamp;\n if (!this.trackDatasInCurrentCluster.has(trackData)) {\n this.trackDatasInCurrentCluster.set(trackData, {\n firstMsTimestamp: msTimestamp\n });\n }\n this.currentClusterMaxMsTimestamp = Math.max(this.currentClusterMaxMsTimestamp, msTimestamp);\n }\n createNewCluster(msTimestamp) {\n msTimestamp = Math.max(0, msTimestamp);\n if (this.currentCluster) {\n this.finalizeCurrentCluster();\n }\n if (this.format._options.onCluster) {\n this.writer.startTrackingWrites();\n }\n this.currentCluster = {\n id: EBMLId.Cluster,\n size: this.format._options.appendOnly ? -1 : CLUSTER_SIZE_BYTES,\n data: [\n { id: EBMLId.Timestamp, data: msTimestamp }\n ]\n };\n this.ebmlWriter.writeEBML(this.currentCluster);\n this.currentClusterStartMsTimestamp = msTimestamp;\n this.currentClusterMaxMsTimestamp = msTimestamp;\n this.trackDatasInCurrentCluster.clear();\n }\n finalizeCurrentCluster() {\n assert(this.currentCluster);\n if (!this.format._options.appendOnly) {\n const clusterSize = this.writer.getPos() - this.ebmlWriter.dataOffsets.get(this.currentCluster);\n const endPos = this.writer.getPos();\n this.writer.seek(this.ebmlWriter.offsets.get(this.currentCluster) + 4);\n this.ebmlWriter.writeVarInt(clusterSize, CLUSTER_SIZE_BYTES);\n this.writer.seek(endPos);\n }\n if (this.format._options.onCluster) {\n assert(this.currentClusterStartMsTimestamp !== null);\n const { data, start } = this.writer.stopTrackingWrites();\n this.format._options.onCluster(data, start, this.currentClusterStartMsTimestamp / 1000);\n }\n const clusterOffsetFromSegment = this.ebmlWriter.offsets.get(this.currentCluster) - this.segmentDataOffset;\n const groupedByTimestamp = new Map;\n for (const [trackData, { firstMsTimestamp }] of this.trackDatasInCurrentCluster) {\n if (!groupedByTimestamp.has(firstMsTimestamp)) {\n groupedByTimestamp.set(firstMsTimestamp, []);\n }\n groupedByTimestamp.get(firstMsTimestamp).push(trackData);\n }\n const groupedAndSortedByTimestamp = [...groupedByTimestamp.entries()].sort((a, b) => a[0] - b[0]);\n for (const [msTimestamp, trackDatas] of groupedAndSortedByTimestamp) {\n assert(this.cues);\n this.cues.data.push({ id: EBMLId.CuePoint, data: [\n { id: EBMLId.CueTime, data: Math.max(0, msTimestamp) },\n ...trackDatas.map((trackData) => {\n return { id: EBMLId.CueTrackPositions, data: [\n { id: EBMLId.CueTrack, data: trackData.track.id },\n { id: EBMLId.CueClusterPosition, data: clusterOffsetFromSegment }\n ] };\n })\n ] });\n }\n }\n async onTrackClose(track) {\n const release = await this.mutex.acquire();\n const trackData = this.trackDatas.find((x) => x.track === track);\n if (trackData) {\n trackData.closed = true;\n }\n if (this.allTracksAreKnown()) {\n this.allTracksKnown.resolve();\n }\n await this.interleaveChunks();\n release();\n }\n async finalize() {\n const release = await this.mutex.acquire();\n this.allTracksKnown.resolve();\n for (const trackData of this.trackDatas) {\n trackData.closed = true;\n }\n if (!this.segment) {\n this.createSegment();\n }\n await this.interleaveChunks(true);\n if (this.currentCluster) {\n this.finalizeCurrentCluster();\n }\n assert(this.cues);\n this.ebmlWriter.writeEBML(this.cues);\n if (!this.format._options.appendOnly) {\n const segmentSize = this.writer.getPos() - this.segmentDataOffset;\n this.writer.seek(this.ebmlWriter.offsets.get(this.segment) + 4);\n this.ebmlWriter.writeVarInt(segmentSize, SEGMENT_SIZE_BYTES);\n const duration = this.startTimestamp === Infinity ? 0 : this.endTimestamp;\n this.segmentDuration.data = new EBMLFloat64(duration);\n this.writer.seek(this.ebmlWriter.offsets.get(this.segmentDuration));\n this.ebmlWriter.writeEBML(this.segmentDuration);\n assert(this.seekHead);\n this.writer.seek(this.ebmlWriter.offsets.get(this.seekHead));\n this.maybeCreateSeekHead(true);\n this.ebmlWriter.writeEBML(this.seekHead);\n }\n release();\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/resample.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class AudioResampler {\n constructor(options) {\n this.sourceSampleRate = null;\n this.sourceNumberOfChannels = null;\n this.startTime = null;\n this.bufferStartFrame = 0;\n this.maxWrittenFrame = null;\n this.targetSampleRate = options.targetSampleRate;\n this.targetNumberOfChannels = options.targetNumberOfChannels;\n this.onSample = options.onSample;\n this.bufferSizeInFrames = Math.floor(this.targetSampleRate * 5);\n this.bufferSizeInSamples = this.bufferSizeInFrames * this.targetNumberOfChannels;\n }\n doChannelMixerSetup() {\n assert(this.sourceNumberOfChannels !== null);\n const sourceNum = this.sourceNumberOfChannels;\n const targetNum = this.targetNumberOfChannels;\n if (sourceNum === 1 && targetNum === 2) {\n this.channelMixer = (sourceData, sourceFrameIndex) => {\n return sourceData[sourceFrameIndex * sourceNum];\n };\n } else if (sourceNum === 1 && targetNum === 4) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return sourceData[sourceFrameIndex * sourceNum] * +(targetChannelIndex < 2);\n };\n } else if (sourceNum === 1 && targetNum === 6) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return sourceData[sourceFrameIndex * sourceNum] * +(targetChannelIndex === 2);\n };\n } else if (sourceNum === 2 && targetNum === 1) {\n this.channelMixer = (sourceData, sourceFrameIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return 0.5 * (sourceData[baseIdx] + sourceData[baseIdx + 1]);\n };\n } else if (sourceNum === 2 && targetNum === 4) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return targetChannelIndex < 2 ? sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] : 0;\n };\n } else if (sourceNum === 2 && targetNum === 6) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return targetChannelIndex < 2 ? sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] : 0;\n };\n } else if (sourceNum === 4 && targetNum === 1) {\n this.channelMixer = (sourceData, sourceFrameIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return 0.25 * (sourceData[baseIdx] + sourceData[baseIdx + 1] + sourceData[baseIdx + 2] + sourceData[baseIdx + 3]);\n };\n } else if (sourceNum === 4 && targetNum === 2) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return 0.5 * (sourceData[baseIdx + targetChannelIndex] + sourceData[baseIdx + targetChannelIndex + 2]);\n };\n } else if (sourceNum === 4 && targetNum === 6) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n if (targetChannelIndex < 2)\n return sourceData[baseIdx + targetChannelIndex];\n if (targetChannelIndex === 2 || targetChannelIndex === 3)\n return 0;\n return sourceData[baseIdx + targetChannelIndex - 2];\n };\n } else if (sourceNum === 6 && targetNum === 1) {\n this.channelMixer = (sourceData, sourceFrameIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return Math.SQRT1_2 * (sourceData[baseIdx] + sourceData[baseIdx + 1]) + sourceData[baseIdx + 2] + 0.5 * (sourceData[baseIdx + 4] + sourceData[baseIdx + 5]);\n };\n } else if (sourceNum === 6 && targetNum === 2) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n return sourceData[baseIdx + targetChannelIndex] + Math.SQRT1_2 * (sourceData[baseIdx + 2] + sourceData[baseIdx + targetChannelIndex + 4]);\n };\n } else if (sourceNum === 6 && targetNum === 4) {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n const baseIdx = sourceFrameIndex * sourceNum;\n if (targetChannelIndex < 2) {\n return sourceData[baseIdx + targetChannelIndex] + Math.SQRT1_2 * sourceData[baseIdx + 2];\n }\n return sourceData[baseIdx + targetChannelIndex + 2];\n };\n } else {\n this.channelMixer = (sourceData, sourceFrameIndex, targetChannelIndex) => {\n return targetChannelIndex < sourceNum ? sourceData[sourceFrameIndex * sourceNum + targetChannelIndex] : 0;\n };\n }\n }\n ensureTempBufferSize(requiredSamples) {\n let length = this.tempSourceBuffer.length;\n while (length < requiredSamples) {\n length *= 2;\n }\n if (length !== this.tempSourceBuffer.length) {\n const newBuffer = new Float32Array(length);\n newBuffer.set(this.tempSourceBuffer);\n this.tempSourceBuffer = newBuffer;\n }\n }\n async add(audioSample) {\n if (this.sourceSampleRate === null) {\n this.sourceSampleRate = audioSample.sampleRate;\n this.sourceNumberOfChannels = audioSample.numberOfChannels;\n this.startTime = audioSample.timestamp;\n const overflowFrames = Math.ceil(this.targetSampleRate / this.sourceSampleRate);\n this.bufferCapacityInFrames = this.bufferSizeInFrames + overflowFrames;\n this.outputBuffer = new Float32Array(this.bufferCapacityInFrames * this.targetNumberOfChannels);\n this.tempSourceBuffer = new Float32Array(this.sourceSampleRate * this.sourceNumberOfChannels);\n this.doChannelMixerSetup();\n }\n assert(this.startTime !== null);\n const requiredSamples = audioSample.numberOfFrames * audioSample.numberOfChannels;\n this.ensureTempBufferSize(requiredSamples);\n const sourceDataSize = audioSample.allocationSize({ planeIndex: 0, format: \"f32\" });\n const sourceView = new Float32Array(this.tempSourceBuffer.buffer, 0, sourceDataSize / 4);\n audioSample.copyTo(sourceView, { planeIndex: 0, format: \"f32\" });\n const inputStartTime = audioSample.timestamp - this.startTime;\n const inputEndTime = inputStartTime + audioSample.duration;\n const outputStartFrame = Math.floor((inputStartTime - 1 / this.sourceSampleRate) * this.targetSampleRate) + 1;\n const outputEndFrame = Math.ceil(inputEndTime * this.targetSampleRate);\n for (let outputFrame = outputStartFrame;outputFrame < outputEndFrame; outputFrame++) {\n if (outputFrame < this.bufferStartFrame) {\n continue;\n }\n while (outputFrame >= this.bufferStartFrame + this.bufferCapacityInFrames) {\n await this.finalizeCurrentBuffer();\n this.bufferStartFrame += this.bufferSizeInFrames;\n }\n const bufferFrameIndex = outputFrame - this.bufferStartFrame;\n assert(bufferFrameIndex < this.bufferCapacityInFrames);\n const outputTime = outputFrame / this.targetSampleRate;\n const inputTime = outputTime - inputStartTime;\n const sourcePosition = inputTime * this.sourceSampleRate;\n const sourceLowerFrame = Math.floor(sourcePosition);\n const sourceUpperFrame = Math.ceil(sourcePosition);\n const fraction = sourcePosition - sourceLowerFrame;\n for (let targetChannel = 0;targetChannel < this.targetNumberOfChannels; targetChannel++) {\n let lowerSample = 0;\n let upperSample = 0;\n if (sourceLowerFrame >= 0 && sourceLowerFrame < audioSample.numberOfFrames) {\n lowerSample = this.channelMixer(sourceView, sourceLowerFrame, targetChannel);\n }\n if (sourceUpperFrame >= 0 && sourceUpperFrame < audioSample.numberOfFrames) {\n upperSample = this.channelMixer(sourceView, sourceUpperFrame, targetChannel);\n }\n const outputSample = lowerSample + fraction * (upperSample - lowerSample);\n const outputIndex = bufferFrameIndex * this.targetNumberOfChannels + targetChannel;\n this.outputBuffer[outputIndex] += outputSample;\n }\n if (this.maxWrittenFrame === null) {\n this.maxWrittenFrame = bufferFrameIndex;\n } else {\n this.maxWrittenFrame = Math.max(this.maxWrittenFrame, bufferFrameIndex);\n }\n }\n }\n async finalizeCurrentBuffer() {\n if (this.maxWrittenFrame === null) {\n return;\n }\n assert(this.startTime !== null);\n const samplesWritten = Math.min(this.maxWrittenFrame + 1, this.bufferSizeInFrames) * this.targetNumberOfChannels;\n const outputData = new Float32Array(samplesWritten);\n outputData.set(this.outputBuffer.subarray(0, samplesWritten));\n const audioSample = new AudioSample({\n format: \"f32\",\n sampleRate: this.targetSampleRate,\n numberOfChannels: this.targetNumberOfChannels,\n timestamp: this.startTime + this.bufferStartFrame / this.targetSampleRate,\n data: outputData\n });\n await this.onSample(audioSample);\n this.outputBuffer.copyWithin(0, this.bufferSizeInSamples);\n this.outputBuffer.fill(0, this.outputBuffer.length - this.bufferSizeInSamples);\n this.maxWrittenFrame = this.maxWrittenFrame >= this.bufferSizeInFrames ? this.maxWrittenFrame - this.bufferSizeInFrames : null;\n }\n async finalize() {\n while (this.maxWrittenFrame !== null) {\n await this.finalizeCurrentBuffer();\n this.bufferStartFrame += this.bufferSizeInFrames;\n }\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/media-source.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var __addDisposableResource2 = function(env, value, async) {\n if (value !== null && value !== undefined) {\n if (typeof value !== \"object\" && typeof value !== \"function\")\n throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose)\n throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === undefined) {\n if (!Symbol.dispose)\n throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async)\n inner = dispose;\n }\n if (typeof dispose !== \"function\")\n throw new TypeError(\"Object not disposable.\");\n if (inner)\n dispose = function() {\n try {\n inner.call(this);\n } catch (e) {\n return Promise.reject(e);\n }\n };\n env.stack.push({ value, dispose, async });\n } else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n };\n var __disposeResources2 = function(SuppressedError2) {\n return function(env) {\n function fail(e) {\n env.error = env.hasError ? new SuppressedError2(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1)\n return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async)\n return s |= 2, Promise.resolve(result).then(next, function(e) {\n fail(e);\n return next();\n });\n } else\n s |= 1;\n } catch (e) {\n fail(e);\n }\n }\n if (s === 1)\n return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError)\n throw env.error;\n }\n return next();\n };\n }(typeof SuppressedError === \"function\" ? SuppressedError : function(error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n });\n\n class MediaSource {\n constructor() {\n this._connectedTrack = null;\n this._closingPromise = null;\n this._closed = false;\n this._nominalBitrate = null;\n }\n _ensureValidAdd() {\n if (!this._connectedTrack) {\n throw new Error(\"Source is not connected to an output track.\");\n }\n if (this._connectedTrack.output.state === \"canceled\") {\n throw new Error(\"Output has been canceled.\");\n }\n if (this._connectedTrack.output.state === \"finalizing\" || this._connectedTrack.output.state === \"finalized\") {\n throw new Error(\"Output has been finalized.\");\n }\n if (this._connectedTrack.output.state === \"pending\") {\n throw new Error(\"Output has not started.\");\n }\n if (this._closed) {\n throw new Error(\"Source is closed.\");\n }\n }\n async _start() {}\n async _flushAndClose(forceClose) {}\n close() {\n if (this._closingPromise) {\n return;\n }\n const connectedTrack = this._connectedTrack;\n if (!connectedTrack) {\n throw new Error(\"Cannot call close without connecting the source to an output track.\");\n }\n if (connectedTrack.output.state === \"pending\") {\n throw new Error(\"Cannot call close before output has been started.\");\n }\n this._closingPromise = (async () => {\n await this._flushAndClose(false);\n this._closed = true;\n if (connectedTrack.output.state === \"finalizing\" || connectedTrack.output.state === \"finalized\") {\n return;\n }\n connectedTrack.output._muxer.onTrackClose(connectedTrack);\n })();\n }\n async _flushOrWaitForOngoingClose(forceClose) {\n return this._closingPromise ??= (async () => {\n await this._flushAndClose(forceClose);\n this._closed = true;\n })();\n }\n }\n\n class VideoSource extends MediaSource {\n constructor(codec) {\n super();\n if (!VIDEO_CODECS.includes(codec)) {\n throw new TypeError(`Invalid video codec '${codec}'. Must be one of: ${VIDEO_CODECS.join(\", \")}.`);\n }\n this._codec = codec;\n }\n }\n var maybeEnsureIsKeyPacket = (track, packet) => {\n if (track.metadata.hasOnlyKeyPackets && packet.type !== \"key\") {\n throw new Error(\"Cannot add non-key packets to a hasOnlyKeyPackets video track.\");\n }\n };\n class VideoEncoderWrapper {\n setError(error) {\n if (!this.errorSet) {\n this.error = error;\n this.errorSet = true;\n }\n }\n constructor(source, encodingConfig) {\n this.source = source;\n this.encodingConfig = encodingConfig;\n this.ensureEncoderPromise = null;\n this.encoderInitialized = false;\n this.encoder = null;\n this.encoderConfig = null;\n this.encodersReclaimed = false;\n this.muxer = null;\n this.lastMultipleOfKeyFrameInterval = -1;\n this.emittedEncoderPackets = 0;\n this.codedWidth = null;\n this.codedHeight = null;\n this.outputWidth = null;\n this.outputHeight = null;\n this.frameRateLastSample = null;\n this.frameRateLastTimestamp = null;\n this.frameRateLastEndTimestamp = null;\n this.preciseTimings = [];\n this.customEncoder = null;\n this.customEncoderCallSerializer = new CallSerializer;\n this.customEncoderQueueSize = 0;\n this.defaultEncodeOptions = {};\n this.alphaEncoder = null;\n this.splitter = null;\n this.splitterCreationFailed = false;\n this.alphaFrameQueue = [];\n this.error = null;\n this.errorSet = false;\n this.lastMuxerPromise = Promise.resolve();\n this.closed = false;\n }\n async add(videoSample, shouldClose, encodeOptions) {\n const originalSample = videoSample;\n try {\n this.checkForEncoderError();\n this.source._ensureValidAdd();\n const config = this.encodingConfig;\n const sizeChangeBehavior = config.sizeChangeBehavior ?? \"deny\";\n let isSizeChange = false;\n if (this.codedWidth !== null && this.codedHeight !== null) {\n if (videoSample.codedWidth !== this.codedWidth || videoSample.codedHeight !== this.codedHeight) {\n isSizeChange = true;\n if (sizeChangeBehavior === \"deny\") {\n throw new Error(`Video sample size must remain constant. Expected ${this.codedWidth}x${this.codedHeight},` + ` got ${videoSample.codedWidth}x${videoSample.codedHeight}. To allow the sample size to` + ` change over time, set \\`sizeChangeBehavior\\` to a value other than 'deny' in the` + ` encoding options.`);\n }\n }\n } else {\n this.codedWidth = videoSample.codedWidth;\n this.codedHeight = videoSample.codedHeight;\n }\n const hasTransformConfig = config.transform?.width !== undefined || config.transform?.height !== undefined || config.transform?.rotate !== undefined || config.transform?.flip !== undefined || config.transform?.crop !== undefined || config.transform?.force === true;\n const needsTransform = hasTransformConfig || isSizeChange && sizeChangeBehavior !== \"passThrough\";\n if (needsTransform) {\n let targetWidth = config.transform?.width;\n let targetHeight = config.transform?.height;\n let appliedFit = config.transform?.fit ?? \"fill\";\n if (isSizeChange && sizeChangeBehavior !== \"passThrough\") {\n assert(this.outputWidth);\n assert(this.outputHeight);\n assert(sizeChangeBehavior !== \"deny\");\n targetWidth = this.outputWidth;\n targetHeight = this.outputHeight;\n appliedFit = sizeChangeBehavior;\n }\n const transformed = await videoSample.transform({\n width: targetWidth,\n height: targetHeight,\n roundDimensionsTo: 2,\n crop: config.transform?.crop,\n rotate: config.transform?.rotate,\n flip: config.transform?.flip,\n fit: appliedFit,\n alpha: config.alpha\n });\n if (this.outputWidth === null || this.outputHeight === null) {\n this.outputWidth = transformed.displayWidth;\n this.outputHeight = transformed.displayHeight;\n }\n if (shouldClose) {\n videoSample.close();\n }\n videoSample = transformed;\n shouldClose = true;\n } else {\n if (this.outputWidth === null || this.outputHeight === null) {\n this.outputWidth = videoSample.codedWidth;\n this.outputHeight = videoSample.codedHeight;\n }\n }\n const frameRate = config.transform?.frameRate;\n if (frameRate !== undefined) {\n const originalEndTimestamp = videoSample.timestamp + videoSample.duration;\n const alignedTimestamp = floorToDivisor(videoSample.timestamp, frameRate);\n if (this.frameRateLastSample !== null) {\n if (alignedTimestamp <= this.frameRateLastTimestamp) {\n this.frameRateLastSample.close();\n this.frameRateLastSample = videoSample.clone();\n this.frameRateLastEndTimestamp = originalEndTimestamp;\n return;\n } else {\n await this.padFrameRate(alignedTimestamp, encodeOptions);\n }\n }\n if (videoSample === originalSample) {\n videoSample = videoSample.clone();\n shouldClose = true;\n }\n videoSample.setTimestamp(alignedTimestamp);\n videoSample.setDuration(1 / frameRate);\n this.frameRateLastSample?.close();\n this.frameRateLastSample = videoSample.clone();\n this.frameRateLastTimestamp = alignedTimestamp;\n this.frameRateLastEndTimestamp = originalEndTimestamp;\n }\n await this.processAndEncode(videoSample, encodeOptions);\n } finally {\n if (shouldClose) {\n videoSample.close();\n }\n }\n }\n async processAndEncode(videoSample, encodeOptions) {\n const config = this.encodingConfig;\n let samplesToEncode;\n if (config.transform?.process) {\n let processed = config.transform.process(videoSample);\n if (isThenable(processed)) {\n processed = await processed;\n }\n if (processed === null) {\n return;\n }\n if (!Array.isArray(processed)) {\n processed = [processed];\n }\n const mappedSamples = [];\n try {\n for (const x of processed) {\n if (x instanceof VideoSample) {\n mappedSamples.push(x);\n } else if (typeof VideoFrame !== \"undefined\" && x instanceof VideoFrame) {\n mappedSamples.push(new VideoSample(x));\n } else {\n mappedSamples.push(new VideoSample(x, {\n timestamp: videoSample.timestamp,\n duration: videoSample.duration\n }));\n }\n }\n } catch (error) {\n for (const sample of mappedSamples) {\n if (sample !== videoSample) {\n sample.close();\n }\n }\n for (const x of processed) {\n if (x instanceof VideoSample && x !== videoSample) {\n x.close();\n } else if (typeof VideoFrame !== \"undefined\" && x instanceof VideoFrame) {\n x.close();\n }\n }\n throw error;\n }\n samplesToEncode = mappedSamples;\n } else {\n samplesToEncode = [videoSample];\n }\n try {\n for (const sampleToEncode of samplesToEncode) {\n if (!this.encoderInitialized) {\n if (!this.ensureEncoderPromise) {\n this.ensureEncoder(sampleToEncode);\n }\n if (!this.encoderInitialized) {\n await this.ensureEncoderPromise;\n }\n }\n assert(this.encoderInitialized);\n if (this.closed) {\n break;\n }\n if (this.encodersReclaimed) {\n this.recreateWebCodecsEncoders();\n }\n const keyFrameInterval = this.encodingConfig.keyFrameInterval ?? 2;\n const multipleOfKeyFrameInterval = Math.floor(sampleToEncode.timestamp / keyFrameInterval);\n const mergedEncodeOptions = {\n ...this.defaultEncodeOptions,\n ...sampleToEncode.encodeOptions,\n ...encodeOptions\n };\n const finalEncodeOptions = {\n ...mergedEncodeOptions,\n keyFrame: mergedEncodeOptions.keyFrame !== undefined ? mergedEncodeOptions.keyFrame : keyFrameInterval === 0 || multipleOfKeyFrameInterval !== this.lastMultipleOfKeyFrameInterval\n };\n this.lastMultipleOfKeyFrameInterval = multipleOfKeyFrameInterval;\n this.encodingConfig.onEncodedSample?.(sampleToEncode);\n if (this.customEncoder) {\n this.customEncoderQueueSize++;\n const clonedSample = sampleToEncode.clone();\n const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample, finalEncodeOptions)).catch((error) => this.setError(error)).finally(() => {\n this.customEncoderQueueSize--;\n clonedSample.close();\n });\n if (this.customEncoderQueueSize >= 4) {\n await promise;\n }\n } else {\n assert(this.encoder);\n const videoFrame = sampleToEncode.toVideoFrame();\n const preciseTimingIndex = binarySearchLessOrEqual(this.preciseTimings, videoFrame.timestamp, (x) => x.microsecondTimestamp);\n const existingEntry = preciseTimingIndex !== -1 ? this.preciseTimings[preciseTimingIndex] : null;\n if (existingEntry && existingEntry.microsecondTimestamp === videoFrame.timestamp) {\n if (existingEntry.timestamp !== sampleToEncode.timestamp) {\n existingEntry.timestampIsValid = false;\n }\n if (existingEntry.duration !== sampleToEncode.duration) {\n existingEntry.durationIsValid = false;\n }\n } else {\n this.preciseTimings.splice(preciseTimingIndex + 1, 0, {\n microsecondTimestamp: videoFrame.timestamp,\n timestamp: sampleToEncode.timestamp,\n duration: sampleToEncode.duration,\n timestampIsValid: true,\n durationIsValid: true\n });\n if (this.preciseTimings.length > 128) {\n this.preciseTimings.shift();\n }\n }\n if (!this.alphaEncoder) {\n try {\n this.encoder.encode(videoFrame, finalEncodeOptions);\n } finally {\n videoFrame.close();\n }\n } else {\n const frameDefinitelyHasNoAlpha = !!videoFrame.format && !videoFrame.format.includes(\"A\");\n if (frameDefinitelyHasNoAlpha || this.splitterCreationFailed) {\n this.alphaFrameQueue.push(null);\n try {\n this.encoder.encode(videoFrame, finalEncodeOptions);\n } finally {\n videoFrame.close();\n }\n } else {\n if (!this.splitter) {\n this.splitter = new ColorAlphaSplitter;\n }\n const { colorFrame, alphaFrame } = await this.splitter.split(videoFrame);\n this.alphaFrameQueue.push(alphaFrame);\n try {\n this.encoder.encode(colorFrame, finalEncodeOptions);\n } finally {\n colorFrame.close();\n }\n }\n }\n if (this.encoder.encodeQueueSize >= 4) {\n await new Promise((resolve) => this.encoder.addEventListener(\"dequeue\", resolve, { once: true }));\n }\n }\n await this.lastMuxerPromise;\n }\n } finally {\n for (const sample of samplesToEncode) {\n if (sample !== videoSample) {\n sample.close();\n }\n }\n }\n }\n async padFrameRate(until, encodeOptions) {\n const frameRate = this.encodingConfig.transform.frameRate;\n assert(this.frameRateLastSample);\n const frameDifference = Math.round((until - this.frameRateLastTimestamp) * frameRate);\n for (let i = 1;i < frameDifference; i++) {\n const env_1 = { stack: [], error: undefined, hasError: false };\n try {\n const sample = __addDisposableResource2(env_1, this.frameRateLastSample.clone(), false);\n sample.setTimestamp(this.frameRateLastTimestamp + i / frameRate);\n sample.setDuration(1 / frameRate);\n await this.processAndEncode(sample, encodeOptions);\n } catch (e_1) {\n env_1.error = e_1;\n env_1.hasError = true;\n } finally {\n __disposeResources2(env_1);\n }\n }\n }\n ensureEncoder(videoSample) {\n this.ensureEncoderPromise = (async () => {\n const quality = resolveQuality(this.encodingConfig.quality, this.encodingConfig.bitrate);\n assert(quality !== undefined);\n const candidates = buildVideoEncoderConfigs({\n ...this.encodingConfig,\n quality,\n width: videoSample.codedWidth,\n height: videoSample.codedHeight,\n squarePixelWidth: videoSample.squarePixelWidth,\n squarePixelHeight: videoSample.squarePixelHeight,\n framerate: this.source._connectedTrack?.metadata.frameRate\n });\n let selected = null;\n let MatchingCustomEncoder;\n for (const candidate of candidates) {\n const candidateConfig = candidate.config;\n this.encodingConfig.onEncoderConfig?.(candidateConfig);\n MatchingCustomEncoder = customVideoEncoders.find((x) => x.supports(this.encodingConfig.codec, candidateConfig));\n if (MatchingCustomEncoder) {\n selected = candidate;\n break;\n }\n if (typeof VideoEncoder === \"undefined\") {\n continue;\n }\n candidateConfig.alpha = \"discard\";\n if (this.encodingConfig.alpha === \"keep\") {\n candidateConfig.latencyMode = \"quality\";\n }\n const hasOddDimension = candidateConfig.width % 2 === 1 || candidateConfig.height % 2 === 1;\n if (hasOddDimension && (this.encodingConfig.codec === \"avc\" || this.encodingConfig.codec === \"hevc\")) {\n throw new Error(`The dimensions ${candidateConfig.width}x${candidateConfig.height} are not supported for codec` + ` '${this.encodingConfig.codec}'; both width and height must be even numbers. Make sure to` + ` round your dimensions to the nearest even number.`);\n }\n try {\n const support = await VideoEncoder.isConfigSupported(candidateConfig);\n if (support.supported) {\n selected = candidate;\n break;\n }\n } catch {}\n }\n if (!selected) {\n if (typeof VideoEncoder === \"undefined\") {\n throw new Error(missingWebCodecsClassMessage(\"VideoEncoder\"));\n }\n const firstConfig = candidates[0].config;\n const rateControls = candidates.map(({ config, quantizer }) => quantizer !== null ? `quantizer ${quantizer}` : `${config.bitrate} bps`);\n throw new Error(`This specific encoder configuration (${firstConfig.codec}, ${rateControls.join(\" / \")},` + ` ${firstConfig.width}x${firstConfig.height}, hardware acceleration:` + ` ${firstConfig.hardwareAcceleration ?? \"no-preference\"}) is not supported in this environment.` + ` Consider using another codec or changing your video parameters.`);\n }\n const encoderConfig = selected.config;\n if (selected.quantizer !== null) {\n this.defaultEncodeOptions = buildQuantizerEncodeOptions(this.encodingConfig.codec, selected.quantizer);\n } else {\n this.source._nominalBitrate = encoderConfig.bitrate ?? null;\n }\n if (MatchingCustomEncoder) {\n this.customEncoder = new MatchingCustomEncoder;\n this.customEncoder.codec = this.encodingConfig.codec;\n this.customEncoder.config = encoderConfig;\n this.customEncoder.onPacket = (packet, meta) => {\n if (!(packet instanceof EncodedPacket)) {\n throw new TypeError(\"The first argument passed to onPacket must be an EncodedPacket.\");\n }\n if (meta !== undefined && (!meta || typeof meta !== \"object\")) {\n throw new TypeError(\"The second argument passed to onPacket must be an object or undefined.\");\n }\n maybeEnsureIsKeyPacket(this.source._connectedTrack, packet);\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n this.lastMuxerPromise = this.muxer.addEncodedVideoPacket(this.source._connectedTrack, packet, meta).catch((error) => {\n this.setError(error);\n });\n };\n this.customEncoder.onError = (error) => {\n this.setError(error);\n };\n await this.customEncoder.init();\n } else {\n this.encoderConfig = encoderConfig;\n this.createWebCodecsEncoders();\n }\n assert(this.source._connectedTrack);\n this.muxer = this.source._connectedTrack.output._muxer;\n this.encoderInitialized = true;\n })();\n }\n createWebCodecsEncoders() {\n const encoderConfig = this.encoderConfig;\n assert(encoderConfig);\n const colorChunkQueue = [];\n const nullAlphaChunkQueue = [];\n let encodedAlphaChunkCount = 0;\n let alphaEncoderQueue = 0;\n const handleError = (error, stack) => {\n if (isCodecReclaimedError(error)) {\n this.encodersReclaimed = true;\n return;\n }\n error.stack = stack;\n this.setError(error);\n };\n const stack = new Error(\"Encoding error\").stack;\n this.encoder = new VideoEncoder({\n output: (chunk, meta) => {\n if (!this.alphaEncoder) {\n this.addPacket(chunk, null, meta);\n return;\n }\n const alphaFrame = this.alphaFrameQueue.shift();\n assert(alphaFrame !== undefined);\n if (alphaFrame) {\n this.alphaEncoder.encode(alphaFrame, {\n ...this.defaultEncodeOptions,\n keyFrame: chunk.type === \"key\"\n });\n alphaEncoderQueue++;\n alphaFrame.close();\n colorChunkQueue.push({ chunk, meta });\n } else {\n if (alphaEncoderQueue === 0) {\n this.addPacket(chunk, null, meta);\n } else {\n nullAlphaChunkQueue.push(encodedAlphaChunkCount + alphaEncoderQueue);\n colorChunkQueue.push({ chunk, meta });\n }\n }\n },\n error: (error) => handleError(error, stack)\n });\n this.encoder.configure(encoderConfig);\n if (this.encodingConfig.alpha === \"keep\") {\n const stack = new Error(\"Encoding error\").stack;\n this.alphaEncoder = new VideoEncoder({\n output: (chunk, meta) => {\n alphaEncoderQueue--;\n const colorChunk = colorChunkQueue.shift();\n assert(colorChunk !== undefined);\n this.addPacket(colorChunk.chunk, chunk, colorChunk.meta);\n encodedAlphaChunkCount++;\n while (nullAlphaChunkQueue.length > 0 && nullAlphaChunkQueue[0] === encodedAlphaChunkCount) {\n nullAlphaChunkQueue.shift();\n const colorChunk = colorChunkQueue.shift();\n assert(colorChunk !== undefined);\n this.addPacket(colorChunk.chunk, null, colorChunk.meta);\n }\n },\n error: (error) => handleError(error, stack)\n });\n this.alphaEncoder.configure(encoderConfig);\n }\n }\n addPacket(colorChunk, alphaChunk, meta) {\n const sideData = {};\n if (alphaChunk) {\n const alphaData = new Uint8Array(alphaChunk.byteLength);\n alphaChunk.copyTo(alphaData);\n sideData.alpha = alphaData;\n }\n let packet = EncodedPacket.fromEncodedChunk(colorChunk, sideData);\n const encoderConfig = this.encoderConfig;\n assert(encoderConfig);\n if (meta?.decoderConfig) {\n meta.decoderConfig.codec = extractVideoCodecString({\n width: encoderConfig.width,\n height: encoderConfig.height,\n codec: this.encodingConfig.codec,\n codecDescription: meta.decoderConfig.description ? toUint8Array(meta.decoderConfig.description) : null,\n colorSpace: meta.decoderConfig.colorSpace ?? null,\n avcType: encoderConfig.codec.includes(\"avc1.\") ? 1 : encoderConfig.codec.includes(\"avc3.\") ? 3 : null,\n avcCodecInfo: this.encodingConfig.codec === \"avc\" && !meta.decoderConfig.description ? extractAvcDecoderConfigurationRecord(packet.data) : null,\n hevcCodecInfo: this.encodingConfig.codec === \"hevc\" && !meta.decoderConfig.description ? extractHevcDecoderConfigurationRecord(packet.data) : null,\n vp9CodecInfo: this.encodingConfig.codec === \"vp9\" ? extractVp9CodecInfoFromPacket(packet.data) : null,\n av1CodecInfo: this.encodingConfig.codec === \"av1\" ? extractAv1CodecInfoFromPacket(packet.data) : null,\n proresFormat: null\n });\n }\n const preciseTimingIndex = binarySearchLessOrEqual(this.preciseTimings, colorChunk.timestamp, (x) => x.microsecondTimestamp);\n const entry = preciseTimingIndex !== -1 ? this.preciseTimings[preciseTimingIndex] : null;\n let actualType = null;\n if (this.emittedEncoderPackets === 0 && packet.type === \"delta\" && meta?.decoderConfig) {\n actualType = determineVideoPacketType(this.encodingConfig.codec, meta.decoderConfig, packet.data);\n }\n if (entry && entry.microsecondTimestamp === colorChunk.timestamp || actualType !== null) {\n packet = packet.clone({\n timestamp: entry?.timestampIsValid ? entry.timestamp : undefined,\n duration: entry?.durationIsValid ? entry.duration : undefined,\n type: actualType ?? undefined\n });\n }\n maybeEnsureIsKeyPacket(this.source._connectedTrack, packet);\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n this.lastMuxerPromise = this.muxer.addEncodedVideoPacket(this.source._connectedTrack, packet, meta).catch((error) => {\n this.setError(error);\n });\n this.emittedEncoderPackets++;\n }\n recreateWebCodecsEncoders() {\n assert(this.encoder);\n if (this.encoder.state !== \"closed\") {\n this.encoder.close();\n }\n if (this.alphaEncoder && this.alphaEncoder.state !== \"closed\") {\n this.alphaEncoder.close();\n }\n this.alphaFrameQueue.forEach((x) => x?.close());\n this.alphaFrameQueue.length = 0;\n this.encodersReclaimed = false;\n this.lastMultipleOfKeyFrameInterval = -1;\n this.createWebCodecsEncoders();\n }\n async flushAndClose(forceClose) {\n try {\n if (!forceClose) {\n this.checkForEncoderError();\n if (this.frameRateLastSample) {\n const frameRate = this.encodingConfig.transform.frameRate;\n const alignedEnd = floorToDivisor(this.frameRateLastEndTimestamp, frameRate);\n await this.padFrameRate(alignedEnd);\n }\n }\n this.closed = true;\n if (!forceClose) {\n if (this.customEncoder) {\n this.customEncoderCallSerializer.call(() => this.customEncoder.flush());\n } else if (this.encoder && !this.encodersReclaimed) {\n await this.encoder.flush();\n await this.alphaEncoder?.flush();\n await wait(25);\n }\n }\n } finally {\n this.closed = true;\n this.frameRateLastSample?.close();\n this.frameRateLastSample = null;\n if (this.customEncoder) {\n await this.customEncoderCallSerializer.call(() => this.customEncoder.close()).catch((error) => this.setError(error));\n } else if (this.encoder) {\n if (this.encoder.state !== \"closed\") {\n this.encoder.close();\n }\n if (this.alphaEncoder && this.alphaEncoder.state !== \"closed\") {\n this.alphaEncoder.close();\n }\n this.alphaFrameQueue.forEach((x) => x?.close());\n this.alphaFrameQueue.length = 0;\n this.splitter?.close();\n }\n }\n if (!forceClose) {\n this.checkForEncoderError();\n }\n }\n getQueueSize() {\n if (this.customEncoder) {\n return this.customEncoderQueueSize;\n } else {\n return this.encoder?.encodeQueueSize ?? 0;\n }\n }\n checkForEncoderError() {\n if (this.errorSet) {\n throw this.error;\n }\n }\n }\n var splitterWorkerUrl = null;\n\n class ColorAlphaSplitter {\n constructor() {\n this.worker = null;\n this.pendingRequests = new Map;\n this.nextRequestId = 0;\n }\n split(sourceFrame) {\n if (!this.worker) {\n if (!splitterWorkerUrl) {\n const blob = new Blob([`(${colorAlphaSplitterWorkerCode.toString()})()`], { type: \"application/javascript\" });\n splitterWorkerUrl = URL.createObjectURL(blob);\n }\n this.worker = new Worker(splitterWorkerUrl);\n this.worker.addEventListener(\"message\", (event) => {\n const data = event.data;\n const pending = this.pendingRequests.get(data.id);\n if (!pending) {\n return;\n }\n this.pendingRequests.delete(data.id);\n if (\"error\" in data) {\n pending.reject(new Error(data.error));\n } else {\n pending.resolve({ colorFrame: data.colorFrame, alphaFrame: data.alphaFrame });\n }\n });\n this.worker.addEventListener(\"error\", (event) => {\n const error = new Error(event.message || \"Color/alpha splitter worker error.\");\n for (const pending of this.pendingRequests.values()) {\n pending.reject(error);\n }\n this.pendingRequests.clear();\n });\n }\n const id = this.nextRequestId++;\n const pending = promiseWithResolvers();\n this.pendingRequests.set(id, pending);\n this.worker.postMessage({ id, sourceFrame }, { transfer: [sourceFrame] });\n return pending.promise;\n }\n close() {\n this.worker?.terminate();\n this.worker = null;\n const error = new Error(\"Color/alpha splitter closed.\");\n for (const pending of this.pendingRequests.values()) {\n pending.reject(error);\n }\n this.pendingRequests.clear();\n }\n }\n var colorAlphaSplitterWorkerCode = () => {\n let cpuSourceBuffer = null;\n let chain = Promise.resolve();\n self.addEventListener(\"message\", (event) => {\n const { id, sourceFrame } = event.data;\n chain = chain.then(async () => {\n try {\n const { colorFrame, alphaFrame } = await split(sourceFrame);\n self.postMessage({ id, colorFrame, alphaFrame }, { transfer: [colorFrame, alphaFrame] });\n } catch (error) {\n self.postMessage({ id, error: error.message });\n } finally {\n sourceFrame.close();\n }\n });\n });\n const split = async (sourceFrame) => {\n const format = sourceFrame.format;\n if (!format) {\n throw new Error(\"CPU color/alpha splitting requires a known VideoFrame format.\");\n }\n const sourceSize = sourceFrame.allocationSize();\n if (!cpuSourceBuffer || cpuSourceBuffer.byteLength !== sourceSize) {\n cpuSourceBuffer = new Uint8Array(sourceSize);\n }\n await sourceFrame.copyTo(cpuSourceBuffer);\n if (format === \"RGBA\" || format === \"BGRA\") {\n return splitInterleavedRgba(cpuSourceBuffer, format, sourceFrame);\n } else if (format === \"I420A\" || format === \"I420AP10\" || format === \"I420AP12\" || format === \"I422A\" || format === \"I422AP10\" || format === \"I422AP12\" || format === \"I444A\" || format === \"I444AP10\" || format === \"I444AP12\") {\n return splitPlanarYuvA(cpuSourceBuffer, format, sourceFrame);\n }\n throw new Error(`CPU color/alpha splitting does not support format '${format}'.`);\n };\n const splitInterleavedRgba = (source, format, sourceFrame) => {\n const width = sourceFrame.visibleRect?.width ?? sourceFrame.codedWidth;\n const height = sourceFrame.visibleRect?.height ?? sourceFrame.codedHeight;\n const pixelCount = width * height;\n const chromaW = Math.ceil(width / 2);\n const chromaH = Math.ceil(height / 2);\n const alphaSize = pixelCount + chromaW * chromaH * 2;\n const alphaBuffer = new Uint8Array(alphaSize);\n for (let i = 0, j = 3;i < pixelCount; i++, j += 4) {\n alphaBuffer[i] = source[j];\n }\n alphaBuffer.fill(128, pixelCount);\n const colorFrame = new VideoFrame(source, {\n format: format === \"RGBA\" ? \"RGBX\" : \"BGRX\",\n codedWidth: width,\n codedHeight: height,\n timestamp: sourceFrame.timestamp,\n duration: sourceFrame.duration ?? undefined\n });\n const alphaInit = {\n format: \"I420\",\n codedWidth: width,\n codedHeight: height,\n timestamp: sourceFrame.timestamp,\n duration: sourceFrame.duration ?? undefined,\n colorSpace: {\n fullRange: true,\n matrix: \"bt709\",\n primaries: \"bt709\",\n transfer: \"bt709\"\n },\n transfer: [alphaBuffer.buffer]\n };\n const alphaFrame = new VideoFrame(alphaBuffer, alphaInit);\n return { colorFrame, alphaFrame };\n };\n const splitPlanarYuvA = (source, format, sourceFrame) => {\n const width = sourceFrame.visibleRect?.width ?? sourceFrame.codedWidth;\n const height = sourceFrame.visibleRect?.height ?? sourceFrame.codedHeight;\n const is10 = format.includes(\"P10\");\n const is12 = format.includes(\"P12\");\n const bytesPerSample = is10 || is12 ? 2 : 1;\n let chromaW;\n let chromaH;\n if (format.startsWith(\"I420\")) {\n chromaW = Math.ceil(width / 2);\n chromaH = Math.ceil(height / 2);\n } else if (format.startsWith(\"I422\")) {\n chromaW = Math.ceil(width / 2);\n chromaH = height;\n } else {\n chromaW = width;\n chromaH = height;\n }\n const ySamples = width * height;\n const uvSamples = chromaW * chromaH;\n const yBytes = ySamples * bytesPerSample;\n const uvBytes = uvSamples * bytesPerSample;\n const aBytes = ySamples * bytesPerSample;\n const colorBytes = yBytes + uvBytes * 2;\n const colorFormat = format.replace(\"A\", \"\");\n const alphaChromaW = Math.ceil(width / 2);\n const alphaChromaH = Math.ceil(height / 2);\n const alphaUvSamples = alphaChromaW * alphaChromaH;\n const alphaUvBytes = alphaUvSamples * bytesPerSample;\n const alphaSize = aBytes + 2 * alphaUvBytes;\n const alphaBuffer = new Uint8Array(alphaSize);\n const aPlaneStart = colorBytes;\n alphaBuffer.set(source.subarray(aPlaneStart, aPlaneStart + aBytes), 0);\n const uvOffset = aBytes;\n const neutralChroma = is10 ? 512 : is12 ? 2048 : 128;\n if (bytesPerSample === 1) {\n alphaBuffer.fill(neutralChroma, uvOffset);\n } else {\n const uvView = new Uint16Array(alphaBuffer.buffer, uvOffset, 2 * alphaUvSamples);\n uvView.fill(neutralChroma);\n }\n const alphaFormat = is10 ? \"I420P10\" : is12 ? \"I420P12\" : \"I420\";\n const colorFrame = new VideoFrame(source.subarray(0, colorBytes), {\n format: colorFormat,\n codedWidth: width,\n codedHeight: height,\n timestamp: sourceFrame.timestamp,\n duration: sourceFrame.duration ?? undefined\n });\n const alphaInit = {\n format: alphaFormat,\n codedWidth: width,\n codedHeight: height,\n timestamp: sourceFrame.timestamp,\n duration: sourceFrame.duration ?? undefined,\n colorSpace: {\n fullRange: true,\n matrix: \"bt709\",\n primaries: \"bt709\",\n transfer: \"bt709\"\n },\n transfer: [alphaBuffer.buffer]\n };\n const alphaFrame = new VideoFrame(alphaBuffer, alphaInit);\n return { colorFrame, alphaFrame };\n };\n };\n\n class VideoSampleSource extends VideoSource {\n constructor(encodingConfig) {\n validateVideoEncodingConfig(encodingConfig);\n super(encodingConfig.codec);\n this._encoder = new VideoEncoderWrapper(this, encodingConfig);\n }\n add(videoSample, encodeOptions) {\n if (!(videoSample instanceof VideoSample)) {\n throw new TypeError(\"videoSample must be a VideoSample.\");\n }\n return this._encoder.add(videoSample, false, encodeOptions);\n }\n _flushAndClose(forceClose) {\n return this._encoder.flushAndClose(forceClose);\n }\n }\n class AudioSource extends MediaSource {\n constructor(codec) {\n super();\n if (!AUDIO_CODECS.includes(codec)) {\n throw new TypeError(`Invalid audio codec '${codec}'. Must be one of: ${AUDIO_CODECS.join(\", \")}.`);\n }\n this._codec = codec;\n }\n }\n class AudioEncoderWrapper {\n setError(error) {\n if (!this.errorSet) {\n this.error = error;\n this.errorSet = true;\n }\n }\n constructor(source, encodingConfig) {\n this.source = source;\n this.encodingConfig = encodingConfig;\n this.ensureEncoderPromise = null;\n this.encoderInitialized = false;\n this.encoder = null;\n this.muxer = null;\n this.lastNumberOfChannels = null;\n this.lastSampleRate = null;\n this.isPcmEncoder = false;\n this.outputSampleSize = null;\n this.writeOutputValue = null;\n this.customEncoder = null;\n this.customEncoderCallSerializer = new CallSerializer;\n this.customEncoderQueueSize = 0;\n this.lastEndSampleIndex = null;\n this.resampler = null;\n this.error = null;\n this.errorSet = false;\n this.lastMuxerPromise = Promise.resolve();\n this.closed = false;\n }\n async add(audioSample, shouldClose) {\n try {\n this.checkForEncoderError();\n this.source._ensureValidAdd();\n if (this.lastNumberOfChannels !== null && this.lastSampleRate !== null) {\n if (audioSample.numberOfChannels !== this.lastNumberOfChannels || audioSample.sampleRate !== this.lastSampleRate) {\n throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at` + ` ${this.lastSampleRate} Hz, got ${audioSample.numberOfChannels} channels at` + ` ${audioSample.sampleRate} Hz.`);\n }\n } else {\n this.lastNumberOfChannels = audioSample.numberOfChannels;\n this.lastSampleRate = audioSample.sampleRate;\n }\n const config = this.encodingConfig;\n const needsResample = config.transform?.numberOfChannels !== undefined || config.transform?.sampleRate !== undefined;\n if (needsResample) {\n if (!this.resampler) {\n this.resampler = new AudioResampler({\n targetNumberOfChannels: config.transform.numberOfChannels ?? audioSample.numberOfChannels,\n targetSampleRate: config.transform.sampleRate ?? audioSample.sampleRate,\n onSample: async (sample) => {\n await this.processAndEncode(sample, true);\n }\n });\n }\n await this.resampler.add(audioSample);\n } else {\n await this.processAndEncode(audioSample, shouldClose);\n }\n } finally {\n if (shouldClose) {\n audioSample.close();\n }\n }\n }\n async processAndEncode(audioSample, shouldClose) {\n const config = this.encodingConfig;\n if (config.transform?.sampleFormat !== undefined && toInterleavedAudioFormat(audioSample.format) !== config.transform.sampleFormat) {\n const newSample = audioSampleToInterleavedFormat(audioSample, config.transform.sampleFormat);\n if (shouldClose) {\n audioSample.close();\n }\n audioSample = newSample;\n shouldClose = true;\n }\n if (config.transform?.process) {\n try {\n let processed = config.transform.process(audioSample);\n if (isThenable(processed)) {\n processed = await processed;\n }\n if (processed === null) {\n return;\n }\n if (!Array.isArray(processed)) {\n processed = [processed];\n }\n try {\n for (const sample of processed) {\n if (!(sample instanceof AudioSample)) {\n throw new TypeError(\"The audio process function must return an AudioSample, null, or an array of\" + \" AudioSamples.\");\n }\n }\n for (const sample of processed) {\n await this.encodeSample(sample, true);\n }\n } finally {\n for (const sample of processed) {\n if (sample instanceof AudioSample) {\n sample.close();\n }\n }\n }\n } finally {\n if (shouldClose) {\n audioSample.close();\n }\n }\n } else {\n await this.encodeSample(audioSample, shouldClose);\n }\n }\n async encodeSample(audioSample, shouldClose) {\n try {\n if (!this.encoderInitialized) {\n if (!this.ensureEncoderPromise) {\n this.ensureEncoder(audioSample);\n }\n if (!this.encoderInitialized) {\n await this.ensureEncoderPromise;\n }\n }\n assert(this.encoderInitialized);\n if (this.closed) {\n return;\n }\n {\n const startSampleIndex = Math.round(audioSample.timestamp * audioSample.sampleRate);\n const endSampleIndex = Math.round((audioSample.timestamp + audioSample.duration) * audioSample.sampleRate);\n if (this.lastEndSampleIndex === null) {\n this.lastEndSampleIndex = endSampleIndex;\n } else {\n const sampleDiff = startSampleIndex - this.lastEndSampleIndex;\n if (sampleDiff >= 64) {\n const fillSample = new AudioSample({\n data: new Float32Array(sampleDiff * audioSample.numberOfChannels),\n format: \"f32-planar\",\n sampleRate: audioSample.sampleRate,\n numberOfChannels: audioSample.numberOfChannels,\n numberOfFrames: sampleDiff,\n timestamp: this.lastEndSampleIndex / audioSample.sampleRate\n });\n await this.encodeSample(fillSample, true);\n }\n this.lastEndSampleIndex += audioSample.numberOfFrames;\n }\n }\n this.encodingConfig.onEncodedSample?.(audioSample);\n if (this.customEncoder) {\n this.customEncoderQueueSize++;\n const clonedSample = audioSample.clone();\n const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample)).catch((error) => this.setError(error)).finally(() => {\n this.customEncoderQueueSize--;\n clonedSample.close();\n });\n if (this.customEncoderQueueSize >= 4) {\n await promise;\n }\n await this.lastMuxerPromise;\n } else if (this.isPcmEncoder) {\n await this.doPcmEncoding(audioSample, shouldClose);\n } else {\n assert(this.encoder);\n const audioData = audioSample.toAudioData();\n this.encoder.encode(audioData);\n audioData.close();\n if (shouldClose) {\n audioSample.close();\n }\n if (this.encoder.encodeQueueSize >= 4) {\n await new Promise((resolve) => this.encoder.addEventListener(\"dequeue\", resolve, { once: true }));\n }\n await this.lastMuxerPromise;\n }\n } finally {\n if (shouldClose) {\n audioSample.close();\n }\n }\n }\n async doPcmEncoding(audioSample, shouldClose) {\n assert(this.outputSampleSize);\n assert(this.writeOutputValue);\n const { numberOfChannels, numberOfFrames, sampleRate, timestamp } = audioSample;\n const CHUNK_SIZE = 2048;\n const outputs = [];\n for (let frame = 0;frame < numberOfFrames; frame += CHUNK_SIZE) {\n const frameCount = Math.min(CHUNK_SIZE, audioSample.numberOfFrames - frame);\n const outputSize = frameCount * numberOfChannels * this.outputSampleSize;\n const outputBuffer = new ArrayBuffer(outputSize);\n const outputView = new DataView(outputBuffer);\n outputs.push({ frameCount, view: outputView });\n }\n const isS32 = audioSample.format === \"s32\" || audioSample.format === \"s32-planar\";\n const readFormat = isS32 ? \"s32-planar\" : \"f32-planar\";\n const scale = isS32 ? 1 / 2147483648 : 1;\n const allocationSize = audioSample.allocationSize({ planeIndex: 0, format: readFormat });\n const values = isS32 ? new Int32Array(allocationSize / 4) : new Float32Array(allocationSize / 4);\n for (let i = 0;i < numberOfChannels; i++) {\n audioSample.copyTo(values, { planeIndex: i, format: readFormat });\n for (let j = 0;j < outputs.length; j++) {\n const { frameCount, view } = outputs[j];\n for (let k = 0;k < frameCount; k++) {\n this.writeOutputValue(view, (k * numberOfChannels + i) * this.outputSampleSize, values[j * CHUNK_SIZE + k] * scale);\n }\n }\n }\n if (shouldClose) {\n audioSample.close();\n }\n const meta = {\n decoderConfig: {\n codec: this.encodingConfig.codec,\n numberOfChannels,\n sampleRate\n }\n };\n for (let i = 0;i < outputs.length; i++) {\n const { frameCount, view } = outputs[i];\n const outputBuffer = view.buffer;\n const startFrame = i * CHUNK_SIZE;\n const packet = new EncodedPacket(new Uint8Array(outputBuffer), \"key\", timestamp + startFrame / sampleRate, frameCount / sampleRate);\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n await this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta);\n }\n }\n ensureEncoder(audioSample) {\n this.ensureEncoderPromise = (async () => {\n const { numberOfChannels, sampleRate } = audioSample;\n const quality = resolveQuality(this.encodingConfig.quality, this.encodingConfig.bitrate);\n const encoderConfig = buildAudioEncoderConfig({\n numberOfChannels,\n sampleRate,\n ...this.encodingConfig,\n quality\n });\n this.encodingConfig.onEncoderConfig?.(encoderConfig);\n this.source._nominalBitrate = encoderConfig.bitrate ?? null;\n const MatchingCustomEncoder = customAudioEncoders.find((x) => x.supports(this.encodingConfig.codec, encoderConfig));\n if (MatchingCustomEncoder) {\n this.customEncoder = new MatchingCustomEncoder;\n this.customEncoder.codec = this.encodingConfig.codec;\n this.customEncoder.config = encoderConfig;\n this.customEncoder.onPacket = (packet, meta) => {\n if (!(packet instanceof EncodedPacket)) {\n throw new TypeError(\"The first argument passed to onPacket must be an EncodedPacket.\");\n }\n if (meta !== undefined && (!meta || typeof meta !== \"object\")) {\n throw new TypeError(\"The second argument passed to onPacket must be an object or undefined.\");\n }\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n this.lastMuxerPromise = this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta).catch((error) => {\n this.setError(error);\n });\n };\n this.customEncoder.onError = (error) => {\n this.setError(error);\n };\n await this.customEncoder.init();\n } else if (PCM_AUDIO_CODECS.includes(this.encodingConfig.codec)) {\n this.initPcmEncoder();\n } else {\n if (typeof AudioEncoder === \"undefined\") {\n throw new Error(missingWebCodecsClassMessage(\"AudioEncoder\"));\n }\n let supported;\n try {\n const support = await AudioEncoder.isConfigSupported(encoderConfig);\n supported = support.supported ?? false;\n } catch {\n supported = false;\n }\n if (!supported) {\n throw new Error(`This specific encoder configuration (${encoderConfig.codec}, ${encoderConfig.bitrate} bps,` + ` ${encoderConfig.numberOfChannels} channels, ${encoderConfig.sampleRate} Hz) is not` + ` supported in this environment. Consider using another codec or changing your` + ` audio parameters.`);\n }\n const stack = new Error(\"Encoding error\").stack;\n this.encoder = new AudioEncoder({\n output: (chunk, meta) => {\n if (this.encodingConfig.codec === \"aac\" && meta?.decoderConfig) {\n let needsDescriptionOverwrite = false;\n if (!meta.decoderConfig.description || meta.decoderConfig.description.byteLength < 2) {\n needsDescriptionOverwrite = true;\n } else {\n const audioSpecificConfig = parseAacAudioSpecificConfig(toUint8Array(meta.decoderConfig.description));\n needsDescriptionOverwrite = audioSpecificConfig.objectType === 0;\n }\n if (needsDescriptionOverwrite) {\n const objectType = Number(last(encoderConfig.codec.split(\".\")));\n meta.decoderConfig.description = buildAacAudioSpecificConfig({\n objectType,\n outputNumberOfChannels: meta.decoderConfig.numberOfChannels,\n outputSampleRate: meta.decoderConfig.sampleRate\n });\n }\n }\n let packet = EncodedPacket.fromEncodedChunk(chunk);\n packet = packet.clone({\n timestamp: roundToDivisor(packet.timestamp, encoderConfig.sampleRate),\n duration: chunk.duration != null ? roundToDivisor(packet.duration, encoderConfig.sampleRate) : undefined\n });\n this.encodingConfig.onEncodedPacket?.(packet, meta);\n this.lastMuxerPromise = this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta).catch((error) => {\n this.setError(error);\n });\n },\n error: (error) => {\n error.stack = stack;\n this.setError(error);\n }\n });\n this.encoder.configure(encoderConfig);\n }\n assert(this.source._connectedTrack);\n this.muxer = this.source._connectedTrack.output._muxer;\n this.encoderInitialized = true;\n })();\n }\n initPcmEncoder() {\n this.isPcmEncoder = true;\n const codec = this.encodingConfig.codec;\n const { dataType, sampleSize, littleEndian } = parsePcmCodec(codec);\n this.outputSampleSize = sampleSize;\n switch (sampleSize) {\n case 1:\n {\n if (dataType === \"unsigned\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setUint8(byteOffset, clamp(Math.round(value * 128) + 128, 0, 255));\n } else if (dataType === \"signed\") {\n this.writeOutputValue = (view, byteOffset, value) => {\n view.setInt8(byteOffset, clamp(Math.round(value * 128), -128, 127));\n };\n } else if (dataType === \"ulaw\") {\n this.writeOutputValue = (view, byteOffset, value) => {\n const int16 = clamp(Math.round(value * 32768), -32768, 32767);\n view.setUint8(byteOffset, toUlaw(int16));\n };\n } else if (dataType === \"alaw\") {\n this.writeOutputValue = (view, byteOffset, value) => {\n const int16 = clamp(Math.round(value * 32768), -32768, 32767);\n view.setUint8(byteOffset, toAlaw(int16));\n };\n } else {\n assert(false);\n }\n }\n ;\n break;\n case 2:\n {\n if (dataType === \"unsigned\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setUint16(byteOffset, clamp(Math.round(value * 32768) + 32768, 0, 65535), littleEndian);\n } else if (dataType === \"signed\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setInt16(byteOffset, clamp(Math.round(value * 32768), -32768, 32767), littleEndian);\n } else {\n assert(false);\n }\n }\n ;\n break;\n case 3:\n {\n if (dataType === \"unsigned\") {\n this.writeOutputValue = (view, byteOffset, value) => setUint24(view, byteOffset, clamp(Math.round(value * 8388608) + 8388608, 0, 16777215), littleEndian);\n } else if (dataType === \"signed\") {\n this.writeOutputValue = (view, byteOffset, value) => setInt24(view, byteOffset, clamp(Math.round(value * 8388608), -8388608, 8388607), littleEndian);\n } else {\n assert(false);\n }\n }\n ;\n break;\n case 4:\n {\n if (dataType === \"unsigned\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setUint32(byteOffset, clamp(Math.round(value * 2147483648) + 2147483648, 0, 4294967295), littleEndian);\n } else if (dataType === \"signed\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setInt32(byteOffset, clamp(Math.round(value * 2147483648), -2147483648, 2147483647), littleEndian);\n } else if (dataType === \"float\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setFloat32(byteOffset, value, littleEndian);\n } else {\n assert(false);\n }\n }\n ;\n break;\n case 8:\n {\n if (dataType === \"float\") {\n this.writeOutputValue = (view, byteOffset, value) => view.setFloat64(byteOffset, value, littleEndian);\n } else {\n assert(false);\n }\n }\n ;\n break;\n default:\n {\n assertNever(sampleSize);\n assert(false);\n }\n ;\n }\n }\n async flushAndClose(forceClose) {\n try {\n if (!forceClose) {\n this.checkForEncoderError();\n if (this.resampler) {\n await this.resampler.finalize();\n }\n }\n this.closed = true;\n if (!forceClose) {\n if (this.customEncoder) {\n this.customEncoderCallSerializer.call(() => this.customEncoder.flush());\n } else if (this.encoder) {\n await this.encoder.flush();\n }\n }\n } finally {\n this.closed = true;\n this.resampler = null;\n if (this.customEncoder) {\n await this.customEncoderCallSerializer.call(() => this.customEncoder.close()).catch((error) => this.setError(error));\n } else if (this.encoder && this.encoder.state !== \"closed\") {\n this.encoder.close();\n }\n }\n if (!forceClose) {\n this.checkForEncoderError();\n }\n }\n getQueueSize() {\n if (this.customEncoder) {\n return this.customEncoderQueueSize;\n } else if (this.isPcmEncoder) {\n return 0;\n } else {\n return this.encoder?.encodeQueueSize ?? 0;\n }\n }\n checkForEncoderError() {\n if (this.errorSet) {\n throw this.error;\n }\n }\n }\n\n class AudioSampleSource extends AudioSource {\n constructor(encodingConfig) {\n validateAudioEncodingConfig(encodingConfig);\n super(encodingConfig.codec);\n this._encoder = new AudioEncoderWrapper(this, encodingConfig);\n }\n add(audioSample) {\n if (!(audioSample instanceof AudioSample)) {\n throw new TypeError(\"audioSample must be an AudioSample.\");\n }\n return this._encoder.add(audioSample, false);\n }\n _flushAndClose(forceClose) {\n return this._encoder.flushAndClose(forceClose);\n }\n }\n class SubtitleSource extends MediaSource {\n constructor(codec) {\n super();\n if (!SUBTITLE_CODECS.includes(codec)) {\n throw new TypeError(`Invalid subtitle codec '${codec}'. Must be one of: ${SUBTITLE_CODECS.join(\", \")}.`);\n }\n this._codec = codec;\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/output-format.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n class OutputFormat {\n get supportsVideoRotationMetadata() {\n return this.supportsVideoTransformationMetadata;\n }\n getSupportedVideoCodecs() {\n return this.getSupportedCodecs().filter((codec) => VIDEO_CODECS.includes(codec));\n }\n getSupportedAudioCodecs() {\n return this.getSupportedCodecs().filter((codec) => AUDIO_CODECS.includes(codec));\n }\n getSupportedSubtitleCodecs() {\n return this.getSupportedCodecs().filter((codec) => SUBTITLE_CODECS.includes(codec));\n }\n _codecUnsupportedHint(codec) {\n return \"\";\n }\n _isFragmentedIsobmff() {\n return false;\n }\n }\n\n class IsobmffOutputFormat extends OutputFormat {\n constructor(options = {}) {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (options.fastStart !== undefined && ![false, \"in-memory\", \"reserve\", \"fragmented\"].includes(options.fastStart)) {\n throw new TypeError(\"options.fastStart, when provided, must be false, 'in-memory', 'reserve', or 'fragmented'.\");\n }\n if (options.minimumFragmentDuration !== undefined && (!isNumber(options.minimumFragmentDuration) || options.minimumFragmentDuration < 0)) {\n throw new TypeError(\"options.minimumFragmentDuration, when provided, must be a non-negative number.\");\n }\n if (options.onFtyp !== undefined && typeof options.onFtyp !== \"function\") {\n throw new TypeError(\"options.onFtyp, when provided, must be a function.\");\n }\n if (options.onMoov !== undefined && typeof options.onMoov !== \"function\") {\n throw new TypeError(\"options.onMoov, when provided, must be a function.\");\n }\n if (options.onMdat !== undefined && typeof options.onMdat !== \"function\") {\n throw new TypeError(\"options.onMdat, when provided, must be a function.\");\n }\n if (options.onMoof !== undefined && typeof options.onMoof !== \"function\") {\n throw new TypeError(\"options.onMoof, when provided, must be a function.\");\n }\n if (options.metadataFormat !== undefined && ![\"mdir\", \"mdta\", \"udta\", \"auto\"].includes(options.metadataFormat)) {\n throw new TypeError(\"options.metadataFormat, when provided, must be either 'auto', 'mdir', 'mdta', or 'udta'.\");\n }\n super();\n this._options = options;\n }\n getSupportedTrackCounts() {\n const max = 2 ** 32 - 1;\n return {\n video: { min: 0, max },\n audio: { min: 0, max },\n subtitle: { min: 0, max },\n total: { min: 0, max }\n };\n }\n get supportsVideoTransformationMetadata() {\n return true;\n }\n get supportsTimestampedMediaData() {\n return true;\n }\n get negativeTimestampSupport() {\n return \"full\";\n }\n _createMuxer(output) {\n return new IsobmffMuxer(output, this);\n }\n _isFragmentedIsobmff() {\n return this._options.fastStart === \"fragmented\";\n }\n }\n\n class Mp4OutputFormat extends IsobmffOutputFormat {\n constructor(options) {\n super(options);\n }\n get _name() {\n return \"MP4\";\n }\n get fileExtension() {\n return \".mp4\";\n }\n get mimeType() {\n return \"video/mp4\";\n }\n getSupportedCodecs() {\n return [\n ...VIDEO_CODECS,\n ...NON_PCM_AUDIO_CODECS,\n \"pcm-s16\",\n \"pcm-s16be\",\n \"pcm-s24\",\n \"pcm-s24be\",\n \"pcm-s32\",\n \"pcm-s32be\",\n \"pcm-f32\",\n \"pcm-f32be\",\n \"pcm-f64\",\n \"pcm-f64be\",\n ...SUBTITLE_CODECS\n ];\n }\n _codecUnsupportedHint(codec) {\n if (new MovOutputFormat().getSupportedCodecs().includes(codec)) {\n return \" Switching to MOV will grant support for this codec.\";\n }\n return \"\";\n }\n }\n\n class CmafOutputFormat extends IsobmffOutputFormat {\n constructor(options) {\n super(options);\n }\n get _name() {\n return \"CMAF\";\n }\n get fileExtension() {\n return \".m4s\";\n }\n get mimeType() {\n return \"video/mp4\";\n }\n getSupportedCodecs() {\n return [\n ...VIDEO_CODECS,\n ...NON_PCM_AUDIO_CODECS,\n \"pcm-s16\",\n \"pcm-s16be\",\n \"pcm-s24\",\n \"pcm-s24be\",\n \"pcm-s32\",\n \"pcm-s32be\",\n \"pcm-f32\",\n \"pcm-f32be\",\n \"pcm-f64\",\n \"pcm-f64be\",\n ...SUBTITLE_CODECS\n ];\n }\n }\n\n class MovOutputFormat extends IsobmffOutputFormat {\n constructor(options) {\n super(options);\n }\n get _name() {\n return \"MOV\";\n }\n get fileExtension() {\n return \".mov\";\n }\n get mimeType() {\n return \"video/quicktime\";\n }\n getSupportedCodecs() {\n return [\n ...VIDEO_CODECS,\n ...AUDIO_CODECS\n ];\n }\n _codecUnsupportedHint(codec) {\n if (new Mp4OutputFormat().getSupportedCodecs().includes(codec)) {\n return \" Switching to MP4 will grant support for this codec.\";\n }\n return \"\";\n }\n }\n\n class MkvOutputFormat extends OutputFormat {\n constructor(options = {}) {\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (options.appendOnly !== undefined && typeof options.appendOnly !== \"boolean\") {\n throw new TypeError(\"options.appendOnly, when provided, must be a boolean.\");\n }\n if (options.minimumClusterDuration !== undefined && (!isNumber(options.minimumClusterDuration) || options.minimumClusterDuration < 0)) {\n throw new TypeError(\"options.minimumClusterDuration, when provided, must be a non-negative number.\");\n }\n if (options.onEbmlHeader !== undefined && typeof options.onEbmlHeader !== \"function\") {\n throw new TypeError(\"options.onEbmlHeader, when provided, must be a function.\");\n }\n if (options.onSegmentHeader !== undefined && typeof options.onSegmentHeader !== \"function\") {\n throw new TypeError(\"options.onHeader, when provided, must be a function.\");\n }\n if (options.onCluster !== undefined && typeof options.onCluster !== \"function\") {\n throw new TypeError(\"options.onCluster, when provided, must be a function.\");\n }\n super();\n this._options = options;\n }\n _createMuxer(output) {\n return new MatroskaMuxer(output, this);\n }\n get _name() {\n return \"Matroska\";\n }\n getSupportedTrackCounts() {\n const max = 127;\n return {\n video: { min: 0, max },\n audio: { min: 0, max },\n subtitle: { min: 0, max },\n total: { min: 0, max }\n };\n }\n get fileExtension() {\n return \".mkv\";\n }\n get mimeType() {\n return \"video/x-matroska\";\n }\n getSupportedCodecs() {\n return [\n ...VIDEO_CODECS,\n ...NON_PCM_AUDIO_CODECS,\n ...PCM_AUDIO_CODECS.filter((codec) => ![\"pcm-s8\", \"pcm-f32be\", \"pcm-f64be\", \"ulaw\", \"alaw\"].includes(codec)),\n ...SUBTITLE_CODECS\n ];\n }\n get supportsVideoTransformationMetadata() {\n return false;\n }\n get supportsTimestampedMediaData() {\n return true;\n }\n get negativeTimestampSupport() {\n return \"prefer-non-negative\";\n }\n }\n\n class WebMOutputFormat extends MkvOutputFormat {\n constructor(options) {\n super(options);\n }\n getSupportedCodecs() {\n return [\n ...VIDEO_CODECS.filter((codec) => [\"vp8\", \"vp9\", \"av1\"].includes(codec)),\n ...AUDIO_CODECS.filter((codec) => [\"opus\", \"vorbis\"].includes(codec)),\n ...SUBTITLE_CODECS\n ];\n }\n get _name() {\n return \"WebM\";\n }\n get fileExtension() {\n return \".webm\";\n }\n get mimeType() {\n return \"video/webm\";\n }\n _codecUnsupportedHint(codec) {\n if (new MkvOutputFormat().getSupportedCodecs().includes(codec)) {\n return \" Switching to MKV will grant support for this codec.\";\n }\n return \"\";\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/output.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var ALL_TRACK_TYPES = [\"video\", \"audio\", \"subtitle\"];\n\n class OutputTrack {\n constructor(id, output, type, source, metadata) {\n this.id = id;\n this.output = output;\n this.type = type;\n this.source = source;\n this.metadata = metadata;\n }\n isVideoTrack() {\n return this.type === \"video\";\n }\n isAudioTrack() {\n return this.type === \"audio\";\n }\n isSubtitleTrack() {\n return this.type === \"subtitle\";\n }\n canBePairedWith(other) {\n if (!(other instanceof OutputTrack)) {\n throw new TypeError(\"other must be an OutputTrack.\");\n }\n if (this === other) {\n return false;\n }\n const thisGroups = toArray(this.metadata.group);\n const otherGroups = toArray(other.metadata.group);\n for (const aGroup of thisGroups) {\n const pairableInSameGroup = this.type !== other.type && otherGroups.some((bGroup) => aGroup === bGroup);\n if (pairableInSameGroup) {\n return true;\n }\n const pairableAcrossGroups = otherGroups.some((bGroup) => aGroup._pairedGroups.has(bGroup));\n if (pairableAcrossGroups) {\n return true;\n }\n }\n return false;\n }\n }\n\n class OutputVideoTrack extends OutputTrack {\n constructor(id, output, source, metadata) {\n super(id, output, \"video\", source, metadata);\n }\n }\n\n class OutputAudioTrack extends OutputTrack {\n constructor(id, output, source, metadata) {\n super(id, output, \"audio\", source, metadata);\n }\n }\n\n class OutputSubtitleTrack extends OutputTrack {\n constructor(id, output, source, metadata) {\n super(id, output, \"subtitle\", source, metadata);\n }\n }\n\n class OutputTrackGroup {\n constructor() {\n this._pairedGroups = new Set;\n }\n pairWith(other) {\n if (!(other instanceof OutputTrackGroup)) {\n throw new TypeError(\"other must be an OutputTrackGroup.\");\n }\n if (this === other) {\n throw new TypeError(\"Cannot pair a group with itself.\");\n }\n this._pairedGroups.add(other);\n other._pairedGroups.add(this);\n }\n }\n var validateBaseTrackMetadata = (metadata) => {\n if (!metadata || typeof metadata !== \"object\") {\n throw new TypeError(\"metadata must be an object.\");\n }\n if (metadata.languageCode !== undefined && !isIso639Dash2LanguageCode(metadata.languageCode)) {\n throw new TypeError(\"metadata.languageCode, when provided, must be a three-letter, ISO 639-2/T language code.\");\n }\n if (metadata.name !== undefined && typeof metadata.name !== \"string\") {\n throw new TypeError(\"metadata.name, when provided, must be a string.\");\n }\n if (metadata.disposition !== undefined) {\n validateTrackDisposition(metadata.disposition);\n }\n if (metadata.maximumPacketCount !== undefined && (!Number.isInteger(metadata.maximumPacketCount) || metadata.maximumPacketCount < 0)) {\n throw new TypeError(\"metadata.maximumPacketCount, when provided, must be a non-negative integer.\");\n }\n if (metadata.bitrate !== undefined && (!Number.isFinite(metadata.bitrate) || metadata.bitrate < 0)) {\n throw new TypeError(\"metadata.bitrate, when provided, must be a non-negative number.\");\n }\n if (metadata.averageBitrate !== undefined && (!Number.isFinite(metadata.averageBitrate) || metadata.averageBitrate < 0)) {\n throw new TypeError(\"metadata.averageBitrate, when provided, must be a non-negative number.\");\n }\n if (metadata.group !== undefined && !(metadata.group instanceof OutputTrackGroup) && (!Array.isArray(metadata.group) || metadata.group.some((group) => !(group instanceof OutputTrackGroup)))) {\n throw new TypeError(\"metadata.group, when provided, must be an OutputTrackGroup instance or an array of\" + \" OutputTrackGroup instances.\");\n }\n };\n\n class Output extends EventEmitter {\n get target() {\n const errorMessage = \"Output.target cannot be used when using PathedTarget with an async callback.\" + \" Use the 'target' event instead.\";\n if (this._rootTargetPromise) {\n throw new TypeError(errorMessage);\n }\n const rootTargetResult = this._getRootTarget();\n if (isThenable(rootTargetResult)) {\n throw new TypeError(errorMessage);\n }\n return rootTargetResult;\n }\n constructor(options) {\n super();\n this.state = \"pending\";\n this.defaultTrackGroup = new OutputTrackGroup;\n this.tracks = [];\n this._onFinalize = null;\n this._unfinalizedTargets = new Set;\n this._rootWriterPromise = null;\n this._startPromise = null;\n this._cancelPromise = null;\n this._finalizePromise = null;\n this._mutex = new AsyncMutex;\n this._metadataTags = {};\n this._rootTarget = null;\n this._rootTargetPromise = null;\n this._firstMediaStreamTimestamp = null;\n if (!options || typeof options !== \"object\") {\n throw new TypeError(\"options must be an object.\");\n }\n if (!(options.format instanceof OutputFormat)) {\n throw new TypeError(\"options.format must be an OutputFormat.\");\n }\n if (!(options.target instanceof Target || options.target instanceof PathedTarget)) {\n throw new TypeError(\"options.target must be a Target or a PathedTarget.\");\n }\n if (options.target instanceof Target) {\n this._rememberTarget(options.target);\n }\n if (options.initTarget !== undefined && !(options.initTarget instanceof Target) && typeof options.initTarget !== \"function\") {\n throw new Error(\"options.initTarget, when provided, must be a Target or a function that returns or resolves to\" + \" a Target.\");\n }\n if (options.onFinalize !== undefined && typeof options.onFinalize !== \"function\") {\n throw new TypeError(\"options.onFinalize, when provided, must be a function.\");\n }\n this.format = options.format;\n this._target = options.target;\n this._onFinalize = options.onFinalize ?? null;\n this._initTarget = options.initTarget ?? null;\n if (this._initTarget instanceof Target) {\n this._rememberTarget(this._initTarget);\n }\n this._muxer = options.format._createMuxer(this);\n }\n _getTargetValidated(request) {\n assert(this._target instanceof PathedTarget);\n const result = this._target.getTarget(request);\n const handleResult = (result) => {\n if (!(result instanceof Target)) {\n throw new TypeError(\"getTarget must return a Target.\");\n }\n return result;\n };\n if (isThenable(result)) {\n return result.then(handleResult);\n } else {\n return handleResult(result);\n }\n }\n async _getTarget(request) {\n assert(this._target instanceof PathedTarget);\n const target = await this._getTargetValidated(request);\n this._emit(\"target\", { target, request, isRoot: request.isRoot });\n if (this.state === \"canceled\") {\n await target._close();\n } else {\n this._rememberTarget(target);\n }\n return target;\n }\n _rememberTarget(target) {\n this._unfinalizedTargets.add(target);\n target.on(\"finalized\", () => this._unfinalizedTargets.delete(target), { once: true });\n }\n async _getInitTarget() {\n assert(this._initTarget !== null);\n if (this._initTarget instanceof Target) {\n return this._initTarget;\n }\n const target = await this._initTarget();\n if (this.state === \"canceled\") {\n await target._close();\n } else {\n this._rememberTarget(target);\n }\n return target;\n }\n _hasInitTarget() {\n return this._initTarget !== null;\n }\n _getRootTarget() {\n if (this._rootTarget) {\n return this._rootTarget;\n }\n if (this._rootTargetPromise) {\n return this._rootTargetPromise;\n }\n if (this._target instanceof Target) {\n this._emit(\"target\", { target: this._target, request: null, isRoot: true });\n this._rootTarget = this._target;\n return this._target;\n }\n const request = {\n path: this._target.rootPath,\n isRoot: true,\n mimeType: this.format.mimeType\n };\n const result = this._getTargetValidated(request);\n const handleResult = (target) => {\n if (this.state === \"canceled\") {\n target._close();\n } else {\n this._rememberTarget(target);\n }\n this._emit(\"target\", { target, request, isRoot: true });\n this._rootTarget = target;\n return target;\n };\n if (isThenable(result)) {\n return this._rootTargetPromise = result.then(handleResult);\n } else {\n return handleResult(result);\n }\n }\n _getRootWriter(isMonotonic) {\n return this._rootWriterPromise ??= (async () => {\n const target = await this._getRootTarget();\n const writer = new Writer(target, typeof isMonotonic === \"boolean\" ? isMonotonic : isMonotonic(target));\n writer.start();\n return writer;\n })();\n }\n addVideoTrack(source, metadata = {}) {\n if (!(source instanceof VideoSource)) {\n throw new TypeError(\"source must be a VideoSource.\");\n }\n validateBaseTrackMetadata(metadata);\n if (metadata.rotation !== undefined && ![0, 90, 180, 270].includes(metadata.rotation)) {\n throw new TypeError(`Invalid video rotation: ${metadata.rotation}. Has to be 0, 90, 180 or 270.`);\n }\n if (metadata.flip !== undefined && typeof metadata.flip !== \"boolean\") {\n throw new TypeError(\"metadata.flip, when provided, must be a boolean.\");\n }\n if (metadata.transformationMatrix !== undefined && (!Array.isArray(metadata.transformationMatrix) || metadata.transformationMatrix.length !== 9 || !metadata.transformationMatrix.every((x) => Number.isFinite(x)))) {\n throw new TypeError(\"metadata.transformationMatrix, when provided, must be an array of 9 finite numbers.\");\n }\n if (metadata.frameRate !== undefined && (!Number.isFinite(metadata.frameRate) || metadata.frameRate <= 0)) {\n throw new TypeError(`Invalid video frame rate: ${metadata.frameRate}. Must be a positive number.`);\n }\n if (metadata.hasOnlyKeyPackets !== undefined && typeof metadata.hasOnlyKeyPackets !== \"boolean\") {\n throw new TypeError(\"metadata.hasOnlyKeyPackets, when provided, must be a boolean.\");\n }\n if (metadata.canBeTransparent !== undefined && typeof metadata.canBeTransparent !== \"boolean\") {\n throw new TypeError(\"metadata.canBeTransparent, when provided, must be a boolean.\");\n }\n if (metadata.decoderConfig !== undefined) {\n validateVideoChunkMetadata({ decoderConfig: metadata.decoderConfig }, source._codec);\n }\n if (metadata.primingPacket !== undefined) {\n if (!(metadata.primingPacket instanceof EncodedPacket)) {\n throw new TypeError(\"metadata.primingPacket, when provided, must be an EncodedPacket.\");\n }\n if (metadata.decoderConfig === undefined) {\n throw new TypeError(\"metadata.primingPacket can only be provided alongside metadata.decoderConfig.\");\n }\n }\n const metadataCopy = { ...metadata };\n metadataCopy.group ??= this.defaultTrackGroup;\n return this._addTrack(new OutputVideoTrack(this.tracks.length + 1, this, source, metadataCopy));\n }\n addAudioTrack(source, metadata = {}) {\n if (!(source instanceof AudioSource)) {\n throw new TypeError(\"source must be an AudioSource.\");\n }\n validateBaseTrackMetadata(metadata);\n if (metadata.decoderConfig !== undefined) {\n validateAudioChunkMetadata({ decoderConfig: metadata.decoderConfig }, source._codec);\n }\n if (metadata.primingPacket !== undefined) {\n if (!(metadata.primingPacket instanceof EncodedPacket)) {\n throw new TypeError(\"metadata.primingPacket, when provided, must be an EncodedPacket.\");\n }\n if (metadata.decoderConfig === undefined) {\n throw new TypeError(\"metadata.primingPacket can only be provided alongside metadata.decoderConfig.\");\n }\n }\n const metadataCopy = { ...metadata };\n metadataCopy.group ??= this.defaultTrackGroup;\n return this._addTrack(new OutputAudioTrack(this.tracks.length + 1, this, source, metadataCopy));\n }\n addSubtitleTrack(source, metadata = {}) {\n if (!(source instanceof SubtitleSource)) {\n throw new TypeError(\"source must be a SubtitleSource.\");\n }\n validateBaseTrackMetadata(metadata);\n const metadataCopy = { ...metadata };\n metadataCopy.group ??= this.defaultTrackGroup;\n return this._addTrack(new OutputSubtitleTrack(this.tracks.length + 1, this, source, metadataCopy));\n }\n setMetadataTags(tags) {\n validateMetadataTags(tags);\n if (this.state !== \"pending\") {\n throw new Error(\"Cannot set metadata tags after output has been started or canceled.\");\n }\n this._metadataTags = tags;\n }\n _addTrack(track) {\n if (this.state !== \"pending\") {\n throw new Error(\"Cannot add track after output has been started or canceled.\");\n }\n if (track.source._connectedTrack) {\n throw new Error(\"Source is already used for a track.\");\n }\n const supportedTrackCounts = this.format.getSupportedTrackCounts();\n const presentTracksOfThisType = this.tracks.reduce((count, t) => count + (t.type === track.type ? 1 : 0), 0);\n const maxCount = supportedTrackCounts[track.type].max;\n if (presentTracksOfThisType === maxCount) {\n throw new Error(maxCount === 0 ? `${this.format._name} does not support ${track.type} tracks.` : `${this.format._name} does not support more than ${maxCount} ${track.type} track` + `${maxCount === 1 ? \"\" : \"s\"}.`);\n }\n const maxTotalCount = supportedTrackCounts.total.max;\n if (this.tracks.length === maxTotalCount) {\n throw new Error(`${this.format._name} does not support more than ${maxTotalCount} tracks` + `${maxTotalCount === 1 ? \"\" : \"s\"} in total.`);\n }\n if (track.isVideoTrack()) {\n const supportedVideoCodecs = this.format.getSupportedVideoCodecs();\n if (supportedVideoCodecs.length === 0) {\n throw new Error(`${this.format._name} does not support video tracks.` + this.format._codecUnsupportedHint(track.source._codec));\n } else if (!supportedVideoCodecs.includes(track.source._codec)) {\n throw new Error(`Codec '${track.source._codec}' cannot be contained within ${this.format._name}. Supported` + ` video codecs are: ${supportedVideoCodecs.map((codec) => `'${codec}'`).join(\", \")}.` + this.format._codecUnsupportedHint(track.source._codec));\n }\n } else if (track.isAudioTrack()) {\n const supportedAudioCodecs = this.format.getSupportedAudioCodecs();\n if (supportedAudioCodecs.length === 0) {\n throw new Error(`${this.format._name} does not support audio tracks.` + this.format._codecUnsupportedHint(track.source._codec));\n } else if (!supportedAudioCodecs.includes(track.source._codec)) {\n throw new Error(`Codec '${track.source._codec}' cannot be contained within ${this.format._name}. Supported` + ` audio codecs are: ${supportedAudioCodecs.map((codec) => `'${codec}'`).join(\", \")}.` + this.format._codecUnsupportedHint(track.source._codec));\n }\n } else if (track.isSubtitleTrack()) {\n const supportedSubtitleCodecs = this.format.getSupportedSubtitleCodecs();\n if (supportedSubtitleCodecs.length === 0) {\n throw new Error(`${this.format._name} does not support subtitle tracks.` + this.format._codecUnsupportedHint(track.source._codec));\n } else if (!supportedSubtitleCodecs.includes(track.source._codec)) {\n throw new Error(`Codec '${track.source._codec}' cannot be contained within ${this.format._name}. Supported` + ` subtitle codecs are: ${supportedSubtitleCodecs.map((codec) => `'${codec}'`).join(\", \")}.` + this.format._codecUnsupportedHint(track.source._codec));\n }\n }\n this.tracks.push(track);\n track.source._connectedTrack = track;\n return track;\n }\n hasEnoughTracks() {\n const supportedTrackCounts = this.format.getSupportedTrackCounts();\n for (const trackType of ALL_TRACK_TYPES) {\n const presentTracksOfThisType = this.tracks.reduce((count, track) => count + (track.type === trackType ? 1 : 0), 0);\n const minCount = supportedTrackCounts[trackType].min;\n if (presentTracksOfThisType < minCount) {\n return false;\n }\n }\n const totalMinCount = supportedTrackCounts.total.min;\n if (this.tracks.length < totalMinCount) {\n return false;\n }\n return true;\n }\n async start() {\n const supportedTrackCounts = this.format.getSupportedTrackCounts();\n for (const trackType of ALL_TRACK_TYPES) {\n const presentTracksOfThisType = this.tracks.reduce((count, track) => count + (track.type === trackType ? 1 : 0), 0);\n const minCount = supportedTrackCounts[trackType].min;\n if (presentTracksOfThisType < minCount) {\n throw new Error(minCount === supportedTrackCounts[trackType].max ? `${this.format._name} requires exactly ${minCount} ${trackType}` + ` track${minCount === 1 ? \"\" : \"s\"}.` : `${this.format._name} requires at least ${minCount} ${trackType}` + ` track${minCount === 1 ? \"\" : \"s\"}.`);\n }\n }\n const totalMinCount = supportedTrackCounts.total.min;\n if (this.tracks.length < totalMinCount) {\n throw new Error(totalMinCount === supportedTrackCounts.total.max ? `${this.format._name} requires exactly ${totalMinCount} track` + `${totalMinCount === 1 ? \"\" : \"s\"}.` : `${this.format._name} requires at least ${totalMinCount} track` + `${totalMinCount === 1 ? \"\" : \"s\"}.`);\n }\n if (this.state === \"canceled\") {\n throw new Error(\"Output has been canceled.\");\n }\n if (this._startPromise) {\n Logging._warn(\"Output has already been started.\");\n return this._startPromise;\n }\n return this._startPromise = (async () => {\n this.state = \"started\";\n const releasePromise = this._mutex.acquire();\n try {\n await this._muxer.start();\n const promises = this.tracks.map((track) => track.source._start());\n await Promise.all(promises);\n } finally {\n (await releasePromise)();\n }\n })();\n }\n getMimeType() {\n return this._muxer.getMimeType();\n }\n async cancel() {\n if (this.state === \"canceled\") {\n return this._cancelPromise ?? undefined;\n } else if (this.state === \"finalizing\" || this.state === \"finalized\") {\n if (this.state === \"finalized\") {\n Logging._warn(\"Output has already been finalized.\");\n }\n return;\n }\n return this._cancelPromise = (async () => {\n this.state = \"canceled\";\n const release = await this._mutex.acquire();\n try {\n const promises = this.tracks.map((x) => x.source._flushOrWaitForOngoingClose(true));\n await Promise.all(promises);\n await Promise.all([...this._unfinalizedTargets].map((target) => target._close()));\n this._unfinalizedTargets.clear();\n } finally {\n release();\n }\n })();\n }\n async finalize() {\n if (this.state === \"pending\") {\n throw new Error(\"Cannot finalize before starting.\");\n }\n if (this.state === \"canceled\") {\n throw new Error(\"Cannot finalize after canceling.\");\n }\n if (this._finalizePromise) {\n Logging._warn(\"Output has already been finalized.\");\n return this._finalizePromise;\n }\n return this._finalizePromise = (async () => {\n this.state = \"finalizing\";\n const release = await this._mutex.acquire();\n try {\n const promises = this.tracks.map((x) => x.source._flushOrWaitForOngoingClose(false));\n await Promise.all(promises);\n await this._muxer.finalize();\n if (this._rootWriterPromise) {\n const rootWriter = await this._rootWriterPromise;\n if (!rootWriter.finalized) {\n await rootWriter.flush();\n await rootWriter.finalize();\n }\n }\n if (this._onFinalize) {\n await this._onFinalize();\n }\n this.state = \"finalized\";\n } catch (error) {\n this.state = \"canceled\";\n throw error;\n } finally {\n await Promise.all([...this._unfinalizedTargets].map((target) => target._close().catch(() => {})));\n this._unfinalizedTargets.clear();\n release();\n }\n })();\n }\n }\n\n // ../../node_modules/.bun/mediabunny@1.61.3/node_modules/mediabunny/dist/modules/src/index.js\n /*!\n * Copyright (c) 2026-present, Vanilagy and contributors\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n var MEDIABUNNY_LOADED_SYMBOL = Symbol.for(\"mediabunny loaded\");\n if (globalThis[MEDIABUNNY_LOADED_SYMBOL]) {\n Logging._error(`[WARNING]\nMediabunny was loaded twice.` + \" This will likely cause Mediabunny not to work correctly.\" + \" Check if multiple dependencies are importing different versions of Mediabunny,\" + \" or if something is being bundled incorrectly.\");\n }\n globalThis[MEDIABUNNY_LOADED_SYMBOL] = true;\n\n // src/core/utils/error-handler.ts\n function extractErrorMessage(error) {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n }\n\n // src/core/utils/logger.ts\n function isDebugEnabled() {\n const globalAny = globalThis;\n if (globalAny.__VIDTREO_DEBUG__ === true || globalAny.__VIDTREO_DEV__ === true) {\n return true;\n }\n const envNode = typeof process !== \"undefined\" && process?.env ? \"development\" : undefined;\n if (envNode === \"development\" || envNode === \"test\") {\n return true;\n }\n if (typeof localStorage !== \"undefined\") {\n const flag = localStorage.getItem(\"VIDTREO_DEBUG\");\n if (flag === \"true\") {\n return true;\n }\n }\n return false;\n }\n var isDevelopment = isDebugEnabled();\n var ANSI_COLORS = {\n reset: \"\\x1B[0m\",\n bright: \"\\x1B[1m\",\n dim: \"\\x1B[2m\",\n red: \"\\x1B[31m\",\n green: \"\\x1B[32m\",\n yellow: \"\\x1B[33m\",\n blue: \"\\x1B[34m\",\n magenta: \"\\x1B[35m\",\n cyan: \"\\x1B[36m\",\n white: \"\\x1B[37m\",\n gray: \"\\x1B[90m\"\n };\n function formatMessage(level, message, options) {\n if (!isDevelopment) {\n return \"\";\n }\n const prefix = options?.prefix || `[${level.toUpperCase()}]`;\n const color = options?.color || getDefaultColor(level);\n const colorCode = ANSI_COLORS[color];\n const resetCode = ANSI_COLORS.reset;\n return `${colorCode}${prefix}${resetCode} ${message}`;\n }\n function getDefaultColor(level) {\n switch (level) {\n case \"error\":\n return \"red\";\n case \"warn\":\n return \"yellow\";\n case \"info\":\n return \"cyan\";\n case \"debug\":\n return \"gray\";\n default:\n return \"white\";\n }\n }\n function log(level, message, ...args) {\n if (!isDevelopment) {\n return;\n }\n const formatted = formatMessage(level, message);\n console[level](formatted, ...args);\n }\n var logger = {\n log: (message, ...args) => {\n log(\"log\", message, ...args);\n },\n info: (message, ...args) => {\n log(\"info\", message, ...args);\n },\n warn: (message, ...args) => {\n log(\"warn\", message, ...args);\n },\n error: (message, ...args) => {\n log(\"error\", message, ...args);\n },\n debug: (message, ...args) => {\n log(\"debug\", message, ...args);\n },\n group: (label, color = \"cyan\") => {\n if (!isDevelopment) {\n return;\n }\n const colorCode = ANSI_COLORS[color];\n const resetCode = ANSI_COLORS.reset;\n console.group(`${colorCode}${label}${resetCode}`);\n },\n groupEnd: () => {\n if (!isDevelopment) {\n return;\n }\n console.groupEnd();\n }\n };\n\n // src/core/utils/validation.ts\n function requireNonNull(value, message) {\n if (value === null || value === undefined) {\n throw new Error(message);\n }\n return value;\n }\n function requireDefined(value, message) {\n if (value === undefined) {\n throw new Error(message);\n }\n return value;\n }\n function requireInitialized(value, componentName) {\n if (value === null || value === undefined) {\n throw new Error(`${componentName} is not initialized`);\n }\n return value;\n }\n\n // src/core/processor/worker/audio-only-config.ts\n var AUDIO_CODEC_CONTAINERS = {\n aac: \"mp4\",\n opus: \"webm\"\n };\n function validateAudioOnlyWorkerConfig(config, audioConfig) {\n if (!audioConfig) {\n throw new Error(\"Audio-only recording requires an audio input\");\n }\n if (audioConfig.sampleRate <= 0) {\n throw new Error(\"Audio sample rate must be greater than zero\");\n }\n if (audioConfig.numberOfChannels <= 0) {\n throw new Error(\"Audio channels must be greater than zero\");\n }\n if (!Number.isFinite(config.audioBitrate) || config.audioBitrate <= 0) {\n throw new Error(\"Audio bitrate must be greater than zero\");\n }\n const expectedFormat = AUDIO_CODEC_CONTAINERS[config.audioCodec];\n if (expectedFormat === undefined) {\n throw new Error(`Audio codec ${config.audioCodec} is not supported`);\n }\n if (config.format !== expectedFormat) {\n throw new Error(`Audio codec ${config.audioCodec} requires the ${expectedFormat} container`);\n }\n return audioConfig;\n }\n\n // src/core/processor/worker/audio-state.ts\n var MILLISECONDS_PER_SECOND = 1000;\n\n class AudioState {\n getNowMilliseconds;\n isPaused = false;\n isMuted = false;\n pausedDuration = 0;\n pauseStartedAt = null;\n lastAudioTimestamp = 0;\n isProcessingActive = false;\n constructor(dependencies) {\n this.getNowMilliseconds = dependencies.getNowMilliseconds;\n }\n reset() {\n this.isPaused = false;\n this.isMuted = false;\n this.pausedDuration = 0;\n this.pauseStartedAt = null;\n this.lastAudioTimestamp = 0;\n this.isProcessingActive = false;\n }\n setProcessingActive(isActive) {\n this.isProcessingActive = isActive;\n }\n isActive() {\n return this.isProcessingActive;\n }\n toggleMuted() {\n this.isMuted = !this.isMuted;\n return this.isMuted;\n }\n setMuted(isMuted) {\n this.isMuted = isMuted;\n }\n getIsMuted() {\n return this.isMuted;\n }\n getIsPaused() {\n return this.isPaused;\n }\n getPausedDuration() {\n return this.pausedDuration;\n }\n pause() {\n if (this.isPaused) {\n return false;\n }\n this.pauseStartedAt = this.getNowMilliseconds() / MILLISECONDS_PER_SECOND;\n this.isPaused = true;\n return true;\n }\n resume() {\n if (!this.isPaused) {\n return false;\n }\n const now = this.getNowMilliseconds() / MILLISECONDS_PER_SECOND;\n if (this.pauseStartedAt !== null) {\n this.pausedDuration += now - this.pauseStartedAt;\n }\n this.pauseStartedAt = null;\n this.isPaused = false;\n return true;\n }\n getAudioTimestamp(timestamp) {\n if (timestamp >= this.lastAudioTimestamp) {\n return timestamp;\n }\n return this.lastAudioTimestamp;\n }\n updateLastAudioTimestamp(timestamp, duration) {\n this.lastAudioTimestamp = timestamp + duration;\n }\n getLastAudioTimestamp() {\n return this.lastAudioTimestamp;\n }\n }\n\n // src/core/processor/worker/buffer-tracker.ts\n var BUFFER_UPDATE_INTERVAL_MILLISECONDS = 1000;\n var BYTES_PER_KILOBYTE = 1024;\n var FILE_SIZE_PRECISION_FACTOR = 100;\n var FILE_SIZE_UNITS = [\"Bytes\", \"KB\", \"MB\", \"GB\"];\n\n class BufferTracker {\n intervalId = null;\n lastEmittedSize = null;\n dependencies;\n constructor(dependencies) {\n this.dependencies = dependencies;\n }\n start() {\n if (this.intervalId !== null) {\n return;\n }\n this.intervalId = this.dependencies.setInterval(() => {\n const size = this.dependencies.getBufferSize();\n if (size === this.lastEmittedSize) {\n return;\n }\n this.lastEmittedSize = size;\n const formatted = formatFileSize(size);\n this.dependencies.onBufferUpdate(size, formatted);\n }, BUFFER_UPDATE_INTERVAL_MILLISECONDS);\n }\n stop() {\n if (this.intervalId === null) {\n return;\n }\n this.dependencies.clearInterval(this.intervalId);\n this.intervalId = null;\n this.lastEmittedSize = null;\n }\n }\n function formatFileSize(bytes) {\n if (bytes === 0) {\n return `0 ${FILE_SIZE_UNITS[0]}`;\n }\n const base = BYTES_PER_KILOBYTE;\n const index = Math.floor(Math.log(bytes) / Math.log(base));\n const size = Math.round(bytes / base ** index * FILE_SIZE_PRECISION_FACTOR) / FILE_SIZE_PRECISION_FACTOR;\n return `${size} ${FILE_SIZE_UNITS[index]}`;\n }\n\n // src/core/processor/worker/encoder-fallback.ts\n var HARDWARE_ACCELERATION_FALLBACK_CHAIN = [\"prefer-hardware\", \"prefer-software\", \"no-preference\"];\n async function resolveVideoHardwareAcceleration(codec, width, height, bitrate) {\n for (const hardwareAcceleration of HARDWARE_ACCELERATION_FALLBACK_CHAIN) {\n const isSupported = await canEncodeVideo(codec, {\n width,\n height,\n bitrate: Math.round(bitrate),\n hardwareAcceleration\n }).catch(() => false);\n if (isSupported) {\n return hardwareAcceleration;\n }\n }\n return \"no-preference\";\n }\n\n // src/core/processor/worker/encoder-warmup.ts\n var WARMUP_WIDTH = 640;\n var WARMUP_HEIGHT = 360;\n var WARMUP_BITRATE = 1e6;\n var WARMUP_FRAMERATE = 30;\n var I420_BYTES_PER_PIXEL = 1.5;\n function resolveWarmupCodecString(codec) {\n switch (codec) {\n case \"avc\":\n return \"avc1.42001f\";\n case \"hevc\":\n return \"hvc1.1.6.L93.B0\";\n case \"vp9\":\n return \"vp09.00.10.08\";\n case \"av1\":\n return \"av01.0.04M.08\";\n case \"vp8\":\n return \"vp8\";\n default:\n return null;\n }\n }\n function encodeWarmupFrame(encoder, config) {\n const frame = new VideoFrame(new Uint8Array(WARMUP_WIDTH * WARMUP_HEIGHT * I420_BYTES_PER_PIXEL), {\n format: \"I420\",\n codedWidth: WARMUP_WIDTH,\n codedHeight: WARMUP_HEIGHT,\n timestamp: 0\n });\n return Promise.resolve().then(() => {\n encoder.configure(config);\n encoder.encode(frame, { keyFrame: true });\n return encoder.flush();\n }).finally(() => {\n frame.close();\n });\n }\n async function warmUpVideoEncoder(codec, hardwareAcceleration) {\n if (typeof VideoEncoder === \"undefined\" || typeof VideoFrame === \"undefined\") {\n return;\n }\n const codecString = resolveWarmupCodecString(codec);\n if (codecString === null) {\n return;\n }\n const config = {\n codec: codecString,\n width: WARMUP_WIDTH,\n height: WARMUP_HEIGHT,\n bitrate: WARMUP_BITRATE,\n framerate: WARMUP_FRAMERATE,\n hardwareAcceleration,\n latencyMode: \"realtime\"\n };\n const support = await VideoEncoder.isConfigSupported(config).catch(() => null);\n if (support?.supported !== true) {\n return;\n }\n const encoder = new VideoEncoder({\n output: () => {\n return;\n },\n error: () => {\n return;\n }\n });\n await encodeWarmupFrame(encoder, config).finally(() => {\n if (encoder.state !== \"closed\") {\n encoder.close();\n }\n });\n }\n\n // src/core/processor/worker/rotation-utils.ts\n var ROTATION_DEGREES_0 = 0;\n var ROTATION_DEGREES_90 = 90;\n var ROTATION_DEGREES_180 = 180;\n var ROTATION_DEGREES_270 = 270;\n var ROTATION_DEGREES_360 = 360;\n function calculateFrameRotationDegrees(input) {\n if (!input.isMobileDevice) {\n return ROTATION_DEGREES_0;\n }\n const targetWidth = input.targetWidth;\n const targetHeight = input.targetHeight;\n if (typeof targetWidth !== \"number\") {\n return ROTATION_DEGREES_0;\n }\n if (typeof targetHeight !== \"number\") {\n return ROTATION_DEGREES_0;\n }\n const isTargetPortrait = targetHeight > targetWidth;\n const isFramePortrait = input.frameHeight > input.frameWidth;\n if (isTargetPortrait === isFramePortrait) {\n return ROTATION_DEGREES_0;\n }\n const settingsRotation = resolveRotationHint(input.settingsRotation);\n if (settingsRotation !== null) {\n return settingsRotation;\n }\n const orientationRotation = resolveRotationHint(input.orientationAngle);\n if (orientationRotation !== null) {\n return orientationRotation;\n }\n const windowRotation = resolveRotationHint(input.windowOrientation);\n if (windowRotation !== null) {\n return windowRotation;\n }\n return getFallbackRotationDegrees();\n }\n function resolveRotationHint(rotationHint) {\n const normalizedRotation = normalizeRotationDegrees(rotationHint);\n if (normalizedRotation === ROTATION_DEGREES_90) {\n return ROTATION_DEGREES_90;\n }\n if (normalizedRotation === ROTATION_DEGREES_270) {\n return ROTATION_DEGREES_270;\n }\n return null;\n }\n function normalizeRotationDegrees(rotationDegrees) {\n if (typeof rotationDegrees !== \"number\") {\n return null;\n }\n const normalizedValue = (rotationDegrees % ROTATION_DEGREES_360 + ROTATION_DEGREES_360) % ROTATION_DEGREES_360;\n const remainder = normalizedValue % ROTATION_DEGREES_90;\n if (remainder !== 0) {\n return null;\n }\n if (normalizedValue === ROTATION_DEGREES_0) {\n return ROTATION_DEGREES_0;\n }\n if (normalizedValue === ROTATION_DEGREES_90) {\n return ROTATION_DEGREES_90;\n }\n if (normalizedValue === ROTATION_DEGREES_180) {\n return ROTATION_DEGREES_180;\n }\n if (normalizedValue === ROTATION_DEGREES_270) {\n return ROTATION_DEGREES_270;\n }\n return null;\n }\n function getFallbackRotationDegrees() {\n return ROTATION_DEGREES_90;\n }\n\n // src/core/processor/worker/watermark-utils.ts\n function calculateWatermarkTargetSize(videoWidth, imageWidth, imageHeight) {\n const targetWidth = Math.round(videoWidth * 0.07);\n const scaleFactor = targetWidth / imageWidth;\n const targetHeight = Math.round(imageHeight * scaleFactor);\n return { width: targetWidth, height: targetHeight };\n }\n function getWatermarkPosition(options) {\n const { watermarkWidth, watermarkHeight, videoWidth, videoHeight, position } = options;\n const padding = 20;\n switch (position) {\n case \"top-left\":\n return { x: padding, y: padding };\n case \"top-right\":\n return { x: videoWidth - watermarkWidth - padding, y: padding };\n case \"bottom-left\":\n return { x: padding, y: videoHeight - watermarkHeight - padding };\n case \"bottom-right\":\n return {\n x: videoWidth - watermarkWidth - padding,\n y: videoHeight - watermarkHeight - padding\n };\n case \"center\":\n return {\n x: (videoWidth - watermarkWidth) / 2,\n y: (videoHeight - watermarkHeight) / 2\n };\n default:\n return {\n x: videoWidth - watermarkWidth - padding,\n y: videoHeight - watermarkHeight - padding\n };\n }\n }\n\n // src/core/processor/worker/frame-compositor.ts\n var DOUBLE_VALUE = 2;\n var DEFAULT_WATERMARK_OPACITY = 1;\n var DEFAULT_WATERMARK_BASE_WIDTH = 1280;\n var OVERLAY_BACKGROUND_OPACITY = 0.6;\n var OVERLAY_PADDING = 16;\n var OVERLAY_TEXT_COLOR = \"#ffffff\";\n var OVERLAY_FONT_SIZE = 16;\n var OVERLAY_FONT_FAMILY = \"Arial, sans-serif\";\n var OVERLAY_MIN_WIDTH = 200;\n var OVERLAY_MIN_HEIGHT = 50;\n var OVERLAY_COLOR_CHANNEL_VALUE = 20;\n var OVERLAY_BORDER_RADIUS = 50;\n var COMPOSITION_CONTEXT_ERROR_MESSAGE = \"Failed to get composition canvas context\";\n var RECORDER_WORKER_LOG_PREFIX = \"[RecorderWorker]\";\n var ROTATION_RADIANS_90 = Math.PI * ROTATION_DEGREES_90 / ROTATION_DEGREES_180;\n var ROTATION_RADIANS_270 = Math.PI * ROTATION_DEGREES_270 / ROTATION_DEGREES_180;\n\n class FrameCompositor {\n overlayCanvas = null;\n compositionCanvas = null;\n compositionContext = null;\n watermarkCanvas = null;\n watermarkCacheKey = null;\n watermarkPreparationKey = null;\n watermarkPreparationPromise = null;\n watermarkRequestId = 0;\n frameRotationDegrees = null;\n videoSettings = null;\n viewportMetadata = null;\n isMobileDevice = false;\n logger;\n fetchResource;\n createImageBitmap;\n sendDebugLog;\n constructor(dependencies) {\n this.logger = dependencies.logger;\n this.fetchResource = dependencies.fetchResource;\n this.createImageBitmap = dependencies.createImageBitmap;\n this.sendDebugLog = dependencies.sendDebugLog;\n }\n resetRecordingState() {\n this.overlayCanvas = null;\n this.compositionCanvas = null;\n this.compositionContext = null;\n this.frameRotationDegrees = null;\n this.videoSettings = null;\n this.viewportMetadata = null;\n this.isMobileDevice = false;\n }\n reset() {\n this.resetRecordingState();\n this.watermarkCanvas = null;\n this.watermarkCacheKey = null;\n this.watermarkPreparationKey = null;\n this.watermarkPreparationPromise = null;\n this.watermarkRequestId += 1;\n }\n setVideoSettings(settings) {\n this.videoSettings = settings;\n this.frameRotationDegrees = null;\n }\n setViewportMetadata(metadata) {\n this.viewportMetadata = metadata;\n this.frameRotationDegrees = null;\n }\n setIsMobileDevice(isMobileDevice) {\n this.isMobileDevice = isMobileDevice;\n this.frameRotationDegrees = null;\n }\n async prepareWatermark(config) {\n const watermarkConfig = config.watermark;\n if (!watermarkConfig) {\n return;\n }\n const cacheKey = this.buildWatermarkCacheKey(config);\n if (this.watermarkCanvas && this.watermarkCacheKey === cacheKey) {\n return;\n }\n if (this.watermarkPreparationPromise && this.watermarkPreparationKey === cacheKey) {\n await this.watermarkPreparationPromise;\n return;\n }\n this.watermarkCanvas = null;\n this.watermarkCacheKey = null;\n this.watermarkPreparationKey = cacheKey;\n this.watermarkRequestId += 1;\n const requestId = this.watermarkRequestId;\n const preparationPromise = this.loadWatermark(config, cacheKey, requestId);\n this.watermarkPreparationPromise = preparationPromise;\n await preparationPromise.finally(() => {\n if (this.watermarkPreparationPromise !== preparationPromise) {\n return;\n }\n this.watermarkPreparationPromise = null;\n this.watermarkPreparationKey = null;\n });\n }\n buildWatermarkCacheKey(config) {\n const watermarkConfig = requireDefined(config.watermark, \"Watermark config is required\");\n let opacity = DEFAULT_WATERMARK_OPACITY;\n if (typeof watermarkConfig.opacity === \"number\") {\n opacity = watermarkConfig.opacity;\n }\n let videoWidth = DEFAULT_WATERMARK_BASE_WIDTH;\n if (typeof config.width === \"number\") {\n videoWidth = config.width;\n }\n return [watermarkConfig.url, opacity, videoWidth].join(\"|\");\n }\n async loadWatermark(config, cacheKey, requestId) {\n const watermarkConfig = requireDefined(config.watermark, \"Watermark config is required\");\n const url = watermarkConfig.url;\n let opacity = DEFAULT_WATERMARK_OPACITY;\n if (typeof watermarkConfig.opacity === \"number\") {\n opacity = watermarkConfig.opacity;\n }\n const response = await this.fetchResource(url, { mode: \"cors\" }).catch((error) => {\n this.logWatermarkError(url, error);\n return null;\n });\n if (!response) {\n return;\n }\n if (!response.ok) {\n const httpError = new Error(`HTTP error! status: ${response.status}`);\n this.logWatermarkError(url, httpError);\n return;\n }\n const blob = await response.blob().catch((error) => {\n this.logWatermarkError(url, error);\n return null;\n });\n if (!blob) {\n return;\n }\n let isVectorImageFormat = false;\n if (url.toLowerCase().endsWith(\".svg\")) {\n isVectorImageFormat = true;\n }\n if (blob.type === \"image/svg+xml\") {\n isVectorImageFormat = true;\n }\n if (isVectorImageFormat) {\n this.logger.warn(`${RECORDER_WORKER_LOG_PREFIX} Loading SVG watermark. Note: Some environments may not support SVG in createImageBitmap inside workers. If the watermark doesn't appear, consider using a PNG or a Data URL.`);\n }\n const imageBitmap = await this.createImageBitmap(blob).catch((error) => {\n const errorMessage = extractErrorMessage(error);\n const bitmapError = new Error(`Failed to create ImageBitmap from blob (${blob.type}). Errors can happen with SVGs in workers or invalid formats: ${errorMessage}`);\n this.logWatermarkError(url, bitmapError);\n return null;\n });\n if (!imageBitmap) {\n return;\n }\n let videoWidth = DEFAULT_WATERMARK_BASE_WIDTH;\n if (typeof config.width === \"number\") {\n videoWidth = config.width;\n }\n const { width: targetWidth, height: targetHeight } = calculateWatermarkTargetSize(videoWidth, imageBitmap.width, imageBitmap.height);\n const scaleFactor = targetWidth / imageBitmap.width;\n const canvas = new OffscreenCanvas(targetWidth, targetHeight);\n const context = requireNonNull(canvas.getContext(\"2d\", { willReadFrequently: false }), \"Failed to get watermark canvas context\");\n context.globalAlpha = opacity;\n context.drawImage(imageBitmap, 0, 0, targetWidth, targetHeight);\n context.globalAlpha = 1;\n imageBitmap.close();\n if (this.watermarkRequestId !== requestId) {\n return;\n }\n if (this.watermarkPreparationKey !== cacheKey) {\n return;\n }\n this.watermarkCanvas = canvas;\n this.watermarkCacheKey = cacheKey;\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX} Watermark prepared with pre-applied opacity`, {\n width: canvas.width,\n height: canvas.height,\n opacity,\n scaleFactor\n });\n }\n composeFrame(parameters) {\n const compositionPlan = this.getCompositionPlan(parameters);\n if (!compositionPlan.needsComposition) {\n return { frameToProcess: parameters.videoFrame, imageBitmap: null };\n }\n const outputDimensions = this.getOutputDimensions(parameters.videoFrame, compositionPlan.rotationDegrees, parameters.config);\n if (!outputDimensions) {\n return { frameToProcess: parameters.videoFrame, imageBitmap: null };\n }\n const width = outputDimensions.width;\n const height = outputDimensions.height;\n let fit = null;\n const sourceDimensions = this.getFrameDimensions(parameters.videoFrame, compositionPlan.rotationDegrees);\n const dimensionsMismatch = sourceDimensions.width !== width || sourceDimensions.height !== height;\n if (compositionPlan.needsResizing || dimensionsMismatch) {\n const shouldCover = parameters.config.shouldFillCamera === true && parameters.isScreenCapture !== true;\n fit = shouldCover ? calculateCoverFit(sourceDimensions.width, sourceDimensions.height, width, height) : this.calculateContainFit(sourceDimensions.width, sourceDimensions.height, width, height);\n }\n const context = this.ensureCompositionCanvas(width, height);\n context.clearRect(0, 0, width, height);\n if (fit && (fit.drawX > 0 || fit.drawY > 0)) {\n context.fillStyle = \"#000000\";\n context.fillRect(0, 0, width, height);\n }\n this.drawVideoFrame({\n context,\n videoFrame: parameters.videoFrame,\n rotationDegrees: compositionPlan.rotationDegrees,\n width,\n height,\n fit\n });\n this.applyOverlayIfNeeded(context, width, compositionPlan.shouldApplyOverlay, parameters.overlayConfig);\n this.applyWatermarkIfNeeded({\n context,\n videoWidth: width,\n videoHeight: height,\n needsWatermark: compositionPlan.needsWatermark,\n config: parameters.config\n });\n return this.buildCompositionResult(parameters.videoFrame);\n }\n getCompositionPlan(parameters) {\n const rotationDegrees = this.getFrameRotationDegrees(parameters.videoFrame, parameters.config);\n const shouldRotateFrame = rotationDegrees !== ROTATION_DEGREES_0;\n let needsWatermark = false;\n if (parameters.config.watermark && this.watermarkCanvas) {\n needsWatermark = true;\n }\n const needsResizing = this.detectResizingNeed(parameters.videoFrame, rotationDegrees, parameters.config);\n let needsComposition = false;\n if (parameters.shouldApplyOverlay) {\n needsComposition = true;\n }\n if (needsWatermark) {\n needsComposition = true;\n }\n if (shouldRotateFrame) {\n needsComposition = true;\n }\n if (needsResizing) {\n needsComposition = true;\n }\n return {\n rotationDegrees,\n shouldApplyOverlay: parameters.shouldApplyOverlay,\n needsWatermark,\n needsResizing,\n needsComposition\n };\n }\n applyOverlayIfNeeded(context, videoWidth, shouldApplyOverlay, overlayConfig) {\n if (!(shouldApplyOverlay && overlayConfig)) {\n return;\n }\n if (!this.overlayCanvas) {\n this.overlayCanvas = this.createOverlayCanvas(overlayConfig.text);\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX} Overlay canvas created`, {\n overlayWidth: this.overlayCanvas.width,\n overlayHeight: this.overlayCanvas.height\n });\n }\n if (!this.overlayCanvas) {\n return;\n }\n const overlayPosition = this.getOverlayPosition(this.overlayCanvas.width, videoWidth);\n context.drawImage(this.overlayCanvas, overlayPosition.horizontal, overlayPosition.vertical);\n }\n applyWatermarkIfNeeded(options) {\n const { context, videoWidth, videoHeight, needsWatermark, config } = options;\n const watermarkCanvas = this.watermarkCanvas;\n if (!(needsWatermark && watermarkCanvas && config.watermark)) {\n return;\n }\n const watermarkPosition = getWatermarkPosition({\n watermarkWidth: watermarkCanvas.width,\n watermarkHeight: watermarkCanvas.height,\n videoWidth,\n videoHeight,\n position: config.watermark.position\n });\n context.drawImage(watermarkCanvas, watermarkPosition.x, watermarkPosition.y);\n }\n buildCompositionResult(videoFrame) {\n const compositionCanvas = requireNonNull(this.compositionCanvas, \"Composition canvas must exist after ensureCompositionCanvas\");\n const imageBitmap = compositionCanvas.transferToImageBitmap();\n let frameInitialization = {};\n if (typeof videoFrame.timestamp === \"number\") {\n frameInitialization = {\n ...frameInitialization,\n timestamp: videoFrame.timestamp\n };\n }\n if (typeof videoFrame.duration === \"number\") {\n frameInitialization = {\n ...frameInitialization,\n duration: videoFrame.duration\n };\n }\n const frameToProcess = new VideoFrame(imageBitmap, frameInitialization);\n return { frameToProcess, imageBitmap };\n }\n createOverlayCanvas(text) {\n requireDefined(text, \"Overlay text is required\");\n const canvas = new OffscreenCanvas(1, 1);\n const context = requireNonNull(canvas.getContext(\"2d\"), \"Failed to get OffscreenCanvas context\");\n context.font = `${OVERLAY_FONT_SIZE}px ${OVERLAY_FONT_FAMILY}`;\n const textMetrics = context.measureText(text);\n const textWidth = textMetrics.width;\n const textHeight = OVERLAY_FONT_SIZE;\n const overlayWidth = Math.max(OVERLAY_MIN_WIDTH, textWidth + OVERLAY_PADDING * DOUBLE_VALUE);\n const overlayHeight = Math.max(OVERLAY_MIN_HEIGHT, textHeight + OVERLAY_PADDING * DOUBLE_VALUE);\n canvas.width = overlayWidth;\n canvas.height = overlayHeight;\n const redValue = OVERLAY_COLOR_CHANNEL_VALUE;\n const greenValue = OVERLAY_COLOR_CHANNEL_VALUE;\n const blueValue = OVERLAY_COLOR_CHANNEL_VALUE;\n const borderRadius = OVERLAY_BORDER_RADIUS;\n context.fillStyle = `rgba(${redValue}, ${greenValue}, ${blueValue}, ${OVERLAY_BACKGROUND_OPACITY})`;\n context.beginPath();\n context.roundRect(0, 0, overlayWidth, overlayHeight, borderRadius);\n context.fill();\n context.fillStyle = OVERLAY_TEXT_COLOR;\n context.font = `${OVERLAY_FONT_SIZE}px ${OVERLAY_FONT_FAMILY}`;\n context.textBaseline = \"middle\";\n context.textAlign = \"center\";\n const textHorizontalPosition = overlayWidth / DOUBLE_VALUE;\n const textVerticalPosition = overlayHeight / DOUBLE_VALUE;\n context.fillText(text, textHorizontalPosition, textVerticalPosition);\n return canvas;\n }\n getOverlayPosition(overlayWidth, videoWidth) {\n return {\n horizontal: videoWidth - overlayWidth - OVERLAY_PADDING,\n vertical: OVERLAY_PADDING\n };\n }\n ensureCompositionCanvas(width, height) {\n if (!this.compositionCanvas) {\n this.compositionCanvas = new OffscreenCanvas(width, height);\n this.compositionContext = requireNonNull(this.compositionCanvas.getContext(\"2d\", { willReadFrequently: false }), COMPOSITION_CONTEXT_ERROR_MESSAGE);\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX} Composition canvas created`, {\n width,\n height\n });\n return this.compositionContext;\n }\n if (!this.compositionContext) {\n this.compositionContext = requireNonNull(this.compositionCanvas.getContext(\"2d\", { willReadFrequently: false }), COMPOSITION_CONTEXT_ERROR_MESSAGE);\n return this.compositionContext;\n }\n const widthChanged = this.compositionCanvas.width !== width;\n const heightChanged = this.compositionCanvas.height !== height;\n let shouldResize = false;\n if (widthChanged) {\n shouldResize = true;\n }\n if (heightChanged) {\n shouldResize = true;\n }\n if (shouldResize) {\n this.compositionCanvas = new OffscreenCanvas(width, height);\n this.compositionContext = requireNonNull(this.compositionCanvas.getContext(\"2d\", { willReadFrequently: false }), COMPOSITION_CONTEXT_ERROR_MESSAGE);\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX} Composition canvas resized`, {\n width,\n height\n });\n return this.compositionContext;\n }\n return this.compositionContext;\n }\n getFrameRotationDegrees(videoFrame, config) {\n if (this.frameRotationDegrees !== null) {\n return this.frameRotationDegrees;\n }\n const rotation = this.determineFrameRotationDegrees(videoFrame, config);\n this.frameRotationDegrees = rotation;\n const rotationLog = JSON.stringify({\n rotationDegrees: rotation,\n configWidth: config.width,\n configHeight: config.height,\n settingsWidth: this.videoSettings?.width,\n settingsHeight: this.videoSettings?.height,\n facingMode: this.videoSettings?.facingMode,\n frameDisplayWidth: videoFrame.displayWidth,\n frameDisplayHeight: videoFrame.displayHeight\n });\n this.sendDebugLog(`${RECORDER_WORKER_LOG_PREFIX} Rotation decision`, rotationLog);\n return rotation;\n }\n determineFrameRotationDegrees(videoFrame, config) {\n const configWidth = config.width;\n const configHeight = config.height;\n let facingMode;\n let settingsRotation;\n if (this.videoSettings) {\n facingMode = this.videoSettings.facingMode;\n settingsRotation = this.videoSettings.rotation;\n }\n let orientationAngle;\n let windowOrientation;\n if (this.viewportMetadata) {\n orientationAngle = this.viewportMetadata.orientationAngle;\n windowOrientation = this.viewportMetadata.windowOrientation;\n }\n return calculateFrameRotationDegrees({\n isMobileDevice: this.isMobileDevice,\n targetWidth: configWidth,\n targetHeight: configHeight,\n frameWidth: videoFrame.displayWidth,\n frameHeight: videoFrame.displayHeight,\n facingMode,\n settingsRotation,\n orientationAngle,\n windowOrientation\n });\n }\n getFrameDimensions(videoFrame, rotationDegrees) {\n let width = videoFrame.displayWidth;\n let height = videoFrame.displayHeight;\n let shouldSwapDimensions = false;\n if (rotationDegrees === ROTATION_DEGREES_90) {\n shouldSwapDimensions = true;\n }\n if (rotationDegrees === ROTATION_DEGREES_270) {\n shouldSwapDimensions = true;\n }\n if (shouldSwapDimensions) {\n width = videoFrame.displayHeight;\n height = videoFrame.displayWidth;\n }\n return { width, height };\n }\n drawVideoFrame(parameters) {\n const { context, videoFrame, rotationDegrees, width, height, fit } = parameters;\n const sourceWidth = videoFrame.displayWidth;\n const sourceHeight = videoFrame.displayHeight;\n if (sourceWidth <= 0 || sourceHeight <= 0) {\n return;\n }\n context.setTransform(1, 0, 0, 1, 0, 0);\n if (!fit) {\n if (rotationDegrees === ROTATION_DEGREES_90) {\n context.translate(width, 0);\n context.rotate(ROTATION_RADIANS_90);\n context.drawImage(videoFrame, 0, 0, sourceWidth, sourceHeight);\n context.setTransform(1, 0, 0, 1, 0, 0);\n return;\n }\n if (rotationDegrees === ROTATION_DEGREES_270) {\n context.translate(0, height);\n context.rotate(ROTATION_RADIANS_270);\n context.drawImage(videoFrame, 0, 0, sourceWidth, sourceHeight);\n context.setTransform(1, 0, 0, 1, 0, 0);\n return;\n }\n context.drawImage(videoFrame, 0, 0, sourceWidth, sourceHeight);\n return;\n }\n if (rotationDegrees === ROTATION_DEGREES_0) {\n context.drawImage(videoFrame, fit.drawX, fit.drawY, fit.drawWidth, fit.drawHeight);\n return;\n }\n const centerX = fit.drawX + fit.drawWidth / DOUBLE_VALUE;\n const centerY = fit.drawY + fit.drawHeight / DOUBLE_VALUE;\n context.translate(centerX, centerY);\n if (rotationDegrees === ROTATION_DEGREES_90) {\n context.rotate(ROTATION_RADIANS_90);\n } else if (rotationDegrees === ROTATION_DEGREES_270) {\n context.rotate(ROTATION_RADIANS_270);\n } else {\n context.rotate(Math.PI * rotationDegrees / ROTATION_DEGREES_180);\n }\n const isSwappedRotation = rotationDegrees === ROTATION_DEGREES_90 || rotationDegrees === ROTATION_DEGREES_270;\n const scaleX = isSwappedRotation ? fit.drawWidth / sourceHeight : fit.drawWidth / sourceWidth;\n const scaleY = isSwappedRotation ? fit.drawHeight / sourceWidth : fit.drawHeight / sourceHeight;\n context.scale(scaleX, scaleY);\n context.drawImage(videoFrame, -sourceWidth / DOUBLE_VALUE, -sourceHeight / DOUBLE_VALUE, sourceWidth, sourceHeight);\n context.setTransform(1, 0, 0, 1, 0, 0);\n }\n detectResizingNeed(videoFrame, rotationDegrees, config) {\n return detectResizingNeed(videoFrame, rotationDegrees, config);\n }\n getOutputDimensions(videoFrame, rotationDegrees, config) {\n return getOutputDimensions(videoFrame, rotationDegrees, config);\n }\n calculateContainFit(sourceWidth, sourceHeight, targetWidth, targetHeight) {\n return calculateContainFit(sourceWidth, sourceHeight, targetWidth, targetHeight);\n }\n logWatermarkError(url, error) {\n const errorMessage = extractErrorMessage(error);\n this.logger.error(`${RECORDER_WORKER_LOG_PREFIX} Failed to load watermark. This is often caused by CORS if the image is on another domain. Try using a Data URL (base64) or ensure the server has Access-Control-Allow-Origin: *.`, {\n url,\n error: errorMessage\n });\n }\n }\n function calculateContainFit(sourceWidth, sourceHeight, targetWidth, targetHeight) {\n if (sourceWidth <= 0 || sourceHeight <= 0 || targetWidth <= 0 || targetHeight <= 0) {\n return {\n drawX: 0,\n drawY: 0,\n drawWidth: targetWidth,\n drawHeight: targetHeight\n };\n }\n const sourceAspect = sourceWidth / sourceHeight;\n const targetAspect = targetWidth / targetHeight;\n let drawWidth;\n let drawHeight;\n if (sourceAspect > targetAspect) {\n drawWidth = targetWidth;\n drawHeight = targetWidth / sourceAspect;\n } else {\n drawHeight = targetHeight;\n drawWidth = targetHeight * sourceAspect;\n }\n const roundedDrawWidth = Math.round(drawWidth);\n const roundedDrawHeight = Math.round(drawHeight);\n const drawX = (targetWidth - roundedDrawWidth) / 2;\n const drawY = (targetHeight - roundedDrawHeight) / 2;\n return {\n drawX: Math.round(drawX),\n drawY: Math.round(drawY),\n drawWidth: roundedDrawWidth,\n drawHeight: roundedDrawHeight\n };\n }\n function detectResizingNeed(videoFrame, rotationDegrees, config) {\n if (typeof config.width !== \"number\" || typeof config.height !== \"number\") {\n return false;\n }\n if (config.width <= 0 || config.height <= 0) {\n return false;\n }\n let frameWidth = videoFrame.displayWidth;\n let frameHeight = videoFrame.displayHeight;\n if (rotationDegrees === 90 || rotationDegrees === 270) {\n frameWidth = videoFrame.displayHeight;\n frameHeight = videoFrame.displayWidth;\n }\n if (frameWidth === config.width && frameHeight === config.height) {\n return false;\n }\n const sourceAspect = frameWidth / frameHeight;\n const targetAspect = config.width / config.height;\n const aspectRatioTolerance = 0.02;\n if (Math.abs(sourceAspect - targetAspect) > aspectRatioTolerance) {\n return true;\n }\n const sourcePixels = frameWidth * frameHeight;\n const targetPixels = config.width * config.height;\n return sourcePixels > targetPixels;\n }\n function getOutputDimensions(videoFrame, rotationDegrees, config) {\n if (typeof config.width === \"number\" && config.width > 0 && typeof config.height === \"number\" && config.height > 0) {\n return { width: config.width, height: config.height };\n }\n let width = videoFrame.displayWidth;\n let height = videoFrame.displayHeight;\n if (rotationDegrees === 90 || rotationDegrees === 270) {\n width = videoFrame.displayHeight;\n height = videoFrame.displayWidth;\n }\n if (width <= 0 || height <= 0) {\n return null;\n }\n return { width, height };\n }\n function calculateCoverFit(sourceWidth, sourceHeight, targetWidth, targetHeight) {\n if (sourceWidth <= 0 || sourceHeight <= 0 || targetWidth <= 0 || targetHeight <= 0) {\n return {\n drawX: 0,\n drawY: 0,\n drawWidth: targetWidth,\n drawHeight: targetHeight\n };\n }\n const scale = Math.max(targetWidth / sourceWidth, targetHeight / sourceHeight);\n const drawWidth = Math.round(sourceWidth * scale);\n const drawHeight = Math.round(sourceHeight * scale);\n return {\n drawX: Math.round((targetWidth - drawWidth) / 2),\n drawY: Math.round((targetHeight - drawHeight) / 2),\n drawWidth,\n drawHeight\n };\n }\n\n // src/core/processor/worker/recording-integrity.ts\n var EXCESSIVE_FRAME_ERROR_RATIO_THRESHOLD = 0.5;\n var MINIMUM_FRAMES_FOR_RATIO_CHECK = 10;\n function validateRecordingIntegrity(params) {\n const { frameCount, totalFrameErrors } = params;\n if (frameCount === 0) {\n return {\n isValid: false,\n error: {\n code: \"recording.no-video-frames\",\n message: \"Recording completed with zero video frames \u2014 the video track would be empty\"\n }\n };\n }\n const totalAttempts = frameCount + totalFrameErrors;\n if (totalAttempts >= MINIMUM_FRAMES_FOR_RATIO_CHECK && totalFrameErrors / totalAttempts > EXCESSIVE_FRAME_ERROR_RATIO_THRESHOLD) {\n return {\n isValid: false,\n error: {\n code: \"recording.excessive-frame-errors\",\n message: `Recording completed with excessive frame errors: ${totalFrameErrors} failures out of ${totalAttempts} attempts`\n }\n };\n }\n return { isValid: true };\n }\n\n // src/core/processor/worker/stop-finalization.ts\n var STOP_PENDING_WRITES_TIMEOUT_MILLISECONDS = 500;\n var STOP_PENDING_WRITES_POLL_INTERVAL_MILLISECONDS = 10;\n var ERROR_STOP_PENDING_WRITES_TIMEOUT = \"stop.pending-writes-timeout\";\n function createDefaultNowMilliseconds() {\n return () => performance.now();\n }\n function createDefaultWaitMilliseconds() {\n return (milliseconds) => new Promise((resolve) => {\n globalThis.setTimeout(resolve, milliseconds);\n });\n }\n async function waitForPendingWritesToDrain(dependencies) {\n let getNowMilliseconds = dependencies.getNowMilliseconds;\n if (!getNowMilliseconds) {\n getNowMilliseconds = createDefaultNowMilliseconds();\n }\n let waitMilliseconds = dependencies.waitMilliseconds;\n if (!waitMilliseconds) {\n waitMilliseconds = createDefaultWaitMilliseconds();\n }\n let timeoutMilliseconds = dependencies.timeoutMilliseconds;\n if (timeoutMilliseconds === undefined) {\n timeoutMilliseconds = STOP_PENDING_WRITES_TIMEOUT_MILLISECONDS;\n }\n const startedAtMilliseconds = getNowMilliseconds();\n let pendingWriteCount = dependencies.getPendingWriteCount();\n while (pendingWriteCount > 0) {\n const elapsedMilliseconds = getNowMilliseconds() - startedAtMilliseconds;\n if (elapsedMilliseconds >= timeoutMilliseconds) {\n throw new Error(ERROR_STOP_PENDING_WRITES_TIMEOUT);\n }\n await waitMilliseconds(STOP_PENDING_WRITES_POLL_INTERVAL_MILLISECONDS);\n pendingWriteCount = dependencies.getPendingWriteCount();\n }\n }\n\n // src/core/processor/worker/stop-transition.ts\n async function runStopTransition(dependencies) {\n await dependencies.finalizeStopSequence().then(() => dependencies.completeStop()).catch((error) => {\n return dependencies.recoverStopFailure().then(() => {\n throw error;\n });\n }).finally(() => {\n dependencies.clearStoppingFlag();\n });\n }\n\n // src/core/processor/worker/timestamp-manager.ts\n var DEFAULT_FRAME_RATE = 30;\n var MILLISECONDS_PER_SECOND2 = 1000;\n var MICROSECONDS_PER_SECOND = 1e6;\n var MAX_LEAD_SECONDS = 0.05;\n var MAX_LAG_SECONDS = 0.1;\n var MAX_DRIFT_CORRECTION_SECONDS = MAX_LAG_SECONDS;\n var DRIFT_OFFSET_DECAY_FACTOR = 0.5;\n var DRIFT_LOG_FRAME_INTERVAL = 90;\n var RECORDER_WORKER_LOG_PREFIX2 = \"[RecorderWorker]\";\n\n class TimestampManager {\n frameRate = DEFAULT_FRAME_RATE;\n lastVideoTimestamp = 0;\n baseVideoTimestamp = null;\n frameCount = 0;\n forceNextKeyFrame = false;\n driftOffset = 0;\n logger;\n getNowMilliseconds;\n constructor(dependencies) {\n this.logger = dependencies.logger;\n this.getNowMilliseconds = dependencies.getNowMilliseconds;\n }\n reset(frameRate) {\n let resolvedFrameRate = DEFAULT_FRAME_RATE;\n if (typeof frameRate === \"number\" && frameRate > 0) {\n resolvedFrameRate = frameRate;\n }\n this.frameRate = resolvedFrameRate;\n this.lastVideoTimestamp = 0;\n this.baseVideoTimestamp = null;\n this.frameCount = 0;\n this.forceNextKeyFrame = false;\n this.driftOffset = 0;\n }\n setFrameRate(frameRate) {\n this.frameRate = frameRate;\n }\n getFrameRate() {\n return this.frameRate;\n }\n getFrameCount() {\n return this.frameCount;\n }\n getLastVideoTimestamp() {\n return this.lastVideoTimestamp;\n }\n getBaseVideoTimestamp() {\n return this.baseVideoTimestamp;\n }\n calculateVideoFrameTimestamp(parameters) {\n if (this.frameRate <= 0) {\n throw new Error(\"Frame rate must be greater than zero\");\n }\n let rawTimestamp = this.getNowMilliseconds() / MILLISECONDS_PER_SECOND2;\n const hasTimestamp = typeof parameters.videoFrame.timestamp === \"number\" && parameters.videoFrame.timestamp !== null;\n if (hasTimestamp) {\n rawTimestamp = parameters.videoFrame.timestamp / MICROSECONDS_PER_SECOND;\n }\n if (this.baseVideoTimestamp === null) {\n this.baseVideoTimestamp = rawTimestamp;\n const logData = {\n baseVideoTimestamp: this.baseVideoTimestamp,\n recordingStartTime: parameters.recordingStartTime,\n difference: this.baseVideoTimestamp - parameters.recordingStartTime,\n pendingUpdates: parameters.pendingVisibilityUpdatesCount\n };\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX2} baseVideoTimestamp set`, logData);\n parameters.processPendingVisibilityUpdates();\n }\n if (this.baseVideoTimestamp === null) {\n throw new Error(\"Base video timestamp must be set\");\n }\n if (this.frameCount === 0 && this.lastVideoTimestamp > 0) {\n const originalBase = this.baseVideoTimestamp;\n const offset = rawTimestamp - originalBase;\n this.baseVideoTimestamp = rawTimestamp - this.lastVideoTimestamp;\n const frameTimestamp = this.lastVideoTimestamp;\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX2} First frame after source switch`, {\n rawTimestamp,\n originalBase,\n offset,\n adjustedBaseVideoTimestamp: this.baseVideoTimestamp,\n continuationTimestamp: this.lastVideoTimestamp,\n frameTimestamp,\n isScreenCapture: parameters.isScreenCapture\n });\n return frameTimestamp;\n }\n const normalizedTimestamp = rawTimestamp - this.baseVideoTimestamp - parameters.pausedDuration;\n let previousTimestamp = 0;\n if (this.lastVideoTimestamp > 0) {\n previousTimestamp = this.lastVideoTimestamp;\n }\n let frameTimestamp = normalizedTimestamp;\n if (normalizedTimestamp < previousTimestamp) {\n frameTimestamp = previousTimestamp + 1 / this.frameRate;\n }\n if (frameTimestamp < 0) {\n this.logger.warn(`${RECORDER_WORKER_LOG_PREFIX2} Negative frame timestamp detected, clamping to zero`, { frameTimestamp, normalizedTimestamp, previousTimestamp });\n return 0;\n }\n if (this.lastVideoTimestamp === 0) {\n this.lastVideoTimestamp = frameTimestamp;\n }\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX2} Frame timestamp calculation`, {\n rawTimestamp,\n baseVideoTimestamp: this.baseVideoTimestamp,\n normalizedTimestamp,\n previousTimestamp,\n frameTimestamp,\n lastVideoTimestamp: this.lastVideoTimestamp,\n isScreenCapture: parameters.isScreenCapture,\n frameCount: this.frameCount\n });\n return frameTimestamp;\n }\n prepareFrameTiming(parameters) {\n const frameDuration = 1 / this.frameRate;\n let adjustedTimestamp = parameters.frameTimestamp + this.driftOffset;\n const monotonicTimestamp = this.lastVideoTimestamp + frameDuration;\n if (adjustedTimestamp < monotonicTimestamp) {\n adjustedTimestamp = monotonicTimestamp;\n }\n let finalTimestamp = adjustedTimestamp;\n if (finalTimestamp - parameters.lastAudioTimestamp > MAX_LEAD_SECONDS) {\n finalTimestamp = parameters.lastAudioTimestamp + MAX_LEAD_SECONDS;\n }\n if (parameters.lastAudioTimestamp - finalTimestamp > MAX_LAG_SECONDS) {\n finalTimestamp = parameters.lastAudioTimestamp - MAX_LAG_SECONDS;\n }\n const minimumTimestamp = this.lastVideoTimestamp + frameDuration;\n if (finalTimestamp < minimumTimestamp) {\n finalTimestamp = minimumTimestamp;\n }\n let isKeyFrame = false;\n if (this.forceNextKeyFrame) {\n isKeyFrame = true;\n }\n this.driftOffset *= DRIFT_OFFSET_DECAY_FACTOR;\n return {\n finalTimestamp,\n frameDuration,\n isKeyFrame\n };\n }\n commitFrame(parameters) {\n this.frameCount += 1;\n this.lastVideoTimestamp = parameters.finalTimestamp;\n if (parameters.isKeyFrame) {\n this.forceNextKeyFrame = false;\n }\n let shouldLogDrift = false;\n let audioVideoDrift = 0;\n if (this.frameCount % DRIFT_LOG_FRAME_INTERVAL === 0 && parameters.audioProcessingActive) {\n audioVideoDrift = parameters.lastAudioTimestamp - this.lastVideoTimestamp;\n shouldLogDrift = true;\n }\n return {\n shouldLogDrift,\n audioVideoDrift,\n frameCount: this.frameCount,\n lastVideoTimestamp: this.lastVideoTimestamp\n };\n }\n handleSourceSwitch(lastAudioTimestamp) {\n if (this.baseVideoTimestamp === null) {\n throw new Error(\"Base video timestamp must be set for source switch\");\n }\n const minFrameDuration = 1 / this.frameRate;\n const rawDrift = lastAudioTimestamp - this.lastVideoTimestamp;\n this.driftOffset = clampValue(rawDrift, -MAX_DRIFT_CORRECTION_SECONDS, MAX_DRIFT_CORRECTION_SECONDS);\n const continuationTimestamp = Math.max(lastAudioTimestamp, this.lastVideoTimestamp) + minFrameDuration;\n const previousVideoTimestamp = this.lastVideoTimestamp;\n this.lastVideoTimestamp = continuationTimestamp;\n this.frameCount = 0;\n this.forceNextKeyFrame = true;\n return {\n continuationTimestamp,\n previousVideoTimestamp,\n minFrameDuration,\n rawDrift,\n driftOffset: this.driftOffset\n };\n }\n }\n function clampValue(value, min, max) {\n return Math.max(min, Math.min(max, value));\n }\n\n // src/core/processor/worker/types.ts\n var WORKER_MESSAGE_TYPE_START_AUDIO = \"startAudio\";\n var WORKER_MESSAGE_TYPE_PROBE = \"probe\";\n var WORKER_MESSAGE_TYPE_WARMUP = \"warmup\";\n var WORKER_MESSAGE_TYPE_AUDIO_CHUNK = \"audioChunk\";\n var WORKER_RESPONSE_TYPE_PROBE_RESULT = \"probeResult\";\n var WORKER_AUDIO_SAMPLE_FORMAT_F32_PLANAR = \"f32-planar\";\n\n // src/core/processor/worker/video-track-state-error.ts\n var ERROR_RECORDING_VIDEO_TRACK_ENDED = \"recording.video-track-ended\";\n function createVideoTrackEndedError(details = {}) {\n const error = new Error(`Video track ended before recording could start [${ERROR_RECORDING_VIDEO_TRACK_ENDED}]`);\n error.code = ERROR_RECORDING_VIDEO_TRACK_ENDED;\n error.isRecoverable = true;\n error.details = details;\n return error;\n }\n function isVideoTrackEndedError(error) {\n if (!(error instanceof Error)) {\n return false;\n }\n const errorWithCode = error;\n return errorWithCode.code === ERROR_RECORDING_VIDEO_TRACK_ENDED;\n }\n function assertVideoTrackLive(videoTrack, details = {}) {\n if (videoTrack.readyState === \"live\") {\n return;\n }\n throw createVideoTrackEndedError({\n ...details,\n trackId: videoTrack.id,\n readyState: videoTrack.readyState\n });\n }\n\n // src/core/processor/worker/visibility-tracker.ts\n var MILLISECONDS_PER_SECOND3 = 1000;\n var OVERLAY_LOG_FRAME_INTERVAL = 90;\n var RECORDER_WORKER_LOG_PREFIX3 = \"[RecorderWorker]\";\n var INITIAL_INTERVAL_INDEX = 0;\n var INITIAL_LAST_OVERLAY_TIMESTAMP = -1;\n\n class VisibilityTracker {\n hiddenIntervals = [];\n currentHiddenIntervalStart = null;\n pendingVisibilityUpdates = [];\n recordingStartTime = 0;\n isScreenCapture = false;\n intervalCursor = INITIAL_INTERVAL_INDEX;\n lastOverlayTimestamp = INITIAL_LAST_OVERLAY_TIMESTAMP;\n logger;\n constructor(dependencies) {\n this.logger = dependencies.logger;\n }\n reset(recordingStartTime, isScreenCapture) {\n this.hiddenIntervals = [];\n this.currentHiddenIntervalStart = null;\n this.pendingVisibilityUpdates = [];\n this.recordingStartTime = recordingStartTime;\n this.isScreenCapture = isScreenCapture;\n this.intervalCursor = INITIAL_INTERVAL_INDEX;\n this.lastOverlayTimestamp = INITIAL_LAST_OVERLAY_TIMESTAMP;\n }\n setRecordingStartTime(recordingStartTime) {\n this.recordingStartTime = recordingStartTime;\n }\n setIsScreenCapture(isScreenCapture) {\n this.isScreenCapture = isScreenCapture;\n }\n getPendingUpdatesCount() {\n return this.pendingVisibilityUpdates.length;\n }\n shouldApplyOverlay(timestamp, frameCount) {\n if (this.isScreenCapture) {\n return false;\n }\n const shouldApplyCurrentInterval = this.currentHiddenIntervalStart !== null && timestamp >= this.currentHiddenIntervalStart;\n let shouldApply = false;\n if (shouldApplyCurrentInterval) {\n shouldApply = true;\n }\n if (!shouldApplyCurrentInterval) {\n shouldApply = this.shouldApplyCompletedIntervalOverlay(timestamp);\n }\n if (frameCount % OVERLAY_LOG_FRAME_INTERVAL === 0) {\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX3} Overlay check`, {\n timestamp,\n shouldApply,\n frameCount,\n intervalsCount: this.hiddenIntervals.length,\n intervalCursor: this.intervalCursor\n });\n }\n return shouldApply;\n }\n shouldApplyCompletedIntervalOverlay(timestamp) {\n if (this.hiddenIntervals.length === 0) {\n return false;\n }\n if (this.lastOverlayTimestamp !== INITIAL_LAST_OVERLAY_TIMESTAMP && timestamp < this.lastOverlayTimestamp) {\n this.intervalCursor = INITIAL_INTERVAL_INDEX;\n }\n this.lastOverlayTimestamp = timestamp;\n let activeCursor = this.intervalCursor;\n while (activeCursor < this.hiddenIntervals.length) {\n const interval = this.hiddenIntervals[activeCursor];\n if (timestamp < interval.start) {\n break;\n }\n if (timestamp <= interval.end) {\n this.intervalCursor = activeCursor;\n return true;\n }\n activeCursor += 1;\n }\n this.intervalCursor = activeCursor;\n return false;\n }\n handleUpdateVisibility(isHidden, timestamp, hasBaseVideoTimestamp, pausedDuration) {\n if (!hasBaseVideoTimestamp) {\n this.pendingVisibilityUpdates = [\n ...this.pendingVisibilityUpdates,\n { isHidden, timestamp }\n ];\n return;\n }\n this.processVisibilityUpdate(isHidden, timestamp, pausedDuration);\n }\n flushPendingUpdates(pausedDuration) {\n if (this.pendingVisibilityUpdates.length === 0) {\n return;\n }\n for (const update of this.pendingVisibilityUpdates) {\n this.processVisibilityUpdate(update.isHidden, update.timestamp, pausedDuration);\n }\n this.pendingVisibilityUpdates = [];\n }\n processVisibilityUpdate(isHidden, timestamp, pausedDuration) {\n const timestampSeconds = timestamp / MILLISECONDS_PER_SECOND3;\n const normalizedTimestamp = timestampSeconds - this.recordingStartTime - pausedDuration;\n if (isHidden && this.currentHiddenIntervalStart === null) {\n this.currentHiddenIntervalStart = Math.max(0, normalizedTimestamp);\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX3} Started hidden interval`, {\n start: this.currentHiddenIntervalStart\n });\n }\n if (isHidden) {\n return;\n }\n if (this.currentHiddenIntervalStart === null) {\n return;\n }\n const endTimestamp = Math.max(0, normalizedTimestamp);\n if (endTimestamp > this.currentHiddenIntervalStart) {\n const interval = {\n start: this.currentHiddenIntervalStart,\n end: endTimestamp\n };\n this.hiddenIntervals = [...this.hiddenIntervals, interval];\n this.logger.debug(`${RECORDER_WORKER_LOG_PREFIX3} Completed hidden interval`, {\n interval,\n duration: endTimestamp - this.currentHiddenIntervalStart,\n totalIntervals: this.hiddenIntervals.length\n });\n } else {\n this.logger.warn(`${RECORDER_WORKER_LOG_PREFIX3} Invalid interval (end <= start), discarding`);\n }\n this.currentHiddenIntervalStart = null;\n }\n }\n\n // src/core/processor/worker/recorder-worker.ts\n var CHUNK_SIZE = 16 * 1024 * 1024;\n var DEFAULT_OUTPUT_FORMAT = \"mp4\";\n var MILLISECONDS_PER_SECOND4 = 1000;\n var ERROR_AUDIO_BITRATE_INVALID = \"Audio bitrate must be greater than zero\";\n var ERROR_AUDIO_SAMPLE_RATE_INVALID = \"Audio sample rate must be greater than zero\";\n var ERROR_AUDIO_CHANNELS_INVALID = \"Audio channels must be greater than zero\";\n var ERROR_AUDIO_FRAMES_INVALID = \"Audio frames must be greater than zero\";\n var STEREO_CHANNEL_COUNT = 2;\n var AUDIO_SAMPLE_AVERAGE_SCALE = 0.5;\n var STOP_PENDING_WRITES_TIMEOUT_MILLISECONDS2 = 500;\n var MP4_FAST_START_DISABLED = false;\n var VIDEO_LATENCY_MODE_REALTIME = \"realtime\";\n var VIDEO_CONTENT_HINT_MOTION = \"motion\";\n var VIDEO_HARDWARE_ACCELERATION_PREFERENCE = \"prefer-hardware\";\n var DEFAULT_VIDEO_WIDTH = 1280;\n var DEFAULT_VIDEO_HEIGHT = 720;\n var DEFAULT_VIDEO_BITRATE = 2500000;\n var CONSECUTIVE_FRAME_ERROR_THRESHOLD = 5;\n var EARLY_FRAME_THRESHOLD = 10;\n\n class RecorderWorker {\n output = null;\n videoSource = null;\n audioSource = null;\n videoProcessor = null;\n audioProcessor = null;\n config = null;\n videoProcessingActive = false;\n isStopping = false;\n isFinalized = false;\n bufferTracker;\n audioState;\n timestampManager;\n frameCompositor;\n overlayConfig = null;\n visibilityTracker;\n recordingStartTime = 0;\n isScreenCapture = false;\n totalSize = 0;\n expectedAudioChannels = null;\n expectedAudioSampleRate = null;\n pendingWriteCount = 0;\n resolvedHardwareAcceleration = VIDEO_HARDWARE_ACCELERATION_PREFERENCE;\n hwAccelCacheKey = null;\n hasWarmedVideoEncoder = false;\n consecutiveFrameErrors = 0;\n videoProcessingRunId = 0;\n totalFrameErrors = 0;\n totalFramesProcessed = 0;\n isAudioOnly = false;\n constructor() {\n this.bufferTracker = new BufferTracker({\n getBufferSize: () => this.totalSize,\n onBufferUpdate: (size, formatted) => {\n const response = {\n type: \"bufferUpdate\",\n size,\n formatted\n };\n self.postMessage(response);\n },\n setInterval: (handler, timeout) => self.setInterval(handler, timeout),\n clearInterval: (intervalId) => self.clearInterval(intervalId)\n });\n this.audioState = new AudioState({\n getNowMilliseconds: () => performance.now()\n });\n this.visibilityTracker = new VisibilityTracker({\n logger: {\n debug: (message, data) => logger.debug(message, data),\n warn: (message, data) => logger.warn(message, data)\n }\n });\n this.timestampManager = new TimestampManager({\n logger: {\n debug: (message, data) => logger.debug(message, data),\n warn: (message, data) => logger.warn(message, data)\n },\n getNowMilliseconds: () => performance.now()\n });\n this.frameCompositor = new FrameCompositor({\n logger: {\n debug: (message, data) => logger.debug(message, data),\n warn: (message, data) => logger.warn(message, data),\n error: (message, data) => logger.error(message, data)\n },\n fetchResource: (input, init) => fetch(input, init),\n createImageBitmap: (image) => createImageBitmap(image),\n sendDebugLog: (_message, _payload) => {\n return;\n }\n });\n self.addEventListener(\"message\", this.handleMessage);\n }\n shouldIgnoreMessage() {\n if (this.isStopping) {\n return true;\n }\n if (this.isFinalized) {\n return true;\n }\n return false;\n }\n handleAsyncOperation(operation, context) {\n operation.catch((error) => {\n logger.error(`[RecorderWorker] Error in ${context}:`, error);\n if (isVideoTrackEndedError(error)) {\n let metadata = {};\n if (error.details) {\n metadata = error.details;\n }\n this.sendFatalError({\n code: error.code,\n message: \"Video track ended before recording could start\",\n isRecoverable: true,\n metadata\n });\n return;\n }\n this.sendError(error);\n });\n }\n handleProbe() {\n const response = {\n type: WORKER_RESPONSE_TYPE_PROBE_RESULT,\n hasMediaStreamTrackProcessor: typeof MediaStreamTrackProcessor !== \"undefined\",\n hasVideoFrame: typeof VideoFrame !== \"undefined\",\n hasAudioData: typeof AudioData !== \"undefined\",\n hasOffscreenCanvas: typeof OffscreenCanvas !== \"undefined\",\n hasCreateImageBitmap: typeof createImageBitmap !== \"undefined\",\n hasReadableStream: typeof ReadableStream !== \"undefined\"\n };\n self.postMessage(response);\n }\n handleWarmup(config) {\n const hardwareAccelerationPromise = this.resolveHardwareAcceleration(config).then((result) => {\n this.resolvedHardwareAcceleration = result;\n return this.warmUpVideoEncoderOnce(config.codec, result);\n }).catch((error) => {\n logger.warn(\"[RecorderWorker] Warmup video encoder preparation failed\", { error: extractErrorMessage(error) });\n });\n const watermarkPromise = this.prepareWatermark(config).catch((error) => {\n logger.warn(\"[RecorderWorker] Warmup watermark preparation failed\", {\n error: extractErrorMessage(error)\n });\n });\n this.handleAsyncOperation(Promise.all([hardwareAccelerationPromise, watermarkPromise]).then(() => {\n return;\n }), \"handleWarmup\");\n }\n warmUpVideoEncoderOnce(codec, hardwareAcceleration) {\n if (this.hasWarmedVideoEncoder) {\n return Promise.resolve();\n }\n this.hasWarmedVideoEncoder = true;\n return warmUpVideoEncoder(codec, hardwareAcceleration);\n }\n handleMessage = (event) => {\n const message = event.data;\n logger.debug(\"[RecorderWorker] Received message:\", { type: message.type });\n switch (message.type) {\n case WORKER_MESSAGE_TYPE_PROBE:\n this.handleProbe();\n return;\n case WORKER_MESSAGE_TYPE_WARMUP:\n this.handleWarmup(message.config);\n return;\n case \"start\":\n this.handleStartMessage(message);\n return;\n case WORKER_MESSAGE_TYPE_START_AUDIO:\n this.handleStartAudioMessage(message);\n return;\n case \"pause\":\n this.handlePause();\n return;\n case \"resume\":\n this.handleResume();\n return;\n case \"stop\":\n this.handleStopMessage();\n return;\n case \"toggleMute\":\n this.handleToggleMute();\n return;\n case WORKER_MESSAGE_TYPE_AUDIO_CHUNK:\n this.handleAudioChunk(message);\n return;\n case \"switchSource\":\n this.handleSwitchSourceMessage(message);\n return;\n case \"updateFps\":\n this.handleUpdateFps(message.fps);\n return;\n case \"updateVisibility\":\n this.visibilityTracker.handleUpdateVisibility(message.isHidden, message.timestamp, this.timestampManager.getBaseVideoTimestamp() !== null, this.audioState.getPausedDuration());\n return;\n case \"updateSourceType\":\n this.handleUpdateSourceType(message.isScreenCapture);\n return;\n case \"requestStats\":\n this.sendRecordingStats();\n return;\n default:\n this.sendError(new Error(`Unknown message type: ${message.type}`));\n }\n };\n handleStartMessage(message) {\n if (this.shouldIgnoreMessage()) {\n logger.debug(\"[RecorderWorker] start ignored (stopping/finalized)\");\n return;\n }\n let videoTrack = null;\n if (message.videoTrack) {\n videoTrack = message.videoTrack;\n }\n let videoStream = null;\n if (message.videoStream) {\n videoStream = message.videoStream;\n }\n this.handleAsyncOperation(this.handleStart(message, videoTrack, videoStream), \"handleStart\");\n }\n handleStartAudioMessage(message) {\n if (this.shouldIgnoreMessage()) {\n logger.debug(\"[RecorderWorker] startAudio ignored (stopping/finalized)\");\n return;\n }\n this.handleAsyncOperation(this.handleAudioOnlyStart(message), \"handleAudioOnlyStart\");\n }\n async handleAudioOnlyStart(message) {\n const audioConfig = validateAudioOnlyWorkerConfig(message.config, message.audioConfig);\n this.isStopping = false;\n this.isFinalized = false;\n if (this.output) {\n await this.cleanup();\n }\n this.isAudioOnly = true;\n this.config = null;\n this.audioState.reset();\n this.pendingWriteCount = 0;\n this.expectedAudioChannels = audioConfig.numberOfChannels;\n this.expectedAudioSampleRate = audioConfig.sampleRate;\n this.createOutput(message.config.format);\n this.audioSource = new AudioSampleSource({\n codec: message.config.audioCodec,\n bitrate: message.config.audioBitrate,\n bitrateMode: \"variable\"\n });\n const output = requireNonNull(this.output, \"Output must be initialized before adding audio track\");\n output.addAudioTrack(this.audioSource);\n this.audioState.setProcessingActive(true);\n await output.start();\n this.bufferTracker.start();\n this.sendReady();\n this.sendStateChange(\"recording\");\n }\n handleStopMessage() {\n if (this.shouldIgnoreMessage()) {\n logger.debug(\"[RecorderWorker] stop ignored (stopping/finalized)\");\n return;\n }\n this.handleAsyncOperation(this.handleStop(), \"handleStop\");\n }\n handleSwitchSourceMessage(message) {\n let videoTrack = null;\n if (message.videoTrack) {\n videoTrack = message.videoTrack;\n }\n let videoStream = null;\n if (message.videoStream) {\n videoStream = message.videoStream;\n }\n this.handleAsyncOperation(this.handleSwitchSource(videoTrack, videoStream), \"handleSwitchSource\");\n }\n validateConfig(config) {\n requireDefined(config, \"Transcode config is required\");\n if (config.width !== undefined && config.width <= 0) {\n throw new Error(\"Video width must be greater than zero\");\n }\n if (config.height !== undefined && config.height <= 0) {\n throw new Error(\"Video height must be greater than zero\");\n }\n if (config.fps !== undefined && config.fps <= 0) {\n throw new Error(\"Frame rate must be greater than zero\");\n }\n if (config.bitrate !== undefined && typeof config.bitrate === \"number\" && config.bitrate <= 0) {\n throw new Error(\"Bitrate must be greater than zero\");\n }\n if (config.keyFrameInterval <= 0) {\n throw new Error(\"Key frame interval must be greater than zero\");\n }\n }\n validateFormat(format) {\n if (format !== \"mp4\") {\n throw new Error(`Format ${format} is not yet supported in worker. Only MP4 is currently supported.`);\n }\n }\n initializeRecordingState(config) {\n this.config = config;\n this.timestampManager.reset(config.fps);\n this.audioState.reset();\n this.expectedAudioChannels = null;\n this.expectedAudioSampleRate = null;\n this.pendingWriteCount = 0;\n this.videoProcessingActive = false;\n this.frameCompositor.resetRecordingState();\n this.recordingStartTime = 0;\n this.visibilityTracker.reset(this.recordingStartTime, this.isScreenCapture);\n }\n setupOverlayConfig(overlayConfig) {\n let nextOverlayConfig = null;\n if (overlayConfig) {\n nextOverlayConfig = {\n enabled: overlayConfig.enabled,\n text: overlayConfig.text\n };\n }\n this.overlayConfig = nextOverlayConfig;\n let recordingStartTimeSeconds = performance.now() / MILLISECONDS_PER_SECOND4;\n if (overlayConfig && overlayConfig.recordingStartTime !== undefined) {\n recordingStartTimeSeconds = overlayConfig.recordingStartTime / MILLISECONDS_PER_SECOND4;\n }\n this.recordingStartTime = recordingStartTimeSeconds;\n this.visibilityTracker.setRecordingStartTime(this.recordingStartTime);\n const logData = {\n hasOverlayConfig: !!this.overlayConfig,\n overlayEnabled: this.overlayConfig?.enabled,\n overlayText: this.overlayConfig?.text,\n recordingStartTime: this.recordingStartTime\n };\n logger.debug(\"[RecorderWorker] Overlay config initialized\", logData);\n }\n createOutput(container = \"mp4\") {\n const writable = new WritableStream({\n write: (chunk) => this.handleOutputChunkWrite(chunk)\n });\n this.output = new Output({\n format: container === \"webm\" ? new WebMOutputFormat : new Mp4OutputFormat({\n fastStart: MP4_FAST_START_DISABLED\n }),\n target: new StreamTarget(writable, {\n chunked: true,\n chunkSize: CHUNK_SIZE\n })\n });\n }\n decrementPendingWriteCount() {\n this.pendingWriteCount -= 1;\n if (this.pendingWriteCount < 0) {\n this.pendingWriteCount = 0;\n }\n }\n handleOutputChunkWrite(chunk) {\n this.pendingWriteCount += 1;\n const writeOperation = Promise.resolve().then(() => {\n this.sendChunk(chunk.data, chunk.position);\n });\n return writeOperation.then(() => {\n this.decrementPendingWriteCount();\n }, (error) => {\n this.decrementPendingWriteCount();\n throw error;\n });\n }\n createVideoSource(config, hardwareAcceleration) {\n const fps = this.timestampManager.getFrameRate();\n const videoSourceOptions = {\n codec: config.codec,\n width: config.width,\n height: config.height,\n sizeChangeBehavior: \"contain\",\n alpha: \"discard\",\n bitrateMode: \"variable\",\n latencyMode: config.latencyMode || VIDEO_LATENCY_MODE_REALTIME,\n contentHint: VIDEO_CONTENT_HINT_MOTION,\n hardwareAcceleration,\n keyFrameInterval: config.keyFrameInterval,\n bitrate: this.deserializeBitrate(config.bitrate)\n };\n this.videoSource = new VideoSampleSource(videoSourceOptions);\n const output = requireNonNull(this.output, \"Output must be initialized before adding video track\");\n const trackOptions = {};\n if (fps !== undefined) {\n trackOptions.frameRate = fps;\n }\n output.addVideoTrack(this.videoSource, trackOptions);\n }\n async resolveHardwareAcceleration(config) {\n if (typeof VideoEncoder === \"undefined\") {\n return VIDEO_HARDWARE_ACCELERATION_PREFERENCE;\n }\n let width = DEFAULT_VIDEO_WIDTH;\n if (config.width !== undefined && config.width > 0) {\n width = config.width;\n }\n let height = DEFAULT_VIDEO_HEIGHT;\n if (config.height !== undefined && config.height > 0) {\n height = config.height;\n }\n let bitrate = DEFAULT_VIDEO_BITRATE;\n if (typeof config.bitrate === \"number\" && config.bitrate > 0) {\n bitrate = config.bitrate;\n }\n const cacheKey = `${config.codec}:${width}:${height}:${config.bitrate}`;\n if (this.hwAccelCacheKey === cacheKey) {\n return this.resolvedHardwareAcceleration;\n }\n const result = await resolveVideoHardwareAcceleration(config.codec, width, height, bitrate);\n this.hwAccelCacheKey = cacheKey;\n return result;\n }\n setupAudioSource(audioConfig, config) {\n if (!audioConfig) {\n return;\n }\n if (!config.audioBitrate) {\n return;\n }\n if (!config.audioCodec) {\n return;\n }\n if (config.audioBitrate <= 0) {\n throw new Error(ERROR_AUDIO_BITRATE_INVALID);\n }\n if (audioConfig.sampleRate <= 0) {\n throw new Error(ERROR_AUDIO_SAMPLE_RATE_INVALID);\n }\n if (audioConfig.numberOfChannels <= 0) {\n throw new Error(ERROR_AUDIO_CHANNELS_INVALID);\n }\n this.expectedAudioChannels = audioConfig.numberOfChannels;\n this.expectedAudioSampleRate = audioConfig.sampleRate;\n this.audioSource = new AudioSampleSource({\n codec: config.audioCodec,\n bitrate: config.audioBitrate,\n bitrateMode: \"variable\"\n });\n const output = requireNonNull(this.output, \"Output must be initialized before adding audio track\");\n output.addAudioTrack(this.audioSource);\n this.audioState.setProcessingActive(true);\n }\n setupAudioStream(audioStream, config) {\n if (!config.audioBitrate) {\n return;\n }\n if (!config.audioCodec) {\n return;\n }\n if (config.audioBitrate <= 0) {\n throw new Error(ERROR_AUDIO_BITRATE_INVALID);\n }\n this.expectedAudioChannels = null;\n this.expectedAudioSampleRate = null;\n this.audioSource = new AudioSampleSource({\n codec: config.audioCodec,\n bitrate: config.audioBitrate,\n bitrateMode: \"variable\"\n });\n const output = requireNonNull(this.output, \"Output must be initialized before adding audio track\");\n output.addAudioTrack(this.audioSource);\n this.audioProcessor = audioStream.getReader();\n this.audioState.setProcessingActive(true);\n this.processAudioData();\n }\n async handleStart(message, videoTrack, videoStream) {\n const audioConfig = message.audioConfig;\n let audioStream = null;\n if (message.audioStream) {\n audioStream = message.audioStream;\n }\n const config = message.config;\n const overlayConfig = message.overlayConfig;\n this.validateConfig(config);\n logger.debug(\"[RecorderWorker] handleStart called\", {\n hasVideoTrack: !!videoTrack,\n hasVideoStream: !!videoStream,\n hasAudioStream: !!audioStream,\n hasAudioConfig: !!audioConfig,\n config: {\n width: config.width,\n height: config.height,\n fps: config.fps,\n bitrate: config.bitrate\n },\n hasOverlayConfig: !!overlayConfig,\n overlayConfig\n });\n this.isStopping = false;\n this.isFinalized = false;\n if (this.output) {\n logger.debug(\"[RecorderWorker] Cleaning up existing output\");\n await this.cleanup();\n }\n this.isAudioOnly = false;\n this.initializeRecordingState(config);\n const resolvedVideoSettings = message.videoSettings;\n if (resolvedVideoSettings) {\n this.frameCompositor.setVideoSettings(resolvedVideoSettings);\n } else {\n this.frameCompositor.setVideoSettings(null);\n }\n const resolvedViewportMetadata = message.viewportMetadata;\n if (resolvedViewportMetadata) {\n this.frameCompositor.setViewportMetadata(resolvedViewportMetadata);\n } else {\n this.frameCompositor.setViewportMetadata(null);\n }\n this.frameCompositor.setIsMobileDevice(message.isMobileDevice === true);\n this.setupOverlayConfig(overlayConfig);\n let format = config.format;\n if (!format) {\n format = DEFAULT_OUTPUT_FORMAT;\n }\n this.validateFormat(format);\n const hwAccelPromise = this.resolveHardwareAcceleration(config);\n const watermarkPromise = this.prepareWatermark(config);\n this.createOutput();\n this.resolvedHardwareAcceleration = await hwAccelPromise;\n await watermarkPromise;\n this.sendEncoderAcceleration(this.resolvedHardwareAcceleration);\n this.createVideoSource(config, this.resolvedHardwareAcceleration);\n if (videoStream) {\n this.setupVideoProcessingFromStream(videoStream);\n }\n if (!videoStream && videoTrack) {\n this.setupVideoProcessing(videoTrack);\n }\n if (audioStream) {\n this.setupAudioStream(audioStream, config);\n } else {\n this.setupAudioSource(audioConfig, config);\n }\n const output = requireNonNull(this.output, \"Output must be initialized before starting\");\n await output.start();\n this.bufferTracker.start();\n this.sendReady();\n this.sendStateChange(\"recording\");\n }\n prepareWatermark(config) {\n if (!config.watermark) {\n return Promise.resolve();\n }\n return this.frameCompositor.prepareWatermark(config);\n }\n setupVideoProcessing(videoTrack) {\n if (!this.videoSource) {\n return;\n }\n if (typeof MediaStreamTrackProcessor === \"undefined\") {\n throw new Error(\"MediaStreamTrackProcessor is not available in worker\");\n }\n assertVideoTrackLive(videoTrack, { stage: \"worker-stream-create\" });\n const processor = new MediaStreamTrackProcessor({ track: videoTrack });\n this.videoProcessor = processor.readable.getReader();\n this.videoProcessingActive = true;\n this.processVideoFrames();\n }\n setupVideoProcessingFromStream(videoStream) {\n if (!this.videoSource) {\n return;\n }\n this.videoProcessor = videoStream.getReader();\n this.videoProcessingActive = true;\n this.processVideoFrames();\n }\n async handlePausedVideoFrame(videoProcessor) {\n const pausedResult = await videoProcessor.read();\n if (pausedResult.done) {\n return false;\n }\n if (pausedResult.value) {\n pausedResult.value.close();\n }\n return true;\n }\n async drainReader(reader) {\n while (true) {\n const result = await reader.read().catch(() => null);\n if (!result || result.done) {\n return;\n }\n result.value?.close();\n }\n }\n async processVideoFrame(videoFrame) {\n const videoSource = requireInitialized(this.videoSource, \"Video source\");\n const config = requireInitialized(this.config, \"Transcode config\");\n const pausedDuration = this.audioState.getPausedDuration();\n const frameTimestamp = this.timestampManager.calculateVideoFrameTimestamp({\n videoFrame,\n pausedDuration,\n recordingStartTime: this.recordingStartTime,\n pendingVisibilityUpdatesCount: this.visibilityTracker.getPendingUpdatesCount(),\n processPendingVisibilityUpdates: () => {\n this.visibilityTracker.flushPendingUpdates(pausedDuration);\n },\n isScreenCapture: this.isScreenCapture\n });\n const overlayConfig = this.overlayConfig;\n let shouldApplyOverlay = false;\n if (overlayConfig?.enabled && !this.isScreenCapture) {\n shouldApplyOverlay = this.visibilityTracker.shouldApplyOverlay(frameTimestamp, this.timestampManager.getFrameCount());\n }\n const compositionResult = this.frameCompositor.composeFrame({\n videoFrame,\n overlayConfig,\n shouldApplyOverlay,\n config,\n isScreenCapture: this.isScreenCapture\n });\n if (compositionResult.imageBitmap) {\n compositionResult.imageBitmap.close();\n }\n const lastAudioTimestamp = this.audioState.getLastAudioTimestamp();\n const frameTiming = this.timestampManager.prepareFrameTiming({\n frameTimestamp,\n lastAudioTimestamp\n });\n const sample = new VideoSample(compositionResult.frameToProcess, {\n timestamp: frameTiming.finalTimestamp,\n duration: frameTiming.frameDuration\n });\n let videoSampleOptions;\n if (frameTiming.isKeyFrame) {\n videoSampleOptions = { keyFrame: true };\n }\n const addError = await videoSource.add(sample, videoSampleOptions).then(() => null).catch((error) => {\n const errorMessage = extractErrorMessage(error);\n logger.warn(`[RecorderWorker] Failed to add video frame: ${errorMessage}`);\n this.handleVideoFrameAddError();\n return error;\n });\n sample.close();\n if (compositionResult.frameToProcess !== videoFrame) {\n compositionResult.frameToProcess.close();\n }\n if (!addError) {\n this.handleVideoFrameAddSuccess();\n const commitResult = this.timestampManager.commitFrame({\n finalTimestamp: frameTiming.finalTimestamp,\n isKeyFrame: frameTiming.isKeyFrame,\n lastAudioTimestamp,\n audioProcessingActive: this.audioState.isActive(),\n isScreenCapture: this.isScreenCapture\n });\n if (commitResult.shouldLogDrift) {\n logger.debug(\"[RecorderWorker] AV drift metrics\", {\n frameCount: commitResult.frameCount,\n lastAudioTimestamp,\n lastVideoTimestamp: commitResult.lastVideoTimestamp,\n audioVideoDrift: commitResult.audioVideoDrift,\n isScreenCapture: this.isScreenCapture\n });\n }\n }\n videoFrame.close();\n }\n async processVideoFrames() {\n if (!(this.videoProcessor && this.videoSource)) {\n return;\n }\n const ownedProcessor = this.videoProcessor;\n const currentRunId = ++this.videoProcessingRunId;\n while (this.videoProcessingActive && !this.isStopping && currentRunId === this.videoProcessingRunId) {\n if (this.audioState.getIsPaused()) {\n const shouldContinue = await this.handlePausedVideoFrame(ownedProcessor);\n if (!shouldContinue) {\n break;\n }\n continue;\n }\n const result = await ownedProcessor.read();\n if (result.done) {\n break;\n }\n const videoFrame = result.value;\n if (!videoFrame) {\n continue;\n }\n await this.processVideoFrame(videoFrame).catch((error) => {\n const errorMessage = extractErrorMessage(error);\n logger.error(\"[RecorderWorker] Error processing video frame\", errorMessage);\n videoFrame.close();\n });\n }\n if (ownedProcessor === this.videoProcessor) {\n await this.drainReader(ownedProcessor);\n }\n }\n handlePausedAudioData(audioData) {\n audioData.close();\n }\n createAudioBuffer(audioData) {\n const numberOfFrames = audioData.numberOfFrames;\n if (numberOfFrames <= 0) {\n throw new Error(ERROR_AUDIO_FRAMES_INVALID);\n }\n const numberOfChannels = audioData.numberOfChannels;\n if (numberOfChannels <= 0) {\n throw new Error(ERROR_AUDIO_CHANNELS_INVALID);\n }\n const audioBuffer = new Float32Array(numberOfFrames * numberOfChannels);\n let channelIndex = 0;\n while (channelIndex < numberOfChannels) {\n const startIndex = channelIndex * numberOfFrames;\n const endIndex = startIndex + numberOfFrames;\n const channelBuffer = audioBuffer.subarray(startIndex, endIndex);\n audioData.copyTo(channelBuffer, { planeIndex: channelIndex });\n channelIndex += 1;\n }\n return audioBuffer;\n }\n createAudioSample(audioBuffer, audioTimestamp, sampleRate, numberOfChannels) {\n if (sampleRate <= 0) {\n throw new Error(ERROR_AUDIO_SAMPLE_RATE_INVALID);\n }\n if (numberOfChannels <= 0) {\n throw new Error(ERROR_AUDIO_CHANNELS_INVALID);\n }\n let bufferToWrite = audioBuffer;\n if (this.audioState.getIsMuted()) {\n bufferToWrite = new Float32Array(audioBuffer.length);\n }\n return new AudioSample({\n data: bufferToWrite,\n format: WORKER_AUDIO_SAMPLE_FORMAT_F32_PLANAR,\n numberOfChannels,\n sampleRate,\n timestamp: audioTimestamp\n });\n }\n async processAudioSample(audioData, audioSample, audioTimestamp, duration) {\n const audioSource = requireInitialized(this.audioSource, \"Audio source\");\n await audioSource.add(audioSample).catch((error) => {\n const errorMessage = extractErrorMessage(error);\n logger.warn(`[RecorderWorker] Failed to add audio sample: ${errorMessage}`);\n });\n this.audioState.updateLastAudioTimestamp(audioTimestamp, duration);\n audioSample.close();\n audioData.close();\n }\n async processAudioData() {\n if (!(this.audioProcessor && this.audioSource)) {\n return;\n }\n while (this.audioState.isActive() && !this.isStopping) {\n const result = await this.audioProcessor.read();\n if (result.done) {\n this.audioState.setProcessingActive(false);\n break;\n }\n const audioData = result.value;\n if (this.shouldSkipAudioData(audioData)) {\n continue;\n }\n const audioFormat = this.getAudioDataFormat(audioData);\n if (!audioFormat) {\n continue;\n }\n const audioBuffer = this.createAudioBuffer(audioData);\n const normalized = this.normalizeAudioBufferForFormat(audioBuffer, audioFormat);\n const duration = audioFormat.numberOfFrames / audioFormat.sampleRate;\n const audioTimestamp = this.audioState.getLastAudioTimestamp();\n const audioSample = this.createAudioSample(normalized.buffer, audioTimestamp, audioFormat.sampleRate, normalized.numberOfChannels);\n await this.processAudioSample(audioData, audioSample, audioTimestamp, duration);\n }\n }\n handleAudioChunk(message) {\n this.handleAsyncOperation(this.processAudioChunk(message), \"handleAudioChunk\");\n }\n async processAudioChunk(message) {\n if (this.shouldIgnoreMessage()) {\n return;\n }\n if (!this.audioSource) {\n return;\n }\n if (!this.audioState.isActive()) {\n return;\n }\n if (this.audioState.getIsPaused()) {\n return;\n }\n if (message.frames <= 0) {\n throw new Error(ERROR_AUDIO_FRAMES_INVALID);\n }\n if (message.sampleRate <= 0) {\n throw new Error(ERROR_AUDIO_SAMPLE_RATE_INVALID);\n }\n if (message.numberOfChannels <= 0) {\n throw new Error(ERROR_AUDIO_CHANNELS_INVALID);\n }\n this.setExpectedAudioFormat(message.sampleRate, message.numberOfChannels);\n if (this.expectedAudioSampleRate !== null && message.sampleRate !== this.expectedAudioSampleRate) {\n this.sendError(new Error(\"Audio sample rate changed during recording\"));\n return;\n }\n if (this.expectedAudioChannels !== null && message.numberOfChannels !== this.expectedAudioChannels) {\n this.sendError(new Error(\"Audio channel count changed during recording\"));\n return;\n }\n let audioBuffer = message.data;\n const numberOfChannels = message.numberOfChannels;\n const expectedSamples = message.frames * numberOfChannels;\n if (audioBuffer.length < expectedSamples) {\n throw new Error(\"Audio buffer length is shorter than expected\");\n }\n const sampleRate = message.sampleRate;\n const duration = message.frames / sampleRate;\n const audioTimestamp = this.audioState.getAudioTimestamp(message.timestamp);\n if (this.audioState.getIsMuted()) {\n audioBuffer = new Float32Array(audioBuffer.length);\n }\n const audioSample = new AudioSample({\n data: audioBuffer,\n format: WORKER_AUDIO_SAMPLE_FORMAT_F32_PLANAR,\n numberOfChannels,\n sampleRate,\n timestamp: audioTimestamp\n });\n const audioSource = requireInitialized(this.audioSource, \"Audio source\");\n const addError = await audioSource.add(audioSample).then(() => null).catch((error) => {\n return new Error(`Failed to add audio sample: ${extractErrorMessage(error)}`);\n });\n if (addError) {\n this.sendError(addError);\n audioSample.close();\n return;\n }\n this.audioState.updateLastAudioTimestamp(audioTimestamp, duration);\n const lastAudioTimestamp = this.audioState.getLastAudioTimestamp();\n logger.debug(\"[RecorderWorker] Audio sample processed\", {\n lastAudioTimestamp,\n duration,\n sampleRate,\n numberOfFrames: message.frames\n });\n audioSample.close();\n }\n shouldSkipAudioData(audioData) {\n if (!audioData) {\n return true;\n }\n if (this.audioState.getIsPaused()) {\n this.handlePausedAudioData(audioData);\n return true;\n }\n return false;\n }\n getAudioDataFormat(audioData) {\n const sampleRate = audioData.sampleRate;\n if (sampleRate <= 0) {\n audioData.close();\n throw new Error(ERROR_AUDIO_SAMPLE_RATE_INVALID);\n }\n const numberOfFrames = audioData.numberOfFrames;\n const numberOfChannels = audioData.numberOfChannels;\n this.setExpectedAudioFormat(sampleRate, numberOfChannels);\n if (this.expectedAudioSampleRate !== null && sampleRate !== this.expectedAudioSampleRate) {\n logger.warn(\"[RecorderWorker] Audio sample rate changed\", {\n expectedSampleRate: this.expectedAudioSampleRate,\n receivedSampleRate: sampleRate\n });\n audioData.close();\n return null;\n }\n return {\n sampleRate,\n numberOfFrames,\n numberOfChannels\n };\n }\n normalizeAudioBufferForFormat(audioBuffer, audioFormat) {\n let bufferToWrite = audioBuffer;\n let channelsToWrite = audioFormat.numberOfChannels;\n if (this.expectedAudioChannels !== null) {\n const normalized = this.normalizeAudioBuffer(audioBuffer, audioFormat.numberOfFrames, audioFormat.numberOfChannels, this.expectedAudioChannels);\n bufferToWrite = normalized.buffer;\n channelsToWrite = normalized.numberOfChannels;\n }\n return {\n buffer: bufferToWrite,\n numberOfChannels: channelsToWrite\n };\n }\n handlePause() {\n if (!this.audioState.pause()) {\n return;\n }\n this.sendStateChange(\"paused\");\n }\n handleResume() {\n if (!this.audioState.resume()) {\n return;\n }\n this.sendStateChange(\"recording\");\n }\n handleStop() {\n if (this.isStopping) {\n logger.debug(\"[RecorderWorker] handleStop ignored (stopping/finalized)\");\n return Promise.resolve();\n }\n if (this.isFinalized) {\n logger.debug(\"[RecorderWorker] handleStop ignored (stopping/finalized)\");\n return Promise.resolve();\n }\n this.isStopping = true;\n this.isFinalized = true;\n this.videoProcessingActive = false;\n this.audioState.setProcessingActive(false);\n return runStopTransition({\n finalizeStopSequence: () => this.finalizeStopSequence(),\n completeStop: () => this.completeStop(),\n recoverStopFailure: () => {\n if (this.isFinalized) {\n this.resetStopStateAfterFailure();\n }\n return this.cleanup().catch((cleanupError) => {\n logger.error(\"[RecorderWorker] Stop failure cleanup failed\", {\n error: extractErrorMessage(cleanupError)\n });\n });\n },\n clearStoppingFlag: () => {\n this.isStopping = false;\n }\n });\n }\n async completeStop() {\n await this.cleanup();\n this.sendStateChange(\"stopped\");\n }\n async finalizeStopSequence() {\n if (this.videoProcessor) {\n await this.videoProcessor.cancel();\n this.videoProcessor = null;\n }\n if (this.audioProcessor) {\n await this.audioProcessor.cancel();\n this.audioProcessor = null;\n }\n if (this.output) {\n await this.output.finalize();\n }\n await waitForPendingWritesToDrain({\n getPendingWriteCount: () => this.pendingWriteCount,\n timeoutMilliseconds: STOP_PENDING_WRITES_TIMEOUT_MILLISECONDS2\n });\n this.sendRecordingStats();\n if (this.isAudioOnly) {\n return;\n }\n this.logEarlyFrameWarning();\n const integrity = validateRecordingIntegrity({\n frameCount: this.timestampManager.getFrameCount(),\n totalFrameErrors: this.totalFrameErrors\n });\n if (!integrity.isValid && integrity.error) {\n this.sendFatalError({\n code: integrity.error.code,\n message: integrity.error.message,\n isRecoverable: true,\n metadata: {\n frameCount: this.timestampManager.getFrameCount(),\n totalErrors: this.totalFrameErrors\n }\n });\n }\n }\n logEarlyFrameWarning() {\n const frameCount = this.timestampManager.getFrameCount();\n if (frameCount <= EARLY_FRAME_THRESHOLD && this.totalFrameErrors > 0) {\n logger.warn(\"[RecorderWorker] Recording finished with early frame errors\", {\n frameCount,\n totalFrameErrors: this.totalFrameErrors\n });\n }\n }\n handleVideoFrameAddError() {\n this.consecutiveFrameErrors += 1;\n this.totalFrameErrors += 1;\n if (this.consecutiveFrameErrors < CONSECUTIVE_FRAME_ERROR_THRESHOLD) {\n return;\n }\n this.videoProcessingActive = false;\n this.audioState.setProcessingActive(false);\n this.sendFatalError({\n code: \"recording.frame-loss\",\n message: `Recording aborted: ${CONSECUTIVE_FRAME_ERROR_THRESHOLD} consecutive video frame errors`,\n isRecoverable: true,\n metadata: {\n consecutiveErrors: this.consecutiveFrameErrors,\n totalErrors: this.totalFrameErrors,\n totalFrames: this.totalFramesProcessed\n }\n });\n }\n handleVideoFrameAddSuccess() {\n this.consecutiveFrameErrors = 0;\n this.totalFramesProcessed += 1;\n }\n resetStopStateAfterFailure() {\n this.isFinalized = false;\n this.videoProcessingActive = false;\n this.audioState.setProcessingActive(false);\n }\n handleToggleMute() {\n this.audioState.toggleMuted();\n }\n handleUpdateFps(fps) {\n if (fps <= 0) {\n throw new Error(\"Frame rate must be greater than zero\");\n }\n const previousFps = this.timestampManager.getFrameRate();\n logger.debug(\"[RecorderWorker] Updating FPS\", {\n fps,\n previousFps\n });\n this.timestampManager.setFrameRate(fps);\n if (this.config) {\n this.config.fps = fps;\n }\n }\n handleUpdateSourceType(isScreenCapture) {\n logger.debug(\"[RecorderWorker] Updating source type\", {\n isScreenCapture,\n previousIsScreenCapture: this.isScreenCapture\n });\n this.isScreenCapture = isScreenCapture;\n this.visibilityTracker.setIsScreenCapture(isScreenCapture);\n }\n async handleSwitchSource(videoTrack, videoStream) {\n if (!(videoTrack || videoStream)) {\n throw new Error(\"Video track or stream is required\");\n }\n const frameRate = this.timestampManager.getFrameRate();\n requireDefined(frameRate, \"Frame rate must be set\");\n if (frameRate <= 0) {\n throw new Error(\"Frame rate must be greater than zero\");\n }\n if (this.videoProcessor) {\n this.videoProcessingActive = false;\n const videoProcessor = this.videoProcessor;\n await videoProcessor.cancel();\n await this.drainReader(videoProcessor);\n this.videoProcessor = null;\n }\n const lastAudioTimestamp = this.audioState.getLastAudioTimestamp();\n const baseVideoTimestamp = this.timestampManager.getBaseVideoTimestamp();\n requireNonNull(baseVideoTimestamp, \"Base video timestamp must be set for source switch\");\n const switchResult = this.timestampManager.handleSourceSwitch(lastAudioTimestamp);\n logger.debug(\"[RecorderWorker] handleSwitchSource - preserving baseVideoTimestamp\", {\n continuationTimestamp: switchResult.continuationTimestamp,\n lastVideoTimestamp: this.timestampManager.getLastVideoTimestamp(),\n frameRate,\n isScreenCapture: this.isScreenCapture,\n baseVideoTimestamp,\n recordingStartTime: this.recordingStartTime,\n lastAudioTimestamp,\n previousVideoTimestamp: switchResult.previousVideoTimestamp,\n minFrameDuration: switchResult.minFrameDuration,\n rawDrift: switchResult.rawDrift,\n driftOffset: switchResult.driftOffset\n });\n if (videoStream) {\n this.setupVideoProcessingFromStream(videoStream);\n return;\n }\n if (videoTrack) {\n this.setupVideoProcessing(videoTrack);\n }\n }\n async cleanup() {\n this.bufferTracker.stop();\n this.videoProcessingActive = false;\n this.audioState.setProcessingActive(false);\n if (this.videoProcessor) {\n const videoProcessor = this.videoProcessor;\n await videoProcessor.cancel();\n await this.drainReader(videoProcessor);\n this.videoProcessor = null;\n }\n if (this.audioProcessor) {\n const audioProcessor = this.audioProcessor;\n await audioProcessor.cancel();\n await this.drainReader(audioProcessor);\n this.audioProcessor = null;\n }\n const videoSource = this.videoSource;\n if (videoSource && !this.isFinalized) {\n videoSource.close();\n }\n if (videoSource) {\n this.videoSource = null;\n }\n const audioSource = this.audioSource;\n if (audioSource && !this.isFinalized) {\n audioSource.close();\n }\n if (audioSource) {\n this.audioSource = null;\n }\n const output = this.output;\n if (output && !this.isFinalized) {\n await output.cancel().catch((error) => {\n logger.warn(\"[RecorderWorker] cancel failed (ignored, possibly finalized)\", error);\n });\n this.isFinalized = true;\n }\n if (output) {\n this.output = null;\n }\n this.timestampManager.reset(undefined);\n this.totalSize = 0;\n this.audioState.reset();\n this.frameCompositor.reset();\n this.overlayConfig = null;\n this.recordingStartTime = 0;\n this.isScreenCapture = false;\n this.expectedAudioChannels = null;\n this.expectedAudioSampleRate = null;\n this.pendingWriteCount = 0;\n this.consecutiveFrameErrors = 0;\n this.totalFrameErrors = 0;\n this.totalFramesProcessed = 0;\n this.visibilityTracker.reset(this.recordingStartTime, this.isScreenCapture);\n this.isAudioOnly = false;\n }\n setExpectedAudioFormat(sampleRate, numberOfChannels) {\n if (this.expectedAudioSampleRate === null) {\n this.expectedAudioSampleRate = sampleRate;\n }\n if (this.expectedAudioChannels === null) {\n this.expectedAudioChannels = numberOfChannels;\n }\n }\n normalizeAudioBuffer(audioBuffer, frames, actualChannels, expectedChannels) {\n if (actualChannels === expectedChannels) {\n return { buffer: audioBuffer, numberOfChannels: actualChannels };\n }\n if (actualChannels === 1 && expectedChannels === STEREO_CHANNEL_COUNT) {\n const expandedBuffer = new Float32Array(frames * STEREO_CHANNEL_COUNT);\n expandedBuffer.set(audioBuffer, 0);\n expandedBuffer.set(audioBuffer, frames);\n return {\n buffer: expandedBuffer,\n numberOfChannels: STEREO_CHANNEL_COUNT\n };\n }\n if (actualChannels === STEREO_CHANNEL_COUNT && expectedChannels === 1) {\n const mixedBuffer = new Float32Array(frames);\n let frameIndex = 0;\n while (frameIndex < frames) {\n const leftSample = audioBuffer[frameIndex];\n const rightSample = audioBuffer[frameIndex + frames];\n mixedBuffer[frameIndex] = (leftSample + rightSample) * AUDIO_SAMPLE_AVERAGE_SCALE;\n frameIndex += 1;\n }\n return { buffer: mixedBuffer, numberOfChannels: 1 };\n }\n logger.warn(\"[RecorderWorker] Audio channel mismatch\", {\n expectedChannels,\n receivedChannels: actualChannels\n });\n return { buffer: audioBuffer, numberOfChannels: actualChannels };\n }\n sendReady() {\n const response = { type: \"ready\" };\n self.postMessage(response);\n }\n sendError(error) {\n const errorMessage = extractErrorMessage(error);\n const response = {\n type: \"error\",\n error: errorMessage\n };\n self.postMessage(response);\n }\n sendChunk(data, position) {\n this.totalSize = Math.max(this.totalSize, position + data.length);\n const response = {\n type: \"chunk\",\n data,\n position\n };\n const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);\n self.postMessage(response, [buffer]);\n }\n sendStateChange(state) {\n const response = {\n type: \"stateChange\",\n state\n };\n self.postMessage(response);\n }\n sendEncoderAcceleration(acceleration) {\n const response = {\n type: \"encoderAcceleration\",\n acceleration\n };\n self.postMessage(response);\n }\n sendFatalError(payload) {\n self.postMessage({\n type: \"fatalError\",\n code: payload.code,\n message: payload.message,\n isRecoverable: payload.isRecoverable,\n metadata: payload.metadata\n });\n }\n sendRecordingStats() {\n self.postMessage({\n type: \"recordingStats\",\n videoFrameCount: this.timestampManager.getFrameCount(),\n totalFrameErrors: this.totalFrameErrors,\n totalFramesProcessed: this.totalFramesProcessed\n });\n }\n deserializeBitrate(bitrate) {\n if (typeof bitrate === \"number\") {\n return bitrate;\n }\n if (bitrate === \"low\") {\n return QUALITY_LOW;\n }\n if (bitrate === \"medium\") {\n return QUALITY_MEDIUM;\n }\n if (bitrate === \"high\") {\n return QUALITY_HIGH;\n }\n if (bitrate === \"very-high\") {\n return QUALITY_VERY_HIGH;\n }\n return QUALITY_HIGH;\n }\n }\n function shouldAutoInitializeRecorderWorker() {\n if (typeof self === \"undefined\") {\n return false;\n }\n if (!(\"importScripts\" in self)) {\n return false;\n }\n return typeof self.postMessage === \"function\";\n }\n function initializeRecorderWorker() {\n return new RecorderWorker;\n }\n if (shouldAutoInitializeRecorderWorker()) {\n initializeRecorderWorker();\n }\n})();\n";
|
|
2396
2586
|
|
|
2397
2587
|
export type FrameCompositorDependencies = {
|
|
2398
2588
|
logger: {
|
|
@@ -2649,7 +2839,7 @@ export declare class AudioWorkletManager {
|
|
|
2649
2839
|
private readonly onChunk;
|
|
2650
2840
|
private readonly createController;
|
|
2651
2841
|
constructor(dependencies: AudioWorkletManagerDependencies);
|
|
2652
|
-
prepareAudioConfig(audioTrack: MediaStreamAudioTrack | null): Promise<WorkerAudioConfig | null>;
|
|
2842
|
+
prepareAudioConfig(audioTrack: MediaStreamAudioTrack | null, channelCount?: number): Promise<WorkerAudioConfig | null>;
|
|
2653
2843
|
startProcessing(): Promise<void>;
|
|
2654
2844
|
stop(): void;
|
|
2655
2845
|
setMuted(isMuted: boolean): void;
|
|
@@ -2684,7 +2874,7 @@ export declare class VisibilityTracker {
|
|
|
2684
2874
|
private processVisibilityUpdate;
|
|
2685
2875
|
}
|
|
2686
2876
|
|
|
2687
|
-
export type WorkerMessage = WorkerStartMessage | WorkerProbeMessage | WorkerWarmupMessage | WorkerPauseMessage | WorkerResumeMessage | WorkerStopMessage | WorkerToggleMuteMessage | WorkerAudioChunkMessage | WorkerSwitchSourceMessage | WorkerUpdateFpsMessage | WorkerUpdateVisibilityMessage | WorkerUpdateSourceTypeMessage | WorkerRequestStatsMessage;
|
|
2877
|
+
export type WorkerMessage = WorkerStartMessage | WorkerStartAudioMessage | WorkerProbeMessage | WorkerWarmupMessage | WorkerPauseMessage | WorkerResumeMessage | WorkerStopMessage | WorkerToggleMuteMessage | WorkerAudioChunkMessage | WorkerSwitchSourceMessage | WorkerUpdateFpsMessage | WorkerUpdateVisibilityMessage | WorkerUpdateSourceTypeMessage | WorkerRequestStatsMessage;
|
|
2688
2878
|
export type WorkerResponse = WorkerReadyResponse | WorkerErrorResponse | WorkerChunkResponse | WorkerBufferUpdateResponse | WorkerEncoderAccelerationResponse | WorkerStateChangeResponse | WorkerProbeResultResponse | WorkerDebugLogResponse | WorkerFatalErrorResponse | WorkerRecordingStatsResponse;
|
|
2689
2879
|
export type WorkerStartMessage = {
|
|
2690
2880
|
type: "start";
|
|
@@ -2702,6 +2892,17 @@ export type WorkerStartMessage = {
|
|
|
2702
2892
|
recordingStartTime?: number;
|
|
2703
2893
|
};
|
|
2704
2894
|
};
|
|
2895
|
+
export declare const WORKER_MESSAGE_TYPE_START_AUDIO: "startAudio";
|
|
2896
|
+
export type WorkerAudioOnlyConfig = {
|
|
2897
|
+
format: "mp4" | "webm";
|
|
2898
|
+
audioCodec: "aac" | "opus";
|
|
2899
|
+
audioBitrate: number;
|
|
2900
|
+
};
|
|
2901
|
+
export type WorkerStartAudioMessage = {
|
|
2902
|
+
type: typeof WORKER_MESSAGE_TYPE_START_AUDIO;
|
|
2903
|
+
audioConfig: WorkerAudioConfig | null;
|
|
2904
|
+
config: WorkerAudioOnlyConfig;
|
|
2905
|
+
};
|
|
2705
2906
|
export declare const WORKER_MESSAGE_TYPE_PROBE: "probe";
|
|
2706
2907
|
export declare const WORKER_MESSAGE_TYPE_WARMUP: "warmup";
|
|
2707
2908
|
export declare const WORKER_MESSAGE_TYPE_AUDIO_CHUNK: "audioChunk";
|
|
@@ -2985,6 +3186,7 @@ export declare class RecorderWorker {
|
|
|
2985
3186
|
private videoProcessingRunId;
|
|
2986
3187
|
private totalFrameErrors;
|
|
2987
3188
|
private totalFramesProcessed;
|
|
3189
|
+
private isAudioOnly;
|
|
2988
3190
|
constructor();
|
|
2989
3191
|
private shouldIgnoreMessage;
|
|
2990
3192
|
private handleAsyncOperation;
|
|
@@ -2993,6 +3195,8 @@ export declare class RecorderWorker {
|
|
|
2993
3195
|
private warmUpVideoEncoderOnce;
|
|
2994
3196
|
handleMessage: (event: MessageEvent<WorkerMessage>) => void;
|
|
2995
3197
|
private handleStartMessage;
|
|
3198
|
+
private handleStartAudioMessage;
|
|
3199
|
+
private handleAudioOnlyStart;
|
|
2996
3200
|
private handleStopMessage;
|
|
2997
3201
|
private handleSwitchSourceMessage;
|
|
2998
3202
|
private validateConfig;
|
|
@@ -3066,6 +3270,8 @@ export declare class BufferTracker {
|
|
|
3066
3270
|
stop(): void;
|
|
3067
3271
|
}
|
|
3068
3272
|
|
|
3273
|
+
export declare function validateAudioOnlyWorkerConfig(config: WorkerAudioOnlyConfig, audioConfig: WorkerAudioConfig | null): WorkerAudioConfig;
|
|
3274
|
+
|
|
3069
3275
|
import type { SourceSwitchCallbacks } from "../stream/source-switch-manager";
|
|
3070
3276
|
import type { UploadCallbacks } from "../upload/types";
|
|
3071
3277
|
export type RecordingCallbackDependencies = {
|
|
@@ -3124,6 +3330,8 @@ export declare class TelemetryManager {
|
|
|
3124
3330
|
private mergeTelemetryProperties;
|
|
3125
3331
|
}
|
|
3126
3332
|
|
|
3333
|
+
import { convertRecordedAudio } from "../audio-recording/audio-file-transcoder";
|
|
3334
|
+
import type { AudioRecording } from "../audio-recording/types";
|
|
3127
3335
|
import type { CameraStreamManager } from "../stream/stream";
|
|
3128
3336
|
import { StreamProcessor } from "../stream/stream-processor";
|
|
3129
3337
|
import { transcodeDeferredVideo } from "../transcode/deferred-video-transcoder";
|
|
@@ -3131,10 +3339,12 @@ import type { TranscodeConfig } from "../transcode/transcode-types";
|
|
|
3131
3339
|
export type RecordingStopResult = {
|
|
3132
3340
|
blob: Blob;
|
|
3133
3341
|
telemetryProperties: Record<string, unknown>;
|
|
3342
|
+
audio?: AudioRecording;
|
|
3134
3343
|
};
|
|
3135
3344
|
type TranscodeVideoDependency = typeof transcodeDeferredVideo;
|
|
3136
3345
|
type RecordingManagerDependencies = {
|
|
3137
3346
|
transcodeVideo: TranscodeVideoDependency;
|
|
3347
|
+
convertRecordedAudio: typeof convertRecordedAudio;
|
|
3138
3348
|
};
|
|
3139
3349
|
export declare class RecordingManager {
|
|
3140
3350
|
private recordingState;
|
|
@@ -3145,6 +3355,8 @@ export declare class RecordingManager {
|
|
|
3145
3355
|
private countdownStartTime;
|
|
3146
3356
|
private isPaused;
|
|
3147
3357
|
private maxRecordingTime;
|
|
3358
|
+
private activeMaxRecordingTime;
|
|
3359
|
+
private lastAudioRecording;
|
|
3148
3360
|
private maxTimeTimer;
|
|
3149
3361
|
private recordingStartTime;
|
|
3150
3362
|
private maxTimeRemaining;
|
|
@@ -3169,6 +3381,7 @@ export declare class RecordingManager {
|
|
|
3169
3381
|
setTabVisibilityOverlayConfig(enabled: boolean, text: string | undefined): void;
|
|
3170
3382
|
getRecordingState(): RecordingState;
|
|
3171
3383
|
isPausedState(): boolean;
|
|
3384
|
+
getAudioRecording(blob: Blob): AudioRecording | null;
|
|
3172
3385
|
getRecordingSeconds(): number;
|
|
3173
3386
|
getStreamProcessor(): StreamProcessor | null;
|
|
3174
3387
|
updateSourceType(isScreenCapture: boolean): void;
|
|
@@ -3187,6 +3400,7 @@ export declare class RecordingManager {
|
|
|
3187
3400
|
private clearCountdownTimers;
|
|
3188
3401
|
cleanup(): void;
|
|
3189
3402
|
private resetRecordingState;
|
|
3403
|
+
private resolveAudioRecording;
|
|
3190
3404
|
private resolveFinalRecordingBlob;
|
|
3191
3405
|
private resolveDeferredCaptureFrameRate;
|
|
3192
3406
|
private resetPauseState;
|
|
@@ -3202,6 +3416,7 @@ export declare class RecordingManager {
|
|
|
3202
3416
|
export {};
|
|
3203
3417
|
|
|
3204
3418
|
import type { AudioLevelCallbacks } from "../audio/types";
|
|
3419
|
+
import type { AudioUploadDetails, MediaType } from "../audio-recording/types";
|
|
3205
3420
|
import type { DeviceCallbacks } from "../device/types";
|
|
3206
3421
|
import type { RecordingRouteDecision as RecordingRouteDecisionType } from "../routing/recording-route-decision";
|
|
3207
3422
|
import type { UploadCallbacks as StorageUploadCallbacks } from "../storage/types";
|
|
@@ -3209,7 +3424,7 @@ import type { PictureInPictureConfig } from "../stream/picture-in-picture-layout
|
|
|
3209
3424
|
import type { TranscodeConfig } from "../transcode/transcode-types";
|
|
3210
3425
|
import type { AccessTokenProvider, UploadCallbacks } from "../upload/types";
|
|
3211
3426
|
export type RecordingState = "idle" | "countdown" | "recording";
|
|
3212
|
-
export type SourceType = "camera" | "screen" | "camera-screen";
|
|
3427
|
+
export type SourceType = "camera" | "screen" | "camera-screen" | "audio";
|
|
3213
3428
|
export type RecordingRouteDecision = RecordingRouteDecisionType;
|
|
3214
3429
|
export type AudioWarning = {
|
|
3215
3430
|
code: "audio.no-signal";
|
|
@@ -3238,6 +3453,8 @@ export type LocalRecording = {
|
|
|
3238
3453
|
mimeType: string;
|
|
3239
3454
|
duration: number;
|
|
3240
3455
|
sourceType: SourceType;
|
|
3456
|
+
mediaType?: MediaType;
|
|
3457
|
+
audio?: AudioUploadDetails;
|
|
3241
3458
|
};
|
|
3242
3459
|
export type AudioRecoveryEvent = {
|
|
3243
3460
|
type: "audio.recovered";
|
|
@@ -3365,6 +3582,7 @@ export declare function sanitizeRecordingTitle(title: string): string;
|
|
|
3365
3582
|
export declare function createRecordingFilename(title: string | null, extension: string): string;
|
|
3366
3583
|
|
|
3367
3584
|
import type { AudioLevelCallbacks } from "../audio/types";
|
|
3585
|
+
import type { AudioUploadDetails, MediaType } from "../audio-recording/types";
|
|
3368
3586
|
import { DeviceManager } from "../device/device-manager";
|
|
3369
3587
|
import type { AvailableDevices } from "../device/types";
|
|
3370
3588
|
import type { PictureInPictureConfigInput, ResolvedPictureInPictureConfig } from "../stream/picture-in-picture-layout";
|
|
@@ -3373,6 +3591,14 @@ import { CameraStreamManager } from "../stream/stream";
|
|
|
3373
3591
|
import type { TranscodeConfig } from "../transcode/transcode-types";
|
|
3374
3592
|
import type { AccessTokenProvider } from "../upload/types";
|
|
3375
3593
|
import { VideoUploadService } from "../upload/upload-service";
|
|
3594
|
+
export type RecordingUploadDetails = {
|
|
3595
|
+
filename: string;
|
|
3596
|
+
mimeType: string;
|
|
3597
|
+
duration: number;
|
|
3598
|
+
mediaType: MediaType;
|
|
3599
|
+
sourceType: SourceType;
|
|
3600
|
+
audio?: AudioUploadDetails;
|
|
3601
|
+
};
|
|
3376
3602
|
export declare class RecorderController {
|
|
3377
3603
|
private readonly streamManager;
|
|
3378
3604
|
private readonly configManager;
|
|
@@ -3403,6 +3629,10 @@ export declare class RecorderController {
|
|
|
3403
3629
|
constructor(callbacks?: RecorderCallbacks);
|
|
3404
3630
|
initialize(config: RecorderConfig): Promise<void>;
|
|
3405
3631
|
startStream(requestedSourceType?: SourceType): Promise<void>;
|
|
3632
|
+
private startAudioStream;
|
|
3633
|
+
private resolveRecordingConfig;
|
|
3634
|
+
getRecordingMediaType(blob: Blob): MediaType;
|
|
3635
|
+
resolveRecordingUploadDetails(blob: Blob, title: string | null): Promise<RecordingUploadDetails>;
|
|
3406
3636
|
stopStream(): Promise<void>;
|
|
3407
3637
|
switchVideoDevice(deviceId: string | null): Promise<MediaStream>;
|
|
3408
3638
|
switchAudioDevice(deviceId: string | null): Promise<MediaStream>;
|
|
@@ -3510,7 +3740,7 @@ export declare const DIRECT_UPLOAD_MAX_PART_SIZE_BYTES: number;
|
|
|
3510
3740
|
export declare const DIRECT_UPLOAD_MAX_PART_COUNT = 10000;
|
|
3511
3741
|
export declare function calculateDirectUploadPartSize(fileSize: number): number;
|
|
3512
3742
|
|
|
3513
|
-
import type { PendingUpload, VideoStorageService } from "../storage/video-storage";
|
|
3743
|
+
import type { NewPendingUpload, PendingUpload, VideoStorageService } from "../storage/video-storage";
|
|
3514
3744
|
type UploadCallbacks = {
|
|
3515
3745
|
onUploadProgress?: (id: string, progress: number) => void;
|
|
3516
3746
|
onUploadProgressDetails?: (id: string, details: UploadProgressDetails) => void;
|
|
@@ -3555,7 +3785,7 @@ export declare class UploadQueueManager {
|
|
|
3555
3785
|
constructor(storageService: VideoStorageService, uploadService: VideoUploadService, getAccessToken?: AccessTokenProvider);
|
|
3556
3786
|
destroy(): void;
|
|
3557
3787
|
setCallbacks(callbacks: UploadCallbacks): void;
|
|
3558
|
-
queueUpload(upload:
|
|
3788
|
+
queueUpload(upload: NewPendingUpload): Promise<string>;
|
|
3559
3789
|
processQueue(): Promise<void>;
|
|
3560
3790
|
getPendingUploads(): Promise<PendingUpload[]>;
|
|
3561
3791
|
processMatchingUpload(match: PendingUploadMatch): Promise<RecordingUploadResult | null>;
|
|
@@ -3653,6 +3883,7 @@ export declare function startFinalizingProgress(model: UploadProgressModel, now:
|
|
|
3653
3883
|
export declare function completeUploadProgress(model: UploadProgressModel): void;
|
|
3654
3884
|
export declare function readUploadProgress(model: UploadProgressModel, now: number): number;
|
|
3655
3885
|
|
|
3886
|
+
import type { AudioUploadDetails, MediaType } from "../audio-recording/types";
|
|
3656
3887
|
export type VideoUploadOptions = {
|
|
3657
3888
|
apiKey?: string;
|
|
3658
3889
|
getAccessToken?: AccessTokenProvider;
|
|
@@ -3664,6 +3895,8 @@ export type VideoUploadOptions = {
|
|
|
3664
3895
|
metadata?: Record<string, unknown>;
|
|
3665
3896
|
userMetadata?: Record<string, unknown>;
|
|
3666
3897
|
isResume?: boolean;
|
|
3898
|
+
mediaType?: MediaType;
|
|
3899
|
+
audio?: AudioUploadDetails;
|
|
3667
3900
|
onProgress?: (progress: number) => void;
|
|
3668
3901
|
onProgressDetails?: (details: UploadProgressDetails) => void;
|
|
3669
3902
|
signal?: AbortSignal;
|
|
@@ -3720,6 +3953,9 @@ export declare class VideoUploadService {
|
|
|
3720
3953
|
private confirmUpload;
|
|
3721
3954
|
private getUploadStatus;
|
|
3722
3955
|
private waitForTerminalUpload;
|
|
3956
|
+
private resolveUploadResource;
|
|
3957
|
+
private uploadPeaksForResult;
|
|
3958
|
+
private uploadAudioPeaks;
|
|
3723
3959
|
private waitForStatusPoll;
|
|
3724
3960
|
private requestAuthenticatedMetadata;
|
|
3725
3961
|
private requestMetadataResponse;
|
|
@@ -3751,11 +3987,14 @@ export declare class VideoUploadService {
|
|
|
3751
3987
|
private normalizeMimeType;
|
|
3752
3988
|
}
|
|
3753
3989
|
|
|
3990
|
+
import type { MediaType } from "../audio-recording/types";
|
|
3754
3991
|
export type RecordingUploadResult = Omit<VideoUploadResult, "uploadUrl"> & {
|
|
3755
3992
|
recordingId: string;
|
|
3756
3993
|
uploadUrl: string;
|
|
3994
|
+
mediaType?: MediaType;
|
|
3995
|
+
audioId?: string;
|
|
3757
3996
|
};
|
|
3758
|
-
export declare function createRecordingUploadResult(result: VideoUploadResult): RecordingUploadResult;
|
|
3997
|
+
export declare function createRecordingUploadResult(result: VideoUploadResult, mediaType?: MediaType): RecordingUploadResult;
|
|
3759
3998
|
|
|
3760
3999
|
export type CapabilityNavigatorConnection = {
|
|
3761
4000
|
effectiveType?: string;
|