In a Python-based video rendering system, processing high-resolution videos (e.g., 4K UHD) resulted in out-of-memory (OOM) crashes despite the system having a 120GB RAM limit. The system was designed for in-memory frame processing, which caused memory spikes exceeding available resources. The solution involved implementing a smart memory-based rendering system that dynamically selects between in-memory and streaming modes based on estimated memory requirements.
Issue
The rendering system consistently crashed when processing:
- 4K UHD videos: Exceeded memory limits even with 120GB RAM due to frame buffer requirements (e.g., 287GB for 11,558 frames).
- High-frame-count videos: Systems with lower RAM (e.g., 47GB for 7,500 frames in 1080p) were also prone to crashes.
The root cause was an in-memory-only architecture, where all frames were loaded into memory before encoding, leading to catastrophic memory spikes that triggered OOM kills.
Culprit
The system lacked a dynamic memory management approach, relying exclusively on an in-memory rendering pipeline. Key issues included:
- No adaptive rendering modes: All frames were forced into memory regardless of system constraints.
- No fallback mechanism: Although a streaming-capable function existed (
write_video_with_ffmpeg_streaming), it was never utilized in production. - Memory spikes: The conversion of frame iterators to lists (
list(tqdm(...))) caused a single memory allocation equal to the total frames × resolution × color depth.
Solution
1. Configurable Memory Thresholds
Introduced a threshold-based system to automatically switch between rendering modes:
- In-memory mode: Used for videos with estimated memory usage below a configurable threshold (e.g., 90GB).
- Streaming mode: Activated for videos exceeding the threshold, preventing OOM crashes.
2. Dual-Mode Rendering Logic
Implemented a conditional rendering approach that:
- Estimated memory requirements before rendering:
(total_frames × width × height × color_depth) / (1024³). - Selected rendering mode based on the estimated memory: if above the threshold, streaming mode was activated.
- Maintained parallel processing: Both modes leveraged multiprocessing for CPU/GPU acceleration.
3. Streaming Integration
Enhanced the system to use existing streaming infrastructure with minimal changes:
- Direct frame-to-encoder pipeline: Eliminated disk I/O overhead by piping frames directly to FFmpeg.
- Memory-efficient operation: Frames were processed iteratively, avoiding bulk memory allocation.
- Configurable safety buffer: The threshold (e.g., 90GB) was set below the OOM kill limit (120GB) to ensure stability.
Impact:
- Eliminated OOM crashes: All videos, regardless of resolution or length, render successfully.
- Automatic adaptation: The system dynamically selects the optimal rendering mode without manual intervention.
- Performance retention: Streaming mode maintains parallel processing capabilities, potentially improving efficiency.
- Flexibility: Thresholds are configurable via environment variables for different hardware setups.