/** * 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 { 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 { 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 { 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 { 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); }