Advance M8-M11 parity workflows
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-17 04:37:07 -04:00
parent 7c16b279ae
commit 0fe8d2bb56
324 changed files with 31920 additions and 863 deletions

View File

@@ -0,0 +1,232 @@
import {
parseSequencerAudioSessionReport,
SEQUENCER_AUDIO_SESSION_SCHEMA,
type SequencerAudioSessionIssueCode,
type SequencerAudioSessionReportIR,
} from "../../../protocol/sequencer-audio-session";
interface AudioParamLike {
value: number;
cancelScheduledValues?(startTime: number): void;
setValueAtTime(value: number, startTime: number): unknown;
}
interface GainNodeLike {
readonly gain: AudioParamLike;
connect(destination: unknown): unknown;
disconnect(): void;
}
export interface AudioContextLike {
readonly state: "suspended" | "running" | "closed" | string;
readonly currentTime: number;
readonly destination: unknown;
createGain(): GainNodeLike;
resume(): Promise<void>;
suspend(): Promise<void>;
close(): Promise<void>;
}
export interface SequencerAudioSessionOptions {
scope?: object;
contextFactory?: () => AudioContextLike;
outputGain?: number;
}
type AudioContextConstructor = new () => AudioContextLike;
export class SequencerAudioSession {
private readonly scope: object;
private readonly contextFactory?: () => AudioContextLike;
private readonly nominalGain: number;
private context: AudioContextLike | null = null;
private output: GainNodeLike | null = null;
private mutedValue = false;
private disposed = false;
private revisionValue = 0;
private issueCode: SequencerAudioSessionIssueCode | null = null;
constructor(options: SequencerAudioSessionOptions = {}) {
const outputGain = options.outputGain ?? 1;
if (typeof outputGain !== "number" || !Number.isFinite(outputGain) || outputGain <= 0 || outputGain > 1) {
const error = new Error("SEQUENCER_AUDIO_CONTEXT_INVALID: outputGain must be in (0, 1]") as Error & { code: string };
error.code = "SEQUENCER_AUDIO_CONTEXT_INVALID";
throw error;
}
this.scope = options.scope ?? globalThis;
this.contextFactory = options.contextFactory;
this.nominalGain = outputGain;
}
snapshot(): SequencerAudioSessionReportIR {
return this.report();
}
async initialize(): Promise<SequencerAudioSessionReportIR> {
this.revisionValue += 1;
if (this.disposed) return this.report();
if (!this.context || this.context.state === "closed") await this.openContext();
return this.report();
}
async resume(): Promise<SequencerAudioSessionReportIR> {
this.revisionValue += 1;
if (this.disposed) return this.report();
if (!this.context || this.context.state === "closed") await this.openContext();
if (!this.context) return this.report();
try {
if (this.context.state !== "running") await this.context.resume();
if (this.context.state !== "running") throw new Error("AudioContext did not enter running state");
this.issueCode = null;
this.applyGain();
}
catch {
this.issueCode = "SEQUENCER_AUDIO_RESUME_FAILED";
}
return this.report();
}
async suspend(): Promise<SequencerAudioSessionReportIR> {
this.revisionValue += 1;
if (this.disposed) return this.report();
if (!this.context) {
this.issueCode = "SEQUENCER_AUDIO_DEVICE_UNAVAILABLE";
return this.report();
}
try {
if (this.context.state === "running") await this.context.suspend();
if (this.context.state !== "suspended") throw new Error("AudioContext did not enter suspended state");
this.issueCode = null;
}
catch {
this.issueCode = "SEQUENCER_AUDIO_SUSPEND_FAILED";
}
return this.report();
}
setMuted(muted: boolean): SequencerAudioSessionReportIR {
if (typeof muted !== "boolean") {
const error = new Error("SEQUENCER_AUDIO_CONTEXT_INVALID: muted must be boolean") as Error & { code: string };
error.code = "SEQUENCER_AUDIO_CONTEXT_INVALID";
throw error;
}
this.revisionValue += 1;
if (!this.disposed) {
this.mutedValue = muted;
this.applyGain();
}
return this.report();
}
async recoverDevice(): Promise<SequencerAudioSessionReportIR> {
this.revisionValue += 1;
if (this.disposed) return this.report();
await this.releaseContext();
this.issueCode = null;
await this.openContext();
return this.report();
}
async close(): Promise<SequencerAudioSessionReportIR> {
this.revisionValue += 1;
if (!this.disposed) {
this.disposed = true;
await this.releaseContext();
this.issueCode = null;
}
return this.report();
}
private createContext(): AudioContextLike | null {
if (this.contextFactory) return this.contextFactory();
const constructor = (this.scope as { AudioContext?: unknown }).AudioContext;
if (typeof constructor !== "function") return null;
return new (constructor as AudioContextConstructor)();
}
private async openContext(): Promise<void> {
try {
const context = this.createContext();
if (!context || context.state === "closed") {
this.issueCode = "SEQUENCER_AUDIO_DEVICE_UNAVAILABLE";
return;
}
const output = context.createGain();
output.connect(context.destination);
this.context = context;
this.output = output;
this.issueCode = null;
this.applyGain();
}
catch {
this.context = null;
this.output = null;
this.issueCode = "SEQUENCER_AUDIO_DEVICE_UNAVAILABLE";
}
}
private applyGain(): void {
if (!this.context || !this.output || this.context.state === "closed") return;
const value = this.mutedValue ? 0 : this.nominalGain;
this.output.gain.cancelScheduledValues?.(this.context.currentTime);
this.output.gain.setValueAtTime(value, this.context.currentTime);
}
private async releaseContext(): Promise<void> {
const context = this.context;
const output = this.output;
this.context = null;
this.output = null;
if (context && output) {
try {
output.gain.cancelScheduledValues?.(context.currentTime);
output.gain.setValueAtTime(0, context.currentTime);
}
catch { /* Context loss can invalidate AudioParam before release. */ }
try { output.disconnect(); }
catch { /* A disconnected node is already released. */ }
}
if (context && context.state !== "closed") {
try { await context.close(); }
catch { /* The session still drops all references on close failure. */ }
}
}
private report(): SequencerAudioSessionReportIR {
if (this.disposed) {
return parseSequencerAudioSessionReport({
schemaVersion: SEQUENCER_AUDIO_SESSION_SCHEMA,
revision: this.revisionValue,
contextState: "CLOSED",
outputState: "SILENT",
muted: this.mutedValue,
outputGain: 0,
issueCode: null,
});
}
if (!this.context || !this.output || this.context.state === "closed") {
return parseSequencerAudioSessionReport({
schemaVersion: SEQUENCER_AUDIO_SESSION_SCHEMA,
revision: this.revisionValue,
contextState: "UNAVAILABLE",
outputState: "BLOCKED",
muted: this.mutedValue,
outputGain: 0,
issueCode: "SEQUENCER_AUDIO_DEVICE_UNAVAILABLE",
});
}
const contextState = this.context.state === "running" ? "RUNNING" : "SUSPENDED";
const outputGain = this.mutedValue ? 0 : this.nominalGain;
return parseSequencerAudioSessionReport({
schemaVersion: SEQUENCER_AUDIO_SESSION_SCHEMA,
revision: this.revisionValue,
contextState,
outputState: contextState === "RUNNING" && !this.mutedValue && this.issueCode === null ? "ENABLED" : "SILENT",
muted: this.mutedValue,
outputGain,
issueCode: this.issueCode,
});
}
}
export type { SequencerAudioSessionReportIR };

