Visión general
Tu sistema no funciona como una transmisión clásica donde el servidor recibe video y luego lo reparte. En este diseño, el servidor ayuda a que los navegadores se encuentren, pero el video lo mueve WebRTC.
El servidor funciona como un organizador. Cuando el teléfono dice “ya tengo cámara”, el servidor guarda quién es la cámara. Cuando un usuario entra a ver, el servidor le dice a la cámara: “hay un nuevo espectador”. Entonces la cámara crea una conexión WebRTC específica para ese espectador.
Partes del sistema
app.py
Es el servidor. Entrega las rutas /camara y /ver. También guarda la cámara activa, los espectadores conectados y el contador de me gusta.
camara.html
Es la página que se abre desde el teléfono. Pide permiso a la cámara, muestra la vista previa y crea una conexión WebRTC para cada espectador.
ver.html
Es la página del espectador. Avisa que quiere ver, recibe la oferta WebRTC, responde y muestra el video remoto.
Flujo paso a paso
Arranca el servidor
Ejecutas python3 app.py. Flask queda escuchando en el puerto 5050. Desde ese momento puede entregar
la página de cámara y la página para ver la transmisión.
El teléfono entra a /camara
El teléfono abre la página transmisora. Todavía no se usa la cámara. Solamente se carga el HTML con el botón para activar o desactivar la cámara.
El usuario activa la cámara
Al presionar el botón, se ejecuta toggleCamara(). Si la cámara está apagada, llama a
activarCamara(). Esa función pide permiso al navegador mediante getUserMedia().
El teléfono avisa que la cámara está lista
Cuando la cámara ya está activa, el navegador manda el evento camara_lista. El servidor guarda
el identificador de ese navegador en camera_sid.
Un espectador entra a /ver
La página del espectador se carga y manda el evento ver_listo. El servidor agrega a ese usuario
al conjunto viewers.
La cámara crea una conexión para ese espectador
El servidor le dice a la cámara que hay un nuevo espectador. La cámara crea un objeto
RTCPeerConnection y lo guarda dentro de pcs, usando el identificador del espectador.
Oferta, respuesta y candidatos ICE
La cámara crea una oferta, el espectador responde y ambos intercambian candidatos ICE. Estos mensajes viajan por Socket.IO, pero el video viaja por WebRTC.
El video aparece en /ver
Cuando WebRTC conecta correctamente, el evento ontrack recibe el stream remoto y lo coloca en
remoteVideo.srcObject. Ahí aparece la transmisión.
Código explicado por partes
En lugar de mostrar todo el código de golpe, aquí puedes abrir solamente la parte que necesitas entender. Cada modal resalta en blanco la línea o bloque importante.
Por qué ahora funciona con varios usuarios
Antes el sistema fallaba con dos espectadores porque la cámara usaba una sola conexión WebRTC. Cuando entraba un segundo usuario, esa nueva conexión reemplazaba a la primera. El resultado era que un usuario veía video y el otro se quedaba sin imagen.
La idea clave
let pcs = {};
pcs[viewerId] = pc;
Si hay tres usuarios viendo, conceptualmente el objeto funciona así:
pcs = {
"usuario_1": RTCPeerConnection,
"usuario_2": RTCPeerConnection,
"usuario_3": RTCPeerConnection
};
Eso significa que la cámara no tiene una sola conexión general. Tiene una conexión separada por cada espectador. Por eso, cuando se conecta un nuevo usuario, no se destruye la conexión anterior.
Resumen final
El sistema se entiende mejor si lo piensas como una coordinación entre tres actores: el teléfono que transmite, el servidor que organiza y los usuarios que ven.
Flask
Entrega las páginas y define las rutas principales.
Socket.IO
Mueve mensajes pequeños de control: ofertas, respuestas, candidatos y likes.
WebRTC
Transporta el video real entre navegador transmisor y navegadores espectadores.