veditor_render_server / src /utils /web-renderer.ts
3v324v23's picture
upload
bc18ad5
/**
* Web Renderer Utilities for Client-Side Rendering
*
* This module provides utilities for checking browser compatibility
* and rendering videos directly in the browser using @remotion/web-renderer.
*/
import {
canRenderMediaOnWeb,
getEncodableVideoCodecs,
getEncodableAudioCodecs,
type CanRenderIssue,
type WebRendererVideoCodec,
type WebRendererAudioCodec,
} from '@remotion/web-renderer';
// Types
export interface CSRSupportResult {
canRender: boolean;
issues: CanRenderIssue[];
resolvedVideoCodec: WebRendererVideoCodec;
resolvedAudioCodec: WebRendererAudioCodec | null;
resolvedOutputTarget: 'web-fs' | 'arraybuffer';
}
export interface AvailableCodecs {
videoCodecs: WebRendererVideoCodec[];
audioCodecs: WebRendererAudioCodec[];
}
export type RenderMode = 'csr' | 'ssr' | 'ssr-local';
export interface RenderOptions {
mode: RenderMode;
container?: 'mp4' | 'webm';
videoCodec?: WebRendererVideoCodec;
audioCodec?: WebRendererAudioCodec;
videoBitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high';
audioBitrate?: 'very-low' | 'low' | 'medium' | 'high' | 'very-high';
}
/**
* Check if the current browser supports Client-Side Rendering
*/
export async function checkCSRSupport(
width: number,
height: number,
options?: {
container?: 'mp4' | 'webm';
videoCodec?: WebRendererVideoCodec;
audioCodec?: WebRendererAudioCodec;
}
): Promise<CSRSupportResult> {
try {
const result = await canRenderMediaOnWeb({
container: options?.container ?? 'mp4',
videoCodec: options?.videoCodec ?? 'h264',
audioCodec: options?.audioCodec,
width,
height,
});
return {
canRender: result.canRender,
issues: result.issues,
resolvedVideoCodec: result.resolvedVideoCodec,
resolvedAudioCodec: result.resolvedAudioCodec,
resolvedOutputTarget: result.resolvedOutputTarget,
};
} catch (error) {
// WebCodecs API not available
return {
canRender: false,
issues: [{
type: 'webcodecs-unavailable',
message: 'WebCodecs API is not available in this browser',
severity: 'error',
}],
resolvedVideoCodec: 'h264',
resolvedAudioCodec: null,
resolvedOutputTarget: 'arraybuffer',
};
}
}
/**
* Get available video and audio codecs for the current browser
*/
export async function getAvailableCodecs(
container: 'mp4' | 'webm' = 'mp4'
): Promise<AvailableCodecs> {
try {
const [videoCodecs, audioCodecs] = await Promise.all([
getEncodableVideoCodecs(container),
getEncodableAudioCodecs(container),
]);
return { videoCodecs, audioCodecs };
} catch (error) {
// Fallback for browsers without WebCodecs
return {
videoCodecs: [],
audioCodecs: [],
};
}
}
/**
* Format CSR error messages for user display
*/
export function formatCSRErrors(issues: CanRenderIssue[]): string[] {
return issues
.filter(issue => issue.severity === 'error')
.map(issue => issue.message);
}
/**
* Format CSR warnings for user display
*/
export function formatCSRWarnings(issues: CanRenderIssue[]): string[] {
return issues
.filter(issue => issue.severity === 'warning')
.map(issue => issue.message);
}
/**
* Check if WebCodecs API is available in the browser
*/
export function isWebCodecsAvailable(): boolean {
return typeof VideoEncoder !== 'undefined' && typeof AudioEncoder !== 'undefined';
}
/**
* Get recommended render mode based on browser support
*/
export async function getRecommendedRenderMode(
width: number,
height: number
): Promise<RenderMode> {
const support = await checkCSRSupport(width, height);
return support.canRender ? 'csr' : 'ssr';
}
/**
* Get default render options with browser-specific adjustments
*/
export async function getDefaultRenderOptions(
width: number,
height: number
): Promise<RenderOptions> {
const support = await checkCSRSupport(width, height);
return {
mode: support.canRender ? 'csr' : 'ssr',
container: 'mp4',
videoCodec: support.resolvedVideoCodec,
audioCodec: support.resolvedAudioCodec ?? undefined,
videoBitrate: 'high',
audioBitrate: 'medium',
};
}
/**
* Download a blob as a file
*/
export function downloadBlob(blob: Blob, filename: string): void {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}