View File

@@ -0,0 +1,145 @@
import {
SEQUENCER_CODEC_PROBE_SCHEMA,
parseSequencerCodecProbeRequest,
type SequencerCodecProbeBackend,
type SequencerCodecProbeBlockReason,
type SequencerCodecProbeRequestIR,
type SequencerCodecProbeResultIR,
} from "../../../protocol/sequencer";
async function sha256(data: ArrayBuffer): Promise<string> {
const digest = await crypto.subtle.digest("SHA-256", data);
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
}
function blocked(
request: SequencerCodecProbeRequestIR,
backend: SequencerCodecProbeBackend | null,
reason: SequencerCodecProbeBlockReason,
): SequencerCodecProbeResultIR {
return { ...request, status: "BLOCKED", backend, reason, decoded: null };
}
function waitForMedia(video: HTMLVideoElement, eventName: "loadeddata" | "seeked", timeoutMs: number): Promise<void> {
return new Promise((resolve, reject) => {
const timeout = window.setTimeout(() => finish(new Error("timeout")), timeoutMs);
const finish = (error?: Error): void => {
window.clearTimeout(timeout);
video.removeEventListener(eventName, ready);
video.removeEventListener("error", failed);
if (error) reject(error); else resolve();
};
const ready = (): void => finish();
const failed = (): void => finish(new Error("decode"));
video.addEventListener(eventName, ready, { once: true });
video.addEventListener("error", failed, { once: true });
});
}
async function probeImage(request: SequencerCodecProbeRequestIR, data: ArrayBuffer): Promise<SequencerCodecProbeResultIR> {
if (typeof createImageBitmap !== "function") return blocked(request, null, "RUNTIME_UNAVAILABLE");
try {
const bitmap = await createImageBitmap(new Blob([data], { type: request.mimeType }));
try {
if (bitmap.width < 1 || bitmap.height < 1) return blocked(request, "IMAGE_BITMAP", "DECODE_FAILED");
return { ...request, status: "READY", backend: "IMAGE_BITMAP", reason: null, decoded: { width: bitmap.width, height: bitmap.height } };
}
finally { bitmap.close(); }
}
catch { return blocked(request, "IMAGE_BITMAP", "DECODE_FAILED"); }
}
async function probeSound(request: SequencerCodecProbeRequestIR, data: ArrayBuffer): Promise<SequencerCodecProbeResultIR> {
if (typeof OfflineAudioContext === "undefined") return blocked(request, null, "RUNTIME_UNAVAILABLE");
const context = new OfflineAudioContext(1, 1, 48_000);
try {
const decoded = await context.decodeAudioData(data.slice(0));
if (decoded.sampleRate < 1 || decoded.numberOfChannels < 1 || decoded.length < 1) return blocked(request, "WEB_AUDIO", "DECODE_FAILED");
return {
...request,
status: "READY",
backend: "WEB_AUDIO",
reason: null,
decoded: { sampleRate: decoded.sampleRate, channels: decoded.numberOfChannels, durationFrames: decoded.length },
};
}
catch { return blocked(request, "WEB_AUDIO", "DECODE_FAILED"); }
}
async function probeMovie(request: SequencerCodecProbeRequestIR, data: ArrayBuffer): Promise<SequencerCodecProbeResultIR> {
if (typeof document === "undefined" || typeof HTMLMediaElement === "undefined") return blocked(request, null, "RUNTIME_UNAVAILABLE");
const video = document.createElement("video");
if (video.canPlayType(request.mimeType) === "") return blocked(request, "HTML_MEDIA", "MIME_UNSUPPORTED");
const url = URL.createObjectURL(new Blob([data], { type: request.mimeType }));
video.preload = "auto";
video.muted = true;
video.playsInline = true;
video.style.display = "none";
document.body.append(video);
try {
video.src = url;
video.load();
await waitForMedia(video, "loadeddata", 10_000);
if (!Number.isFinite(video.duration) || video.duration <= 0 || video.videoWidth < 1 || video.videoHeight < 1) {
return blocked(request, "HTML_MEDIA", "DECODE_FAILED");
}
const target = Math.min(video.duration / 2, Math.max(0, video.duration - 0.001));
if (target > 0) {
video.currentTime = target;
await waitForMedia(video, "seeked", 10_000);
}
const canvas = document.createElement("canvas");
canvas.width = 1;
canvas.height = 1;
const context = canvas.getContext("2d", { willReadFrequently: true });
if (!context) return blocked(request, "HTML_MEDIA", "RUNTIME_UNAVAILABLE");
context.drawImage(video, 0, 0, 1, 1);
context.getImageData(0, 0, 1, 1);
return {
...request,
status: "READY",
backend: "HTML_MEDIA",
reason: null,
decoded: {
width: video.videoWidth,
height: video.videoHeight,
durationMicros: Math.max(1, Math.round(video.duration * 1_000_000)),
},
};
}
catch { return blocked(request, "HTML_MEDIA", "DECODE_FAILED"); }
finally {
video.removeAttribute("src");
video.load();
video.remove();
URL.revokeObjectURL(url);
}
}
export async function probeSequencerCodec(
requestValue: unknown,
source: ArrayBuffer,
): Promise<SequencerCodecProbeResultIR> {
const request = parseSequencerCodecProbeRequest(requestValue);
if (!(source instanceof ArrayBuffer) || source.byteLength !== request.byteLength || await sha256(source) !== request.sourceSha256) {
return blocked(request, null, "SOURCE_IDENTITY_MISMATCH");
}
if (request.stripType === "IMAGE") return probeImage(request, source);
if (request.stripType === "SOUND") return probeSound(request, source);
return probeMovie(request, source);
}
export function createSequencerCodecProbeRequest(
stripType: SequencerCodecProbeRequestIR["stripType"],
mimeType: string,
byteLength: number,
sourceSha256: string,
): SequencerCodecProbeRequestIR {
return parseSequencerCodecProbeRequest({
schemaVersion: SEQUENCER_CODEC_PROBE_SCHEMA,
stripType,
mimeType,
byteLength,
sourceSha256,
});
}

