Multimodal: Entrada de Video

Un agente en tiempo real puede ver. Junto con la transmisión de audio, puedes enviar fotogramas de imagen desde una cámara (o pantalla) al modelo, para que el usuario pueda mostrarle algo al agente — un producto dañado para una devolución, un error en una pantalla, un documento — en lugar de describirlo.

La API

Un método en el Runner, reflejando send_audio:

runner.send_video_frame("image/jpeg", &base64_jpeg).await?;

También está disponible en el nivel inferior RealtimeRunner y directamente en RealtimeSession. La implementación predeterminada del trait es una no-op, por lo que los proveedores/modelos que no aceptan entrada visual simplemente ignoran los fotogramas.

Cómo cada proveedor maneja los fotogramas

Los dos backends tratan la visión de manera diferente, y eso debería determinar la frecuencia con la que envías fotogramas:

ProveedorMecanismoCadencia
Gemini en VivoFragmentos de medios realtimeInput continuosTransmitirlos — ~1–2 fps es natural
OpenAI en Tiempo RealUna parte input_image en un elemento de conversaciónInstantáneas — limitar a ~1 cada pocos segundos

Gemini está diseñado para video continuo: los fotogramas fluyen como el audio y el modelo razona sobre la imagen en movimiento. La visión en tiempo real de OpenAI es imagen en contexto: cada fotograma se convierte en un elemento de conversación, por lo que enviar muchos por segundo inunda el contexto y cuesta más. Envía un fotograma a OpenAI cuando sea útil (por ejemplo, el usuario dijo "mira esto"), no en cada tick.

Las interfaces de usuario de ejemplo codifican esto:

const intervalMs = provider === 'gemini' ? 700 : 2500;  // ~1.4 fps vs ~0.4 fps

Captura de fotogramas en un navegador

Toma la cámara, dibuja el <video> en un lienzo, y envía frames JPEG a través de tu WebSocket (el puente del lado del servidor los reenvía a send_video_frame):

const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480 } });
video.srcObject = stream;

const canvas = document.createElement('canvas');
setInterval(() => {
  if (ws.readyState !== WebSocket.OPEN) return;
  canvas.width = 640; canvas.height = 480;
  canvas.getContext('2d').drawImage(video, 0, 0, 640, 480);
  const data = canvas.toDataURL('image/jpeg', 0.6).split(',')[1];  // strip data: prefix
  ws.send(JSON.stringify({ type: 'video_frame', mime: 'image/jpeg', data }));
}, intervalMs);

En el servidor, reenvía al modelo:

ClientMsg::VideoFrame { mime, data } => {
    let _ = runner.send_video_frame(&mime, &data).await;  // non-fatal on error
}

Mantén los frames modestos (640×480, calidad JPEG ~0.6) — la visión no necesita resolución completa, y frames más pequeños significan menor latencia y costo.

Indicaciones para la visión

Dile al agente que puede ver, para que use la cámara de forma natural:

You can hear the customer and SEE what they show their camera. When they show
you an item (e.g. a damaged product for a return), briefly describe what you see
and use it to help resolve the issue.

Pruébalo

El ejemplo de customer_service tiene un panel de cámara: inicia la cámara, sujeta un objeto y di "Quiero devolver esto — ¿puedes verlo?". Funciona mejor en Gemini (video continuo); en OpenAI el agente razona sobre las instantáneas periódicas.

Siguiente: Diálogo afectivo →

Multimodal: Entrada de Video - Documentación ADK-Rust | ADK-Rust