mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-25 15:25:44 +00:00
fix: replace real-time audio export with streaming decode + chunked offline rendering
Major rewrite of the audio export pipeline for stability and memory efficiency: ## Streaming Decode - Primary decode path uses WebDemuxer + AudioDecoder (WebCodecs streaming) - Falls back to bulk decodeAudioData if streaming fails - Avoids holding full compressed file + decoded PCM simultaneously ## Chunked Offline Rendering - Processes timeline in 30-second OfflineAudioContext chunks - Memory bounded to ~30s of PCM per chunk regardless of recording length ## Chunked WAV Writing - Writes PCM in ~256KB chunks instead of single massive ArrayBuffer - Eliminates OOM for long recordings on the native/FFmpeg export path ## Chunked Encoding - Single AudioEncoder kept alive across all chunks (clean AAC stream) - Proper backpressure and error propagation per chunk ## scheduleBufferThroughTimeline chunk windowing - Clips source nodes to chunk boundaries for correct cross-chunk audio - Backwards compatible defaults when no chunk params provided ## Other export fixes - Progress cap: 99% -> 100% in both exporters and UI - Muxing errors: propagated instead of silently swallowed - Mac recording: enhanced warning when no audio files for muxing - Export timeout: eliminated by removing real-time rendering
This commit is contained in:
@@ -158,7 +158,10 @@ export async function muxNativeMacRecordingWithAudio(
|
||||
}
|
||||
|
||||
if (availableAudioInputs.length === 0) {
|
||||
console.warn("[mux] No valid audio files to mux");
|
||||
console.warn(
|
||||
"[mux] No valid audio files to mux — video will have no audio. " +
|
||||
`system=${systemAudioPath ?? "none"} mic=${microphonePath ?? "none"}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -4177,8 +4177,8 @@ export default function VideoEditor() {
|
||||
? Math.min(
|
||||
typeof exportProgress?.renderProgress === "number"
|
||||
? exportProgress.renderProgress
|
||||
: (exportProgress?.percentage ?? 99),
|
||||
99,
|
||||
: (exportProgress?.percentage ?? 100),
|
||||
100,
|
||||
)
|
||||
: null;
|
||||
const isLightningExportInProgress =
|
||||
@@ -4454,8 +4454,7 @@ export default function VideoEditor() {
|
||||
</p>
|
||||
{isRenderingAudio ? (
|
||||
<p className="mt-1 text-[11px] text-muted-foreground/70">
|
||||
Audio requires real-time playback for speed/overlay
|
||||
edits
|
||||
Processing audio with speed/overlay edits
|
||||
</p>
|
||||
) : exportRenderSpeedLabel ? (
|
||||
<p className="mt-1 text-[11px] text-muted-foreground/70">
|
||||
|
||||
+928
-513
File diff suppressed because it is too large
Load Diff
@@ -1152,10 +1152,10 @@ export class ModernVideoExporter {
|
||||
const estimatedTimeRemaining =
|
||||
averageRenderFps > 0 ? remainingFrames / averageRenderFps : 0;
|
||||
const safeRenderProgress =
|
||||
phase === "finalizing" ? Math.max(0, Math.min(renderProgress ?? 99, 99)) : undefined;
|
||||
phase === "finalizing" ? Math.max(0, Math.min(renderProgress ?? 100, 100)) : undefined;
|
||||
const percentage =
|
||||
phase === "finalizing"
|
||||
? (safeRenderProgress ?? 99)
|
||||
? (safeRenderProgress ?? 100)
|
||||
: totalFrames > 0
|
||||
? (currentFrame / totalFrames) * 100
|
||||
: 100;
|
||||
@@ -1399,6 +1399,10 @@ export class ModernVideoExporter {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Muxing error:", error);
|
||||
if (!this.encoderError) {
|
||||
this.encoderError = error instanceof Error ? error : new Error(String(error));
|
||||
}
|
||||
this.cancelled = true;
|
||||
}
|
||||
});
|
||||
this.encodeQueue--;
|
||||
|
||||
@@ -885,10 +885,10 @@ export class VideoExporter {
|
||||
const estimatedTimeRemaining =
|
||||
averageRenderFps > 0 ? remainingFrames / averageRenderFps : 0;
|
||||
const safeRenderProgress =
|
||||
phase === "finalizing" ? Math.max(0, Math.min(renderProgress ?? 99, 99)) : undefined;
|
||||
phase === "finalizing" ? Math.max(0, Math.min(renderProgress ?? 100, 100)) : undefined;
|
||||
const percentage =
|
||||
phase === "finalizing"
|
||||
? (safeRenderProgress ?? 99)
|
||||
? (safeRenderProgress ?? 100)
|
||||
: totalFrames > 0
|
||||
? (currentFrame / totalFrames) * 100
|
||||
: 100;
|
||||
|
||||
Reference in New Issue
Block a user