En un artículo anterior explicaba como transmitir el vídeo de una cámara junto al audio de un micrófono usando VLC. En ese artículo también se explicaba como crear una interfaz web para reproducir la transmisión. En este artículo voy a añadir a la interfaz web la posibilidad de grabar audio de un micrófono y enviarlo al ordenador de la cámara para su reproducción por un altavoz. Esto nos permite tener una comunicación de audio bidireccional.
Al realizar una comunicación bidireccional de audio utilizando altavoces hay que tener en cuenta el problema del eco. Supongamos que tenemos una comunicación entre dos equipos (A y B) con micrófono y altavoz. El audio del micrófono A será reproducido por el altavoz B y el audio del micrófono B por el altavoz A. Pero hay un problema, el audio del altavoz A va a ser captado por el micrófono A y el audio del altavoz B por el micrófono B. Esto hace que cualquier sonido captado por los micrófonos entre en un bucle infinito creando un eco muy molesto.
Por esta razón es necesario que mediante hardware o software se elimine del audio captado por los micrófonos el audio emitido por su correspondiente altavoz. Por ejemplo podemos usar el módulo de cancelación de eco de PulseAudio o un dispositivo que integre altavoz y micrófono con cancelación de eco. Estos sistemas también suelen incluir cancelación de ruido de fondo, lo que mejorará aún más la calidad del sonido.
CANCELACIÓN DE ECO ------------------ Micrófono A -----------------> Altavoz B ʌ | | | | v Altavoz A <----------------- Micrófono B Micrófono A -> Eliminación Audio Altavoz A -> Altavoz B Micrófono B -> Eliminación Audio Altavoz B -> Altavoz A
Si utilizamos Linux y su sistema de audio ALSA (Advanced Linux Sound Architecture), podemos fijar el volumen del altavoz mediante el programa alsamixer. Pulsando F6 seleccionamos la tarjeta de sonido y con F3 el dispositivo de reproducción.
┌───────────────────────────── AlsaMixer v1.2.8 ─────────────────────────────┐ │ Card: USB PnP Audio Device F1: Help │ │ Chip: USB Mixer F2: System information │ │ View: F3:[Playback] F4: Capture F5: All F6: Select sound card │ │ Item: USB PnP Audio Device [dB gain: -3.00] Esc: Exit │ │ | | ┌──┐ │ │ │ │ │ │ │▒▒│ │ │ │▒▒│ │ │ │▒▒│ │ │ │▒▒│ │ │ │▒▒│ │ │ │▒▒│ │ │ │▒▒│ │ │ │▒▒│ │ │ ├──┤ │ │ │OO│ │ │ └──┘ │ │ 89 │ │ <USB PnP Audio Device> │ └────────────────────────────────────────────────────────────────────────────┘
El comando aplay nos permite listar los dispositivos capaces de reproducir audio y en el archivo /etc/asound.conf podemos configurar el dispositivo que queremos utilizar por defecto. En mi caso utilizo un dispositivo USB que integra altavoz y micrófono y es detectado como dispositivo 1.
# aplay -l **** List of PLAYBACK Hardware Devices **** card 0: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones] Subdevices: 8/8 Subdevice #0: subdevice #0 Subdevice #1: subdevice #1 Subdevice #2: subdevice #2 Subdevice #3: subdevice #3 Subdevice #4: subdevice #4 Subdevice #5: subdevice #5 Subdevice #6: subdevice #6 Subdevice #7: subdevice #7 card 1: Device [USB PnP Audio Device], device 0: USB Audio [USB Audio] Subdevices: 1/1 Subdevice #0: subdevice #0
# vi /etc/asound.conf defaults.pcm.card 1 defaults.ctl.card 1
Para capturar el audio del micrófono en la web podemos utilizar la Media Capture and Streams API. Mediante el método getUserMedia se solicita al usuario el permiso para grabar el audio del micrófono. Con la clase MediaRecorder se realiza la grabación del audio. Se puede especificar el tipo de contenedor y la codificación de audio con el parámetro mimeType. Por ejemplo podemos usar el contenedor WebM y codificar el audio con Opus. No todos los navegadores permiten los mismos contenedores y codificaciones.
<script> var recorder = null; navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => { recorder = new MediaRecorder(stream,{mimeType: "audio/webm; codecs=opus"}); recorder.ondataavailable = (event) => { var request = new XMLHttpRequest(); request.open("POST", "/cgi-bin/microphone.sh", true); request.send(event.data); } }); var start = document.getElementById("start"); var stop = document.getElementById("stop"); start.onclick = () => { recorder.start(); start.style.display = "none"; stop.style.display = "initial"; } stop.onclick = () => { recorder.stop(); start.style.display = "initial"; stop.style.display = "none"; } </script>
La clase MediaRecorder tiene un método start para iniciar la grabación y un método stop para pararla. Al parar la grabación se envía el evento dataavailable con el audio grabado. Para ejecutar estos métodos podemos utilizar dos botones. Mediante CSS es posible hacer que tengan un tamaño suficientemente grande para que sea fácil su pulsación en la pantalla de un móvil o una tablet. Con la propiedad CSS display podemos mostrar y ocultar los botones. Así cuando no se esté grabando se puede mostrar solo el botón de empezar la grabación y cuando se esté grabando solo el botón de parar la grabación.
<head> <style> button { width: 200px; height: 100px; font-size: 120%; } </style> </head>
<button id="start">Start Record</button> <button id="stop" style="display: none">Stop Record</button>
Una vez grabado el audio se puede enviar al ordenador de la cámara utilizando la clase XMLHttpRequest. En el ordenador remoto se puede procesar el archivo enviado mediante cualquier lenguaje ejecutado en el servidor HTTP o a través de CGI. Por ejemplo es posible crear un script Bash y ejecutarlo mediante CGI.
El script recibe el archivo a través de la entrada estándar (stdin). Se puede reproducir directamente con un programa como cvlc o copiar a un sistema de archivos con el comando cat y después reproducirlo. A cada archivo le podemos poner de nombre la fecha y hora actuales para guardar un registro de los archivos recibidos. El script debe imprimir un código de estado HTTP seguido de dos líneas vacías. Por ejemplo el código "200 OK" si el script se ha ejecutado correctamente.
#!/usr/bin/bash cvlc -q --play-and-exit - 2> /dev/null printf "Status: 200 OK\n\n"
#!/usr/bin/bash DIRECTORY=/var/lib/microphone datetime=`date +%F_%H-%M-%S` file=$DIRECTORY/$datetime.webm cat > $file cvlc -q --play-and-exit $file 2> /dev/null printf "Status: 200 OK\n\n"
Para ejecutar el script mediante CGI con el servidor Apache hay que copiar el script al directorio /usr/lib/cgi-bin y darle permisos de ejecución. Debemos crear el directorio donde se van a guardar los archivos y dar permisos de escritura al usuario www-data, con el que se ejecuta el servidor Apache. Este usuario también debe pertenecer al grupo audio para poder reproducir los archivos de audio. Con los comandos a2enmod y a2enconf podemos habilitar el módulo cgi y su configuración serve-cgi-bin. Finalmente hay que reiniciar el servidor con el comando systemctl restart.
# cp microphone.sh /usr/lib/cgi-bin # chmod +x /usr/lib/cgi-bin/microphone.sh # mkdir /var/lib/microphone # chown www-data:www-data /var/lib/microphone # usermod -a -G audio www-data # a2enmod cgi # a2enconf serve-cgi-bin # systemctl restart apache2
En el archivo de configuración /etc/apache2/conf-enabled/serve-cgi-bin.conf se define el alias /cgi-bin/ para el directorio /usr/lib/cgi-bin/. Esto permite ejecutar cualquier programa de este directorio utilizando la dirección /cgi-bin/.
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
También es posible hacer la comunicación bidireccional de audio de forma continua. En lugar de grabar un archivo de audio y mandarlo al ordenador de la cámara, se podría enviar continuamente el audio capturado. Pensaré en como hacerlo para otro artículo.
No hay comentarios:
Publicar un comentario