add stabilisation camera recorder

This commit is contained in:
Michael committed 2026-01-22 17:27:17 +01:00
1 parent 6add333ce6
commit b4347d068d
2 files changed
+62 -18

No files matched your search

+49 -14
View File
@@ -53,11 +53,22 @@ export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
console.log(`📸 Starting camera: ${config.label}, facing: ${facingMode}`);
const constraints: MediaStreamConstraints = {
audio: true,
audio: {
echoCancellation: true,
noiseSuppression: true,
},
video: {
facingMode: facingMode,
width: { ideal: config.width, min: 640 },
height: { ideal: config.height, min: 480 },
frameRate: { ideal: 30 },
// @ts-ignore - Advanced constraints for iOS stabilization
advanced: [
{ zoom: 1.0 },
// Enable video stabilization on supported devices (iOS)
{ videoStabilizationMode: 'cinematic' },
{ videoStabilizationMode: 'auto' },
]
}
};
@@ -240,18 +251,23 @@ export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
<X className="h-6 w-6" />
</Button>
<div className="flex items-center gap-2">
<div className="bg-black/30 backdrop-blur-md px-3 py-1 rounded-full text-white font-mono text-sm">
{isRecording ? formatTime(timer) : (resolutionInfo || 'Camera')}
</div>
{!isRecording && (
<Button
variant="ghost"
size="icon"
{/* Recording timer or quality selector */}
{isRecording ? (
<div className="bg-red-600 px-3 py-1 rounded-full text-white font-mono text-sm flex items-center gap-2">
<div className="w-2 h-2 bg-white rounded-full animate-pulse" />
{formatTime(timer)}
</div>
) : (
<button
onClick={() => setShowSettings(!showSettings)}
className="text-white"
className="bg-black/50 backdrop-blur-md px-3 py-2 rounded-full text-white font-medium text-sm flex items-center gap-2 border border-white/30"
>
<Settings2 className="h-5 w-5" />
</Button>
<Settings2 className="h-4 w-4" />
{quality.toUpperCase()}
<span className="text-white/60 text-xs">
{resolutionInfo || QUALITY_CONFIGS[quality].label}
</span>
</button>
)}
</div>
<Button variant="ghost" size="icon" onClick={switchCamera} className="text-white">
@@ -272,8 +288,8 @@ export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
setShowSettings(false);
}}
className={`px-3 py-2 rounded-lg text-sm font-medium transition-all ${quality === q
? 'bg-white text-black'
: 'bg-white/20 text-white hover:bg-white/30'
? 'bg-white text-black'
: 'bg-white/20 text-white hover:bg-white/30'
}`}
>
{q.toUpperCase()}
@@ -298,7 +314,26 @@ export function CameraRecorder({ onCapture, onCancel }: CameraRecorderProps) {
</div>
{/* Controls */}
<div className="absolute bottom-0 left-0 right-0 p-8 pb-12 flex justify-center items-center bg-gradient-to-t from-black/80 to-transparent">
<div className="absolute bottom-0 left-0 right-0 p-6 pb-10 flex flex-col items-center bg-gradient-to-t from-black/80 to-transparent">
{/* Quality selector - visible before recording */}
{!isRecording && (
<div className="flex gap-2 mb-6">
{(Object.keys(QUALITY_CONFIGS) as VideoQuality[]).map((q) => (
<button
key={q}
onClick={() => setQuality(q)}
className={`px-4 py-2 rounded-full text-sm font-bold transition-all ${quality === q
? 'bg-white text-black'
: 'bg-white/20 text-white border border-white/40'
}`}
>
{q.toUpperCase()}
</button>
))}
</div>
)}
{/* Record button */}
{isRecording ? (
<button
onClick={stopRecording}
+13 -4
View File
@@ -310,15 +310,22 @@ async def process_reel(request: ReelRequest, x_api_key: str = Header(None)):
# (Original audio is never used per user request)
if has_music:
# Music volume
music_vol = request.music_volume * 0.4 if has_tts else request.music_volume
# Music volume - ensure it's properly scaled (0.0 to 1.0)
# Apply volume more aggressively for noticeable effect
music_vol = request.music_volume
if has_tts:
# Reduce music even more when TTS is active so voice is clear
music_vol = music_vol * 0.3
print(
f"🎵 Music volume applied: {music_vol:.2f} (requested: {request.music_volume})"
)
filter_complex_parts.append(f"[1:a]volume={music_vol}[a1]")
mix_inputs += 1
if has_tts:
tts_idx = 2 if has_music else 1
# Voice needs to be loud and clear
filter_complex_parts.append(f"[{tts_idx}:a]volume=2.0[a2]")
filter_complex_parts.append(f"[{tts_idx}:a]volume=1.5[a2]")
mix_inputs += 1
# Build mix command
@@ -330,8 +337,10 @@ async def process_reel(request: ReelRequest, x_api_key: str = Header(None)):
if has_tts:
inputs_str += "[a2]"
# IMPORTANT: normalize=0 prevents amix from auto-adjusting volumes
# which was causing volume slider to have no effect
filter_complex_parts.append(
f"{inputs_str}amix=inputs={mix_inputs}:duration=first:dropout_transition=2[aout]"
f"{inputs_str}amix=inputs={mix_inputs}:duration=first:dropout_transition=2:normalize=0[aout]"
)
cmd.extend(["-filter_complex", ";".join(filter_complex_parts)])