View File

@@ -0,0 +1,18 @@
import {
routeSequencerFinalExport,
type SequencerFinalExportRequestIR,
type SequencerFinalExportRouteIR,
} from "../../../protocol/sequencer-export";
export async function routeSequencerFinalExportInBrowser(
request: SequencerFinalExportRequestIR,
serverExportAvailable: boolean,
scope: object = globalThis,
): Promise<SequencerFinalExportRouteIR> {
return routeSequencerFinalExport(request, {
serverExportAvailable,
browserVideoEncoderAvailable: "VideoEncoder" in scope,
});
}
export type { SequencerFinalExportRequestIR, SequencerFinalExportRouteIR };

View File

@@ -0,0 +1,201 @@
import {
SEQUENCER_MEDIA_PROXY_MAX_BYTES,
createSequencerMediaCacheManifest,
computeSequencerMediaCacheIdentity,
parseSequencerMediaProxyProfile,
sequencerMediaCacheKey,
verifySequencerMediaCacheEntry,
SequencerMediaCacheValidationError,
type SequencerMediaCacheManifestIR,
type SequencerMediaProxyProfileIR,
} from "../../../protocol/sequencer-media-cache";
import {
gateSequencerCodec,
parseSequencerCodecProbeRequest,
parseSequencerCodecProbeResult,
type SequencerCodecProbeRequestIR,
type SequencerCodecProbeResultIR,
} from "../../../protocol/sequencer";
export interface SequencerMediaProxyCacheStatsIR {
entries: number;
bytes: number;
maxBytes: number;
hits: number;
misses: number;
evictions: number;
keys: string[];
}
export interface SequencerGeneratedProxyFrameIR {
manifest: SequencerMediaCacheManifestIR;
data: ArrayBuffer;
}
interface CacheEntry {
manifest: SequencerMediaCacheManifestIR;
data: ArrayBuffer;
}
async function sha256(data: ArrayBuffer): Promise<string> {
const result = await crypto.subtle.digest("SHA-256", data);
return Array.from(new Uint8Array(result), (value) => value.toString(16).padStart(2, "0")).join("");
}
function waitForLoadedFrame(video: HTMLVideoElement, timeoutMs: number): Promise<void> {
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) return Promise.resolve();
return new Promise((resolve, reject) => {
const timeout = window.setTimeout(() => finish(new Error("timeout")), timeoutMs);
const finish = (error?: Error): void => {
window.clearTimeout(timeout);
video.removeEventListener("loadeddata", ready);
video.removeEventListener("error", failed);
if (error) reject(error); else resolve();
};
const ready = (): void => finish();
const failed = (): void => finish(new Error("decode"));
video.addEventListener("loadeddata", ready, { once: true });
video.addEventListener("error", failed, { once: true });
});
}
export async function generateInitialSequencerMovieProxyFrame(
sourceValue: unknown,
capabilityValue: unknown,
profileValue: unknown,
sourceData: ArrayBuffer,
): Promise<SequencerGeneratedProxyFrameIR> {
const source = parseSequencerCodecProbeRequest(sourceValue);
const capability = parseSequencerCodecProbeResult(capabilityValue);
const profile = parseSequencerMediaProxyProfile(profileValue);
if (source.stripType !== "MOVIE" || capability.status !== "READY" || capability.stripType !== "MOVIE" ||
gateSequencerCodec(source, capability).status !== "READY") {
throw new SequencerMediaCacheValidationError("SEQUENCER_CODEC_UNSUPPORTED", "Movie proxy generation requires a READY movie receipt");
}
if (!(sourceData instanceof ArrayBuffer) || sourceData.byteLength !== source.byteLength || await sha256(sourceData) !== source.sourceSha256) {
throw new SequencerMediaCacheValidationError("SEQUENCER_CACHE_SOURCE_MISMATCH", "Movie proxy source bytes failed identity verification");
}
if (typeof document === "undefined" || typeof HTMLMediaElement === "undefined") {
throw new SequencerMediaCacheValidationError("SEQUENCER_CODEC_UNSUPPORTED", "HTML media proxy generation is unavailable");
}
const video = document.createElement("video");
video.muted = true;
video.playsInline = true;
video.preload = "auto";
const url = URL.createObjectURL(new Blob([sourceData], { type: source.mimeType }));
try {
video.src = url;
video.load();
await waitForLoadedFrame(video, 10_000);
const canvas = document.createElement("canvas");
canvas.width = profile.width;
canvas.height = profile.height;
const context = canvas.getContext("2d", { alpha: true, willReadFrequently: true });
if (!context || video.videoWidth < 1 || video.videoHeight < 1) {
throw new SequencerMediaCacheValidationError("SEQUENCER_CODEC_UNSUPPORTED", "Movie proxy frame could not be decoded");
}
context.clearRect(0, 0, profile.width, profile.height);
context.drawImage(video, 0, 0, profile.width, profile.height);
const pixels = context.getImageData(0, 0, profile.width, profile.height).data;
const data = pixels.buffer.slice(pixels.byteOffset, pixels.byteOffset + pixels.byteLength);
return {
manifest: await createSequencerMediaCacheManifest(source, capability, profile, 0, data),
data,
};
}
catch (error) {
if (error instanceof SequencerMediaCacheValidationError) throw error;
throw new SequencerMediaCacheValidationError("SEQUENCER_CODEC_UNSUPPORTED", "Movie proxy frame decode failed");
}
finally {
video.pause();
video.removeAttribute("src");
video.load();
URL.revokeObjectURL(url);
}
}
export class SequencerMediaProxyCache {
readonly maxBytes: number;
private readonly entries = new Map<string, CacheEntry>();
private currentBytes = 0;
private hitCount = 0;
private missCount = 0;
private evictionCount = 0;
constructor(maxBytes: number) {
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > SEQUENCER_MEDIA_PROXY_MAX_BYTES) {
throw new SequencerMediaCacheValidationError("SEQUENCER_BUDGET_EXCEEDED", "Sequencer proxy cache budget is invalid");
}
this.maxBytes = maxBytes;
}
async put(
manifestValue: unknown,
data: ArrayBuffer,
currentSource: SequencerCodecProbeRequestIR,
currentCapability: SequencerCodecProbeResultIR,
): Promise<string> {
const manifest = await verifySequencerMediaCacheEntry(manifestValue, data, currentSource, currentCapability);
if (data.byteLength > this.maxBytes) {
throw new SequencerMediaCacheValidationError("SEQUENCER_BUDGET_EXCEEDED", "Sequencer proxy entry exceeds the cache budget");
}
const key = sequencerMediaCacheKey(manifest);
const previous = this.entries.get(key);
if (previous) {
this.currentBytes -= previous.data.byteLength;
this.entries.delete(key);
}
while (this.currentBytes + data.byteLength > this.maxBytes) {
const oldest = this.entries.entries().next().value as [string, CacheEntry] | undefined;
if (!oldest) break;
this.entries.delete(oldest[0]);
this.currentBytes -= oldest[1].data.byteLength;
this.evictionCount++;
}
this.entries.set(key, { manifest, data: data.slice(0) });
this.currentBytes += data.byteLength;
return key;
}
async get(
sourceValue: unknown,
capabilityValue: unknown,
profileValue: unknown,
sourceFrame: number,
): Promise<SequencerGeneratedProxyFrameIR | undefined> {
const source = parseSequencerCodecProbeRequest(sourceValue);
const capability = parseSequencerCodecProbeResult(capabilityValue);
const profile = parseSequencerMediaProxyProfile(profileValue);
const identitySha256 = await computeSequencerMediaCacheIdentity(source, capability, profile, sourceFrame);
const key = `sequencer-media-cache:v1:${identitySha256}`;
const entry = this.entries.get(key);
if (!entry) { this.missCount++; return undefined; }
this.hitCount++;
this.entries.delete(key);
this.entries.set(key, entry);
return { manifest: entry.manifest, data: entry.data.slice(0) };
}
clear(): number {
const released = this.currentBytes;
this.entries.clear();
this.currentBytes = 0;
return released;
}
stats(): SequencerMediaProxyCacheStatsIR {
return {
entries: this.entries.size,
bytes: this.currentBytes,
maxBytes: this.maxBytes,
hits: this.hitCount,
misses: this.missCount,
evictions: this.evictionCount,
keys: [...this.entries.keys()],
};
}
}
export type { SequencerMediaCacheManifestIR, SequencerMediaProxyProfileIR };

