pipecat-facemode package is a small Pipecat FrameProcessor that taps
TTSAudioRawFrame, sends the PCM audio to FaceMode over the canonical
WebSocket protocol, and converts the avatar video and audio tracks in your
LiveKit room back into Pipecat output frames. It does not replace a Pipecat transport.
Pipeline placement
PlaceFaceModeVideoService after your TTS service and before the output
transport:
Install
websockets>=14
requirement matters: the service uses the additional_headers handshake API
and never falls back to an unaffinitized connection.
Quickstart
This example uses Pipecat’s LiveKit transport. Because FaceMode publishes the avatar video and audio tracks directly into the room, the transport’s own audio and video outputs are disabled.Constructor options
FaceModeService and FaceModeProcessor are exported aliases for
FaceModeVideoService.
Useful members: service.session_id, service.is_started, and
service.avatar_ready.
How audio flows
- Each
TTSAudioRawFrameis sent to FaceMode as binary little-endian signed 16-bit PCM, with no JSON or base64 wrapper.start_utteranceprecedes the first chunk of an utterance. end_utteranceis sent on a TTS stop frame or afterutterance_idle_timeoutseconds of silence.cancel_utteranceis sent on Pipecat interruption frames.- The avatar’s LiveKit audio track is pushed downstream as
OutputAudioRawFrame; the avatar video track becomes an output image frame. - The negotiated sample rate must be an integer between 8000 and 48000 Hz
(preferred: 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000) and
channels must be 1 or 2. A mid-session format change raises
FaceModeProtocolError.
Token grants
Keep all LiveKit and FaceMode credentials on the server. Two LiveKit connections are involved, and production deployments should use separate participant identities and tokens for each:room.tokenidentifies the avatar participant and must grantroom_joinfor the exact room name,can_publish(for the avatar video and audio tracks), andcan_subscribe.livekit_subscriber_tokenidentifies this Pipecat process and must grantroom_joinfor the same room andcan_subscribe. Grant publish only if the application needs it.
livekit-api:
Waiting for avatar media
await service.wait_for_avatar() resolves when FaceMode reports audio_ready
or the first avatar video frame arrives. It raises FaceModeNotReadyError
after the timeout (30 seconds by default; pass timeout=None to wait
indefinitely). Call it before announcing that avatar media is available to the
user.
Automatic reconnect
If the WebSocket drops after the session is fully started, the service reconnects without a pipeline-visible failure:- It calls
POST /api/sessions/{id}/reconnectwith the originalroomobject (plus the persistedinputProviderwhen configured). Every response mints a fresh one-timewsToken; tokens are never reused. - It opens a new socket with the
facemode.<newToken>subprotocol, resends the samestartnegotiation, and waits forstartedbefore audio resumes. An explicitly open utterance is re-declared on the new socket. - At most 2 attempts run with bounded backoff; recovery is single-flight. Sequence numbers keep increasing and already-sent audio is never replayed. Sends that arrive during the gap wait for the new socket instead of failing.
EndFrame, CancelFrame,
stop, cleanup), on server-terminal states (session_ending, ended, fatal
error messages), or before the session is fully started. When the budget is
exhausted the failure surfaces as a FaceModeProtocolError.
Lifecycle and failure notes
EndFrametriggers a graceful shutdown: the service sendsend_sessionand waits up to 3 seconds forendedbefore closing the socket.CancelFrameandcleanup()skip the graceful message.endedis a WebSocket protocol acknowledgement, not proof that backend session cleanup is complete. Callers that need backend cleanup confirmation should pollGET /api/sessions/{id}until the session reaches a terminal state.- The ingestion-ready poll budget and the WebSocket open budget are each 240 seconds.
- LiveKit tracks published before the subscriber connects are discovered at
startup; later
track_subscribedevents are handled automatically. The local subscriber’s own track is always ignored, and remote tracks are accepted only fromavatar_participant_identity. - Typed FaceMode exceptions never include API keys or token values.
Error types
All errors derive fromFaceModeError (a RuntimeError):