4 Llamadas
SITO edited this page 2026-08-20 00:14:35 +02:00

Llamadas de audio y vídeo

⚠ Rama experimental, no oficial. El Oasis oficial es https://github.com/epsylon/oasis · Volver a la portada

Dentro de una sala de Karvan hay un botón de llamada. El audio y el vídeo van directamente entre los participantes por WebRTC, cifrados de extremo a extremo por el propio protocolo.

Cómo funciona una llamada

Cómo se llama, paso a paso

1. Pulsar Call

Los cuatro botones están dentro de la sala, encima de los mensajes: 📞 Call, micrófono, cámara y colgar. Antes de llamar están apagados y el estado dice server relay.


2. Android pide la cámara

El sistema pregunta la primera vez que pulsas Llamar, no al instalar.

While using the app concede el permiso mientras la aplicación esté abierta. Only this time lo concede solo para esta llamada.


3. Y el micrófono

Segunda pregunta, la del micrófono. Son dos permisos independientes: se puede conceder uno y negar el otro, y entonces la llamada sale solo con audio o solo con vídeo.


4. Llamada en curso

Concedidos los permisos, aparece tu propia imagen. En la captura se ve el patrón de prueba de la cámara virtual del emulador; en un móvil de verdad ahí está la cámara.


Los cuatro botones se ponen amarillos —encendidos— y debajo aparece el estado mic ON · cam ON.

Tocar el micrófono o la cámara los apaga sin colgar; el botón rojo termina la llamada y devuelve la sala a su estado inicial. Los mensajes siguen ahí abajo mientras hablas.


Los permisos se piden al llamar, no al instalar

La app declara los permisos de cámara y micrófono porque Android obliga a que la lista sea fija y se decida al compilar. Pero no se conceden al instalar: son permisos peligrosos y el sistema los pide la primera vez que pulsas Llamar.

Si nunca llamas, no se te pide nada y la aplicación no accede a la cámara ni al micrófono.

Esto no era posible con la APK oficial, y no por falta de ganas. Se comprobó sobre el APK que se venía usando:

Permisos declarados INTERNET, ACCESS_NETWORK_STATE, FOREGROUND_SERVICE
Apariciones de onPermissionRequest en su código 0

Son dos barreras independientes y ninguna se sortea desde fuera de la aplicación:

  1. La lista de permisos del manifiesto es fija, decidida al compilar. Android no deja conceder un permiso que la aplicación no declara: ni desde ajustes, ni con pm grant, ni desde JavaScript. Ni siquiera aparece como opción.
  2. Aunque estuvieran declarados y concedidos, el navegador interno deniega por defecto las peticiones de cámara y micrófono si el envoltorio no implementa WebChromeClient.onPermissionRequest. Eso es código Java, fuera del alcance del backend.

Por eso esta rama trae su propio envoltorio Android, en android/, compilable con Gradle. Ver Compilar la APK.

Sin servidores de terceros

Casi todas las aplicaciones con WebRTC traen configurado un servidor STUN público, normalmente de Google. Aquí no hay ninguno.

El motivo: un servidor STUN aprende la IP pública y el momento exacto de cada consulta. Con la configuración habitual, cada llamada le estaría diciendo a Google que ese usuario está llamando ahora. En un proyecto con esta postura sobre la privacidad, no se sostiene.

Por defecto solo se usan candidatos host: funciona en la misma red local y con routers domésticos amables. Se prefiere degradar a nada antes que a un tercero.

Si necesitas atravesar NAT

Con datos móviles o detrás de CGNAT hará falta un servidor TURN. La configuración va en oasis-config.json:

"rtc": {
  "stun": ["stun:tu.pub:3478"],
  "turn": {
    "urls": ["turn:tu.pub:3478"],
    "secret": "<el static-auth-secret de tu coturn>",
    "ttlSec": 3600
  },
  "relayOnly": false
}

Con secret se generan credenciales que caducan solas (mecanismo REST de coturn): no hay usuarios que dar de alta y una credencial filtrada vale una hora.

Con relayOnly: true toda la media pasa por el TURN, de modo que ningún participante ve la dirección IP del otro. Cuesta ancho de banda del servidor, pero es lo que hay que activar si los participantes no deberían conocer sus IPs.

Si montas el TURN, endurécelo

Un TURN mal configurado es un relay abierto hacia la red interna del servidor. Como mínimo, en turnserver.conf:

use-auth-secret
static-auth-secret=<32 bytes aleatorios, fichero con permisos 600>
realm=<tu dominio>
fingerprint

no-loopback-peers
no-multicast-peers
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.168.0.0-192.168.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=127.0.0.0-127.255.255.255
denied-peer-ip=<LA IP PÚBLICA DEL PROPIO SERVIDOR>

user-quota=12
total-quota=1200
max-bps=1500000

La última línea de denied-peer-ip es la que casi todo el mundo olvida: no-loopback-peers impide llegar a 127.0.0.1, pero no impide que alguien pida un relay hacia la IP pública de tu propia máquina y alcance así tu puerto de SSB o tu servidor web. Sin esa línea, tu TURN es una puerta a tus propios servicios.

Compruébalo con turnutils_uclient contra una IP privada y contra la IP pública del servidor: las dos deben fallar.

Probado entre dos participantes

Dos navegadores distintos en la misma sala, cada uno con su cámara. A la derecha, el vídeo que llega del otro participante, con su reloj corriendo.

Lo que ocurre por debajo, leído de la señalización de la sala:

  1. Los dos anuncian su presencia (hello).
  2. Se abre primero un canal de datos, y el indicador pasa de server relay (punto gris) a 🔒 P2P (WebRTC) en verde: desde ahí, los mensajes de la sala van directos entre navegadores sin tocar el servidor.
  3. Al pulsar Llamar se renegocia añadiendo audio y video, y el otro lado responde. Comprobado en el SDP intercambiado.

Los participantes se identifican por navegador, no por identidad de SSB: dos navegadores del mismo nodo también se enlazan entre sí, que es como está hecha esta prueba.

Limitaciones honestas

  • Sin TURN, con datos móviles casi nunca conectará. El CGNAT de los operadores lo impide, y por eso hace falta el TURN del pub.
  • La señalización pasa por el servidor de la sala: un servidor comprometido podría colocarse en medio. La media va cifrada extremo a extremo, pero quien controla la señalización controla con quién crees que hablas.