View File

@@ -0,0 +1,76 @@
import {
SEQUENCER_MEDIA_REVISION_SCHEMA,
gateSequencerMediaRevision,
parseSequencerMediaRevisionRequest,
parseSequencerMediaRevisionState,
SequencerMediaRevisionValidationError,
type SequencerMediaOperation,
type SequencerMediaRevisionDecisionIR,
type SequencerMediaRevisionRequestIR,
type SequencerMediaRevisionResultIR,
type SequencerMediaRevisionStateIR,
} from "../../../protocol/sequencer-media-revision";
export class SequencerMediaRevisionGate {
private stateValue: SequencerMediaRevisionStateIR;
constructor(timelineId: string, timelineRevision: number) {
this.stateValue = parseSequencerMediaRevisionState({
schemaVersion: SEQUENCER_MEDIA_REVISION_SCHEMA,
timelineId,
timelineRevision,
latestRequestRevision: 0,
});
}
state(): SequencerMediaRevisionStateIR {
return { ...this.stateValue };
}
begin(operation: SequencerMediaOperation, frame: number): SequencerMediaRevisionRequestIR {
this.stateValue = {
...this.stateValue,
latestRequestRevision: this.stateValue.latestRequestRevision + 1,
};
return parseSequencerMediaRevisionRequest({
schemaVersion: SEQUENCER_MEDIA_REVISION_SCHEMA,
requestId: `media:${this.stateValue.latestRequestRevision}`,
timelineId: this.stateValue.timelineId,
timelineRevision: this.stateValue.timelineRevision,
requestRevision: this.stateValue.latestRequestRevision,
operation,
frame,
});
}
replaceTimeline(timelineId: string, timelineRevision: number): SequencerMediaRevisionStateIR {
if (timelineId === this.stateValue.timelineId && timelineRevision <= this.stateValue.timelineRevision) {
throw new SequencerMediaRevisionValidationError("REVISION_CONFLICT", "Sequencer timeline revision must advance monotonically");
}
this.stateValue = parseSequencerMediaRevisionState({
schemaVersion: SEQUENCER_MEDIA_REVISION_SCHEMA,
timelineId,
timelineRevision,
latestRequestRevision: this.stateValue.latestRequestRevision + 1,
});
return this.state();
}
resolve(
request: SequencerMediaRevisionRequestIR,
result: SequencerMediaRevisionResultIR,
publish: (result: SequencerMediaRevisionResultIR) => void,
): SequencerMediaRevisionDecisionIR {
const decision = gateSequencerMediaRevision(request, this.stateValue, result);
if (decision.status === "PUBLISH") publish(result);
return decision;
}
}
export type {
SequencerMediaOperation,
SequencerMediaRevisionDecisionIR,
SequencerMediaRevisionRequestIR,
SequencerMediaRevisionResultIR,
SequencerMediaRevisionStateIR,
};

