How Meetily's Incremental Saver Handles Real-Time Audio File Writing in Rust
The IncrementalAudioSaver buffers 30-second chunks of audio data and flushes them to disk as MP4 checkpoints, enabling crash recovery while maintaining minimal memory footprint during recording sessions.
Meetily is an open-source meeting recording application built with Tauri and Rust that captures simultaneous microphone and system audio streams. When auto-save is enabled, the system must continuously persist audio data without exhausting available RAM. The IncrementalAudioSaver component solves this by writing audio incrementally to disk rather than accumulating entire sessions in memory.
Architecture of the Real-Time Audio Pipeline
Channel-Based Stream Ingestion
In frontend/src-tauri/src/audio/recording_saver.rs, the RecordingSaver::start_accumulation method establishes an unbounded mpsc channel to receive AudioChunk structs. When auto_save is set to true, the system calls initialize_meeting_folder, which creates a .checkpoints/ subdirectory and instantiates IncrementalAudioSaver::new.
AudioChunk Structure and Flow
Each AudioChunk (defined in recording_state.rs) contains raw Vec<f32> samples captured at 48 kHz, accompanied by timestamp and device type metadata. The audio pipeline produces these chunks continuously and transmits them through the channel to a dedicated accumulation task spawned via tokio::spawn.
The Accumulation Task
While the is_saving atomic flag remains true, the task iterates over the channel and hands each received chunk to the incremental saver via incremental_saver.add_chunk(chunk). This decouples audio capture from disk I/O, preventing pipeline stalls.
Checkpoint Strategy and Buffer Management
30-Second Flush Threshold
Inside frontend/src-tauri/src/audio/incremental_saver.rs, the add_chunk method wraps incoming data in an internal AudioData struct and appends it to checkpoint_buffer. When the buffer accumulates 30 seconds of audio (calculated as sample_rate * 30 samples), the saver automatically triggers save_checkpoint.
Atomic Checkpoint Writes
The save_checkpoint function concatenates all buffered chunks and invokes encode_single_audio from encode.rs. This utility leverages FFmpeg to encode the raw f32 samples into a temporary MP4 file within .checkpoints/, named sequentially as audio_chunk_XXX.mp4. After writing completes, the checkpoint counter increments and the buffer clears, ensuring only 30 seconds of raw audio resides in RAM at any moment.
Memory Efficiency Guarantees
Because the system flushes checkpoints every 30 seconds, memory consumption remains constant regardless of recording duration. This design prevents the OOM (Out of Memory) errors that would occur if the application buffered hours of uncompressed audio data.
Finalization and Recovery Operations
Stopping the Recording
When the user ends the session, RecordingSaver::stop_and_save clears the is_saving flag, allowing the accumulation task to drain remaining chunks. The saver then invokes incremental_saver.finalize() to complete the process.
Merging Checkpoints Without Re-Encoding
The finalize method first saves any residual buffered audio as a final checkpoint. It then calls merge_checkpoints, which generates a concat_list.txt file for FFmpeg and executes:
ffmpeg -f concat -safe 0 -i concat_list.txt -c copy -y audio.mp4
The -c copy flag performs a stream copy without re-encoding, producing the final merged file rapidly while preserving original quality. After successful merging, the system deletes the .checkpoints/ directory.
Crash Recovery Mechanism
If the application terminates unexpectedly, checkpoint files remain on disk atomically. The frontend can invoke recover_audio_from_checkpoints to locate all existing audio_chunk_XXX.mp4 files and reconstruct the complete audio recording without data loss.
Implementation Example
// Initialize the saver with auto-save enabled
let mut saver = RecordingSaver::default();
saver.set_meeting_name(Some("Team Standup".into()));
let audio_sender = saver.start_accumulation(true); // true enables incremental saving
// Stream chunks from the audio pipeline
for chunk in audio_chunks_from_pipeline {
audio_sender.send(chunk).unwrap();
}
// Finalize when recording stops
let app_handle = app_handle.clone();
let duration = Some(125.3);
let final_path = saver.stop_and_save(&app_handle, duration).await?;
println!("Saved audio at {}", final_path.unwrap());
If auto_save were false, the saver would skip IncrementalAudioSaver initialization and discard raw audio chunks, preserving only transcript segments.
Summary
- The IncrementalAudioSaver manages real-time audio file writing by buffering 30-second chunks and flushing them to disk as MP4 checkpoints.
- This architecture prevents memory exhaustion during long recording sessions while enabling crash recovery through atomic file writes.
- Finalization uses FFmpeg's concat demuxer with stream copying (
-c copy) for rapid, lossless merging of checkpoint segments. - Key source files include
recording_saver.rsfor orchestration,incremental_saver.rsfor checkpoint logic, andencode.rsfor FFmpeg integration.
Frequently Asked Questions
How much memory does the incremental saver use during recording?
The system maintains approximately 30 seconds of raw audio samples in memory at any given time. At 48 kHz with 32-bit float samples, this equates to roughly 5.76 MB (48,000 × 30 × 4 bytes), regardless of whether the recording lasts minutes or hours.
What happens if the application crashes during a recording?
Checkpoint files written to the .checkpoints/ directory persist on disk atomically. Upon restart, the application can invoke recover_audio_from_checkpoints to locate all audio_chunk_XXX.mp4 files within the meeting folder and reconstruct the complete audio recording without data loss.
Why does Meetily use 30-second intervals for checkpoints?
The 30-second threshold balances write frequency with I/O overhead. Shorter intervals would increase disk writes and FFmpeg encoding overhead, while longer intervals would increase memory consumption and risk greater data loss during crashes. Thirty seconds provides optimal resilience while maintaining minimal RAM usage.
Does the final merge operation re-encode the audio?
No. The merge_checkpoints function utilizes FFmpeg's concat demuxer with the -c copy flag, which performs a stream copy without re-encoding. This preserves original audio quality and minimizes CPU usage during the finalization phase.
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 →