el video se descargue automáticamente al detener la grabación
quieres que el video se descargue automáticamente al detener la grabación y eliminar el botón de descargar. Aquí está la versión modificada:
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Webcam con Supresor de Ruido y Grabación</title> <style> body { font-family: Arial, sans-serif; display: flex; flex-direction: column; align-items: center; padding: 20px; background-color: #f5f5f5; } #videoElement { width: 100%; max-width: 640px; background-color: #333; margin: 20px 0; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } .controls { display: flex; gap: 10px; margin-bottom: 20px; } button { padding: 10px 20px; background-color: #4285F4; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; transition: background-color 0.3s; } button:hover { background-color: #3367D6; } button:disabled { background-color: #9E9E9E; cursor: not-allowed; } #errorMsg { color: #D32F2F; margin-top: 20px; text-align: center; } .audio-panel { width: 100%; max-width: 640px; background-color: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); margin-bottom: 20px; } .meter { height: 20px; background: #E0E0E0; border-radius: 10px; overflow: hidden; margin-top: 10px; } .level { height: 100%; width: 0%; background: linear-gradient(to right, #4CAF50, #FFEB3B, #F44336); transition: width 0.05s; } .controls-row { display: flex; justify-content: space-between; margin-top: 10px; } label { display: flex; align-items: center; gap: 5px; cursor: pointer; } h2 { color: #4285F4; margin-top: 0; } #recordingIndicator { display: none; width: 12px; height: 12px; background-color: red; border-radius: 50%; margin-left: 5px; animation: pulse 1.5s infinite; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.3; } 100% { opacity: 1; } } #timer { margin-left: 10px; font-weight: bold; } </style> </head> <body> <h1>Webcam con Supresor de Ruido</h1> <div class="row g-3 align-items-stretch"> <div class="col-md-6 d-flex"> <video id="videoElement" autoplay playsinline class="img-fluid rounded w-100 h-auto bg-dark"></video> </div> <div class="col-md-6 d-flex"> <div class="card audio-panel w-100"> <div class="card-body d-flex flex-column"> <h2 class="card-title">Control de Audio</h2> <div class="mb-2">Nivel de micrófono:</div> <div class="progress mb-3" style="height: 20px;"> <div class="progress-bar" id="audioLevel" role="progressbar" style="width: 0%;"></div> </div> <div class="mt-auto"> <div class="form-group mb-3"> <div class="form-check form-switch"> <input class="form-check-input" type="checkbox" id="noiseSuppression" checked> <label class="form-check-label" for="noiseSuppression">Supresión de ruido</label> </div> </div> <div class="form-group mb-3"> <label for="gainControl" class="form-label">Volumen: <span id="gainValue">125%</span></label> <input type="range" class="form-range" id="gainControl" min="0" max="200" value="125"> </div> <div class="d-flex justify-content-between gap-2"> <div> <button id="startButton" class="btn btn-primary">Iniciar</button> <button id="stopButton" class="btn btn-danger" disabled>Detener</button> </div> <div> <button id="recordButton" class="btn btn-success" disabled> Grabar <span id="recordingIndicator"></span> <span id="timer"></span> </button> </div> </div> </div> </div> </div> </div> </div> <div id="errorMsg" class="mt-3 alert alert-danger d-none"></div> <script> // Elementos del DOM const videoElement = document.getElementById('videoElement'); const startButton = document.getElementById('startButton'); const stopButton = document.getElementById('stopButton'); const recordButton = document.getElementById('recordButton'); const recordingIndicator = document.getElementById('recordingIndicator'); const timerElement = document.getElementById('timer'); const errorMsg = document.getElementById('errorMsg'); const audioLevel = document.getElementById('audioLevel'); const noiseSuppression = document.getElementById('noiseSuppression'); const gainControl = document.getElementById('gainControl'); const gainValue = document.getElementById('gainValue'); // Objetos para el procesamiento de audio y grabación let stream = null; let audioContext = null; let analyser = null; let microphone = null; let noiseSuppressor = null; let gainNode = null; let animationId = null; let mediaRecorder = null; let recordedChunks = []; let recordingStartTime = null; let timerInterval = null; // Iniciar captura async function startCapture() { try { // Solicitar acceso a dispositivos stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: { noiseSuppression: true, echoCancellation: true, autoGainControl: true } }); // Configurar video videoElement.srcObject = stream; // Configurar procesamiento de audio setupAudioProcessing(stream); // Habilitar botones startButton.disabled = true; stopButton.disabled = false; recordButton.disabled = false; errorMsg.textContent = ''; } catch (err) { console.error("Error:", err); errorMsg.textContent = `Error: ${err.message}`; } } // Configurar procesamiento de audio function setupAudioProcessing(stream) { // Crear contexto de audio audioContext = new(window.AudioContext || window.webkitAudioContext)(); // Crear nodos de procesamiento analyser = audioContext.createAnalyser(); analyser.fftSize = 64; // Nodo de ganancia (control de volumen) gainNode = audioContext.createGain(); updateGain(); // Conectar el flujo de audio microphone = audioContext.createMediaStreamSource(stream); // Configurar supresión de ruido (filtro paso alto) if (noiseSuppression.checked) { setupNoiseSuppression(); } else { microphone.connect(gainNode); } gainNode.connect(analyser); analyser.connect(audioContext.destination); // Iniciar visualización updateAudioLevel(); } // Configurar supresión de ruido function setupNoiseSuppression() { if (noiseSuppressor) { noiseSuppressor.disconnect(); } // Crear filtro paso alto para reducir ruido de baja frecuencia noiseSuppressor = audioContext.createBiquadFilter(); noiseSuppressor.type = "highpass"; noiseSuppressor.frequency.value = 150; // Conectar los nodos microphone.disconnect(); microphone.connect(noiseSuppressor); noiseSuppressor.connect(gainNode); } // Actualizar control de ganancia function updateGain() { if (gainNode) { const gain = gainControl.value / 100; gainNode.gain.value = gain; gainValue.textContent = `${gainControl.value}%`; } } // Actualizar visualización de nivel de audio function updateAudioLevel() { if (!analyser) return; const dataArray = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(dataArray); let sum = 0; let count = 0; for (let i = 3; i < dataArray.length; i++) { sum += dataArray[i]; count++; } const average = count > 0 ? sum / count : 0; audioLevel.style.width = `${average}%`; if (average < 30) { audioLevel.style.backgroundColor = "#4CAF50"; } else if (average < 70) { audioLevel.style.backgroundColor = "#FFEB3B"; } else { audioLevel.style.backgroundColor = "#F44336"; } animationId = requestAnimationFrame(updateAudioLevel); } // Iniciar grabación function startRecording() { recordedChunks = []; // Configurar MediaRecorder const options = { mimeType: 'video/webm;codecs=vp9' }; mediaRecorder = new MediaRecorder(stream, options); // Evento para recoger datos de grabación mediaRecorder.ondataavailable = function(event) { if (event.data.size > 0) { recordedChunks.push(event.data); } }; // Evento cuando se detiene la grabación mediaRecorder.onstop = function() { downloadVideo(); }; // Iniciar grabación mediaRecorder.start(100); // Recoger datos cada 100ms // Actualizar UI recordButton.textContent = "Detener grabación"; recordButton.classList.remove("btn-success"); recordButton.classList.add("btn-warning"); recordingIndicator.style.display = "inline-block"; // Iniciar temporizador recordingStartTime = new Date(); updateTimer(); timerInterval = setInterval(updateTimer, 1000); } // Detener grabación function stopRecording() { if (mediaRecorder && mediaRecorder.state !== 'inactive') { mediaRecorder.stop(); // Detener temporizador clearInterval(timerInterval); timerElement.textContent = ""; // Actualizar UI recordButton.textContent = "Grabar"; recordButton.classList.remove("btn-warning"); recordButton.classList.add("btn-success"); recordingIndicator.style.display = "none"; } } // Descargar video grabado function downloadVideo() { const blob = new Blob(recordedChunks, { type: 'video/webm' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.style.display = 'none'; a.href = url; a.download = `grabacion-${new Date().toISOString().slice(0, 19).replace(/[:T]/g, '-')}.webm`; document.body.appendChild(a); a.click(); setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 100); } // Actualizar temporizador function updateTimer() { const elapsed = Math.floor((new Date() - recordingStartTime) / 1000); const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0'); const seconds = (elapsed % 60).toString().padStart(2, '0'); timerElement.textContent = `${minutes}:${seconds}`; } // Detener captura function stopCapture() { // Detener grabación si está activa if (mediaRecorder && mediaRecorder.state === 'recording') { stopRecording(); } if (stream) { // Detener pistas stream.getTracks().forEach(track => track.stop()); videoElement.srcObject = null; // Detener procesamiento de audio if (animationId) { cancelAnimationFrame(animationId); animationId = null; } if (audioContext) { audioContext.close(); audioContext = null; } // Detener temporizador clearInterval(timerInterval); // Resetear UI audioLevel.style.width = "0%"; startButton.disabled = false; stopButton.disabled = true; recordButton.disabled = true; recordButton.textContent = "Grabar"; recordButton.classList.remove("btn-warning"); recordButton.classList.add("btn-success"); recordingIndicator.style.display = "none"; timerElement.textContent = ""; } } // Event listeners startButton.addEventListener('click', startCapture); stopButton.addEventListener('click', stopCapture); recordButton.addEventListener('click', function() { if (recordButton.textContent === "Grabar") { startRecording(); } else { stopRecording(); } }); noiseSuppression.addEventListener('change', () => { if (noiseSuppression.checked && microphone && gainNode) { setupNoiseSuppression(); } else if (microphone && gainNode) { microphone.disconnect(); microphone.connect(gainNode); } }); gainControl.addEventListener('input', updateGain); // Limpiar al salir window.addEventListener('beforeunload', stopCapture); </script> </body> </html>
Cambios principales:
Eliminé el botón de descargar ya que ahora la descarga es automática al detener la grabación.
Modifiqué la función
stopRecording()para que:Llame automáticamente a
downloadVideo()cuando se detiene la grabación (a través del eventoonstopdel MediaRecorder)Mantenga la limpieza de la interfaz (temporizador, indicador de grabación, etc.)
La función
downloadVideo()ahora se llama automáticamente cuando se detiene la grabación, generando un archivo con nombre basado en la fecha y hora actual.La interfaz es más limpia con solo tres botones principales: Iniciar, Detener y Grabar/Detener grabación.
El video se descargará automáticamente en formato WEBM cuando el usuario haga clic en "Detener grabación". El nombre del archivo incluirá la fecha y hora de la grabación para facilitar su identificación.
Comentarios
Publicar un comentario