View File

@@ -8,7 +8,54 @@ export {
sequencerSourceFrame,
} from "../../../protocol/sequencer";
export type {
SequencerCodecProbeRequestIR,
SequencerCodecProbeResultIR,
SequencerFrameStripIR,
SequencerTimelineIR,
SequencerTransitionFrameIR,
} from "../../../protocol/sequencer";
export {
computeSequencerMediaCacheIdentity,
createSequencerMediaCacheManifest,
parseSequencerMediaCacheManifest,
parseSequencerMediaProxyProfile,
sequencerMediaCacheKey,
verifySequencerMediaCacheEntry,
} from "../../../protocol/sequencer-media-cache";
export {
gateSequencerMediaRevision,
parseSequencerMediaRevisionRequest,
parseSequencerMediaRevisionResult,
parseSequencerMediaRevisionState,
} from "../../../protocol/sequencer-media-revision";
export {
parseSequencerFinalExportEnvironment,
parseSequencerFinalExportRequest,
routeSequencerFinalExport,
} from "../../../protocol/sequencer-export";
export type {
SequencerFinalExportEnvironmentIR,
SequencerFinalExportRequestIR,
SequencerFinalExportRouteIR,
} from "../../../protocol/sequencer-export";
export {
parseSequencerAudioSessionReport,
SEQUENCER_AUDIO_SESSION_SCHEMA,
} from "../../../protocol/sequencer-audio-session";
export type {
SequencerAudioContextState,
SequencerAudioOutputState,
SequencerAudioSessionIssueCode,
SequencerAudioSessionReportIR,
} from "../../../protocol/sequencer-audio-session";
export type {
SequencerMediaOperation,
SequencerMediaRevisionDecisionIR,
SequencerMediaRevisionRequestIR,
SequencerMediaRevisionResultIR,
SequencerMediaRevisionStateIR,
} from "../../../protocol/sequencer-media-revision";
export type {
SequencerMediaCacheManifestIR,
SequencerMediaProxyProfileIR,
} from "../../../protocol/sequencer-media-cache";