How the OpenMAIC Render Service Facilitates Video Export: Architecture and Implementation
The OpenMAIC render service facilitates video export by orchestrating a three-tier pipeline—React client hooks, server-side proxy logic, and an isolated containerized executor—to stream MP4 rendering progress from HyperFrames producers while maintaining UI responsiveness.
OpenMAIC separates video export into distinct client, server, and service layers to ensure scalable, cancellable rendering workflows. This architecture delegates CPU-intensive MP4 generation to an isolated container while preserving real-time progress tracking through a global state store that survives component unmounts.
Architectural Layers of the Export Pipeline
The system partitions responsibility across three logical tiers. The Client UI manages user interaction through React hooks defined in lib/video-export-app/use-render-video.ts. The Server-side orchestration layer in lib/server/render-service.ts detects whether a dedicated render service is available via the RENDER_SERVICE_URL environment variable. Finally, the Render Service—an isolated container implemented in render-service/src/render-executor.ts and coordinated by render-service/src/render-coordinator.ts—executes the actual HyperFrames production using the InProcessExecutor class.
Client-Side Integration with useRenderVideo
Components initiate exports through the useRenderVideo hook. This hook proxies the global video-render store (useVideoRenderStore) from lib/store/video-render.ts and bundles the i18n t function with the current locale before invoking startRender.
// lib/video-export-app/use-render-video.ts
import { useVideoRenderStore } from '@/lib/store/video-render';
export function useRenderVideo() {
const store = useVideoRenderStore();
return {
rendering: store.status === 'rendering',
percent: store.percent,
etaMs: store.etaMs,
options: store.options,
setOptions: store.setOptions,
renderVideo: () => store.startRender(),
};
}
The hook reads mutable state—including status ('idle' | 'compiling' | 'rendering' | 'failed' | 'succeeded'), percent, etaMs, and export options (fps, quality, format)—that persists across UI unmounts. This ensures the export ring remains visible during navigation, as implemented in components like components/stage/video-export-dialog.tsx.
Server-Side Routing and Proxy Logic
The server determines execution strategy via isRenderServiceConfigured() in lib/server/render-service.ts. This function checks for the RENDER_SERVICE_URL environment variable using getRenderServiceUrl().
// lib/server/render-service.ts
export function isRenderServiceConfigured(): boolean {
return getRenderServiceUrl() !== null;
}
When configured, the request is proxied to the isolated service endpoint (e.g., http://render-service:9000/render). If unconfigured, the system falls back to a ZIP download, allowing users to run a local CLI render. The /health endpoint enables the UI to query service capability without causing hard failures.
// Simplified server-side handler pattern
import { resolveRenderServiceUrl } from '@/lib/server/render-service';
import { proxyFetch } from '@/lib/server/proxy-fetch';
export default async function handler(req, res) {
const svc = resolveRenderServiceUrl();
if (svc.error) return res.status(503).json({ error: 'Render service not configured' });
const resp = await proxyFetch(`${svc.url}/render`, {
method: 'POST',
body: JSON.stringify(req.body),
signal: req.signal,
});
res.status(resp.status);
resp.body?.pipeTo(res);
}
Containerized Execution with InProcessExecutor
Inside the isolated container, the InProcessExecutor class orchestrates actual MP4 generation. It implements the RenderExecutor interface and manages the HyperFrames producer lifecycle through a tiny HTTP API exposing /render and /health endpoints.
Job Creation and Execution Flow
The executor follows a strict sequence:
- Create a producer job via
createRenderJob, injecting user options such asfps,quality, andformat. - Execute the job through
executeRenderJobagainst the project directory, streaming progress via theonProgresscallback. - Collect performance data into a
RenderPerfSummaryobject, translated toRenderPerformanceSummaryfor UI consumption.
For large projects, the executor delegates to executeRenderChunks, which parallelizes work across multiple workers, respects deadline constraints, and responds to AbortSignal cancellation.
Error Handling and Status Codes
The service provides granular error classification for precise failure handling:
'cancelled': Triggered when the client aborts viaAbortSignal.'deadline_exceeded': Returned when rendering exceeds the configured timeout, withcode: 'deadline_exceeded'.'unsupported_capture_mode': Raised when the project lacks requiredbeginframemarkers, returningcode: 'unsupported_capture_mode'.
All responses include a metrics object detailing runtime versions, resource profiles, and actual worker counts, which the UI can display for diagnostics.
Real-Time Progress Streaming
Progress flows from the container back to the client through streaming JSON objects. The server pipes these updates via proxyFetch, allowing the Zustand store to update without polling.
{
"progress": 45,
"stage": "rendering",
"framesRendered": 450,
"totalFrames": 1000
}
The onProgress handler in useVideoRenderStore updates percent and etaMs, while UI components read these values to render progress bars and time estimates in dialogs like components/stage/video-export-dialog.tsx.
Summary
- OpenMAIC uses a three-tier architecture separating UI (
lib/video-export-app/use-render-video.ts), routing logic (lib/server/render-service.ts), and execution (render-service/src/render-executor.ts). - The
useRenderVideohook provides the primary interface for starting exports while maintaining persistent state across the application. - Server-side logic decides between micro-service proxying and ZIP fallback based on the presence of
RENDER_SERVICE_URL. - The
InProcessExecutorhandles actual MP4 generation with support for chunked parallel processing viaexecuteRenderChunks. - Progress streaming maintains UI responsiveness through persistent state in
useVideoRenderStore. - Explicit error codes (
cancelled,deadline_exceeded,unsupported_capture_mode) enable precise failure handling and user feedback.
Frequently Asked Questions
What happens if the render service is not configured?
If RENDER_SERVICE_URL is unset, isRenderServiceConfigured() returns false and the server falls back to generating a ZIP download, as shown in lib/server/render-service.ts. Users then run the render locally via CLI tools while the UI continues to display export options and status indicators.
How does OpenMAIC handle large video projects?
For large projects, the InProcessExecutor in render-service/src/render-executor.ts invokes executeRenderChunks, which splits the workload across parallel workers. This method respects deadline constraints and cancels via AbortSignal, preventing resource exhaustion on massive timelines.
Can users cancel a video export once it starts?
Yes. The client can abort the fetch request, triggering an AbortSignal that the InProcessExecutor monitors continuously. When detected, the service immediately halts the HyperFrames producer and returns a status of 'cancelled' with a clear termination message.
What performance metrics does the render service provide?
Every render completion includes a metrics object inside the response. According to the render-service/src/render-executor.ts implementation, this contains runtime versions, the resource profile used, and actual worker counts, which the UI translates into a RenderPerformanceSummary for diagnostic display and optimization.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →