Un video de avatar solo se siente creíble si la persona se mantiene consistente en el tiempo. Cuando la cara se desplaza, los dientes cambian, el lip-sync se desajusta o el movimiento se reinicia entre clips, la gente se da cuenta enseguida. Esto importa todavía más en los avatares que en muchas otras tareas de generación de video, porque quien mira está viendo a una persona específica hablar, muchas veces de cerca y durante bastante tiempo.
En el mundo actual de generación de video, la duración sigue siendo una de las limitaciones más visibles. Muchos modelos y productos ofrecen la generación como un clip de longitud fija —unos pocos segundos, con muy pocos sistemas capaces de generar más de unos pocos minutos. En los productos de avatares, ese límite aparece directamente en los flujos de trabajo de los clientes. Los clientes quieren escenas/videos más largos y consistentes para videos de capacitación, demos de ventas, recorridos de producto, educación, soporte y agentes que tengan que seguir hablando hasta que la tarea esté terminada, y además quieren una vista previa rápida para iterar sobre prompts, movimiento y guion.
En HeyGen, eso se tradujo en tres requisitos concretos:
- Long-scene consistency. The avatar needs to preserve identity, lip sync, expression, and motion continuity not just for one short clip, but across many chunks of generated video.
- No fixed duration cap. A generation might be ten seconds, ten minutes, or an open-ended realtime session.
- Fast preview, realtime or faster-than-realtime generation. The system should start producing frames quickly and even allow streaming out the generated frames while inference is still ongoing.
This post walks through the inference framework we built to meet those requirements.
The Underlying Model Architecture
The framework is built around HeyGen's avatar video generation models — the Avatar IV and Avatar V families. At a high level, the model takes a reference image/video, driving audio, and optional text or scene conditioning, then generates a video of that avatar speaking with the right identity, expression, and motion.
The core generation model is a Diffusion Transformer, or DiT, trained with flow matching. Instead of compressing the person into a small identity embedding, the model conditions on rich reference tokens so it can preserve details that matter for avatars: face shape, teeth, skin texture, mouth movement, gesture style, and speaking rhythm.
La ruta de inferencia en producción tiene tres etapas principales:
- Generación de audio a video. Un DiT base genera latentes de video de baja resolución a partir de la identidad de referencia, las características de audio y las señales de condicionamiento. Esta etapa se centra en el movimiento, el lip-sync y la coherencia temporal.
- Súper resolución con reconocimiento de identidad. Un segundo modelo refina esos latentes para generar una salida en alta resolución, poniendo especial atención en las zonas donde las personas son más sensibles a los artefactos, especialmente la cara y la boca.
- Decodificación VAE en streaming. Un decodificador VAE convierte latentes de alta resolución en fotogramas RGB por partes, para que los fotogramas se puedan emitir antes de que el video completo esté listo.
Para generar videos largos, el sistema procesa los datos en bloques. Mientras que el primer bloque depende por completo de la referencia estática, los bloques siguientes usan datos de los límites de los segmentos anteriores. Esto permite que el avatar siga hablando de forma natural sin tener que reiniciar su postura ni su identidad desde cero.
El framework de streaming y el ciclo de la pipeline
Para admitir la ejecución por bloques, el framework de inferencia usa una arquitectura modular de tres niveles que opera sobre ventanas de tiempo localizadas y libera los recursos inmediatamente después de que se procesa cada bloque.
- Módulo: un contenedor alrededor de un modelo específico y su checkpoint (por ejemplo, A2V DiT, Super-Resolution DiT, componentes VAE, codificadores de texto/audio).
- Etapa: una unidad de ejecución tipada que coordina uno o más módulos (por ejemplo, generación de contexto, super-resolución).
- Pipeline: el grafo de ejecución que conecta las etapas entre sí, administra el estado compartido y coordina los modos de ejecución en streaming o por lotes.
La fase de inicialización codifica la identidad de referencia en latentes una vez por cada solicitud. Luego, el pipeline ejecuta un bucle continuo a través de las etapas restantes hasta que se agota el flujo de audio de entrada:

- Generación de contexto: convierte los segmentos de audio entrantes en características, los combina con texto o condicionamiento de escena y prepara los tensores de ruido objetivo.
- Audio a video: Ejecuta un proceso de difusión en múltiples pasos para generar latentes de baja resolución. Esta etapa condiciona el fragmento actual con los cuadros límite del fragmento anterior para mantener la continuidad del movimiento.
- Súper resolución: aumenta la resolución de los latentes de movimiento a resolución completa en un solo paso, priorizando el detalle espacial en el rostro.
- VAE Decode-and-Publish: decodifica los latentes de alta resolución en cuadros RGB y los envía directamente al codificador de salida (H.264 / AAC) para su almacenamiento inmediato o reproducción en vivo.
Continuidad de bordes y consistencia entre segmentos
Generar video en segmentos separados introduce posibles discontinuidades en los límites. El framework mitiga esto utilizando dos clasificaciones de bloques distintas:
- N Chunks: segmentos que generan la línea de tiempo principal del avatar.
- I Chunks (Interpolación): segmentos diseñados para suavizar las transiciones entre N chunks secuenciales.
La secuencia de ejecución está estructurada de la siguiente manera:
N0 -> N1 -> I0 -> N2 -> I1 -> N3 -> I2 -> ...
Un bloque I se genera solo después de que se completan los bloques N anteriores y posteriores. Usa el cuadro final del bloque N previo y un cuadro temprano del bloque N actual como cuadros de anclaje para calcular el movimiento de transición. Después de la generación, se descartan las predicciones de anclaje redundantes, dejando únicamente la transición suavemente interpolada. Este mecanismo acota la ventana de contexto necesaria y, al mismo tiempo, preserva la consistencia temporal.
Memoria constante a lo largo de la duración
Un flujo de trabajo de video convencional acumula latentes, cuadros decodificados y contexto de atención durante la ejecución, lo que hace que el consumo de memoria de la GPU aumente de forma lineal con la duración del video.
Para habilitar la generación abierta, este framework mantiene un estado rodante estricto. El sistema conserva solo el condicionamiento de referencia estático y un conjunto mínimo de tensores ancla necesarios para las transiciones entre chunks. Todos los recursos intermedios —incluidas las características de audio, los tensores de ruido, las activaciones internas y los fotogramas RGB en bruto— se eliminan de la memoria inmediatamente después de que un chunk se decodifica y se escribe.
Como resultado, el perfil máximo de uso de memoria de la GPU se mantiene constante tanto si generás un clip corto como una secuencia extendida; la utilización de recursos escala según el tamaño de bloque definido y no según la duración total de la sesión.
Etapas de carga y descarga dentro del pipeline
Cada solicitud se ejecuta en un nodo con 8 GPU. Usamos FSDP para fragmentar los parámetros de modelos grandes entre las GPU. Cada rango posee solo una fracción de los pesos, reúne los parámetros que necesita para un cálculo y luego los libera de nuevo. Esto es lo que permite que varios modelos grandes —el DiT base, el DiT de superresolución, el codificador de texto, el codificador de audio y el VAE— entren en un solo nodo.
Hay un compromiso. FSDP introduce sobrecarga de comunicación durante la inferencia porque los parámetros tienen que reunirse durante las pasadas hacia adelante. Usamos una combinación de técnicas para ocultar esa sobrecarga y mantener los modelos co-ubicados fuera de la GPU cuando no están en uso:
- Prefetching hacia adelante. El AllGather de los parámetros del siguiente bloque se ejecuta por adelantado y se superpone con el cálculo del bloque actual, ocultando la latencia de recopilación en la ruta crítica.
- Desfragmentación perezosa por bloque desde la CPU. Cuando un modelo se trae de vuelta desde la memoria de CPU fijada (pinned), no preparamos de antemano todo el conjunto de pesos. Cada bloque del transformer se desfragmenta (copia de host a dispositivo + AllGather) justo antes de su pasada hacia adelante, de modo que la transferencia H2D del bloque n+1 se superpone con el cómputo del bloque n.
- Descarga en CPU fijada entre etapas. Los parámetros de un modelo que no se está ejecutando en ese momento se mantienen en memoria de CPU fijada, así los modelos que comparten la misma máquina (DiT base, DiT de super-resolución, codificador de texto, codificador de audio, VAE) no necesitan tener todos sus pesos en la GPU al mismo tiempo. La memoria fijada es lo que hace que las copias H2D sean lo suficientemente rápidas como para solaparse con el cómputo.
- Ubicación de procesos optimizada para NUMA. Cada proceso se fija al mismo nodo NUMA que su GPU asignada, de modo que las transferencias CPU↔GPU se realizan al ancho de banda completo de PCIe/NVLink sin cruzar el interconector entre sockets.
Cambio de modelo en menos de 10 ms entre etapas
El beneficio práctico de las técnicas anteriores es que pasar la GPU del modelo de una etapa al de la siguiente —por ejemplo, A2V DiT → Super-Resolution DiT, o SR DiT → decodificador VAE— es prácticamente gratis. Como el modelo saliente se descarga de forma asíncrona y el primer bloque del modelo entrante se desfragmenta justo a tiempo, tanto la copia H2D como el AllGather quedan ocultos detrás del cómputo que ya se está ejecutando. De punta a punta, la sobrecarga observable por cada cambio de modelo es menor a 10 ms —muy por debajo del presupuesto de un solo frame en nuestras tasas de cuadros objetivo. En concreto, esto es lo que permite que el loop del pipeline de streaming (Context Gen → A2V → SR → VAE Decode-and-Publish) recorra varios modelos grandes por chunk sin que el propio cambio de modelo se convierta nunca en el cuello de botella.
Publicación en streaming en tiempo real
Para que el modelo sea lo suficientemente rápido como para transmitir en tiempo real, hicimos muchas optimizaciones de inferencia; consultá https://www.heygen.com/research/avatar-v-inference para más detalles sobre esta parte.
Una vez que el pipeline emite el video en bloques, en tiempo real, la entrega por streaming se vuelve una extensión natural de la inferencia en lugar de un paso de posprocesamiento separado.
Para la ruta en tiempo real de tipo transmisión, publicamos los cuadros generados en Amazon Kinesis Video Streams (KVS). KVS suele mencionarse en el contexto de cámaras, dispositivos IoT y contenido multimedia subido. En nuestro caso, la "cámara" es el propio pipeline de inferencia: los cuadros son creados por el modelo, codificados de inmediato y enviados a KVS como una transmisión en vivo.
El escritor de salida recibe cuadros RGB decodificados desde el VAE de streaming y los envía a un pipeline de GStreamer. El video se codifica en H.264 y el audio en AAC, y luego ambas pistas se envían a kvssink, el sink productor de KVS. Desde ahí, los espectadores pueden reproducir la sesión como una transmisión en vivo mientras todavía se está generando.
Resultados y aprendizajes
El framework cambió la generación de Avatar IV y Avatar V de un renderizado de escenas fijas a una generación continua y abierta por streaming. El resultado más importante es simple: eliminamos los límites de duración de escena para Avatar IV y Avatar V. Para la generación en tiempo real de Avatar IV, logramos un tiempo hasta el primer cuadro de menos de 5 segundos y una generación de más de 27 cuadros por segundo para videos Avatar IV en 720p — más rápido que la reproducción en tiempo real.