From a7c488a36b509496faec5a71a84530f6c70b2eab Mon Sep 17 00:00:00 2001 From: Christian Stuff Date: Wed, 17 Sep 2025 13:14:02 +0200 Subject: [PATCH 1/2] implement multi-participant handling & state --- client-react/src/PipecatClientAudio.tsx | 102 +++++++-- .../src/PipecatClientParticipantManager.tsx | 203 ++++++++++++++++++ client-react/src/PipecatClientProvider.tsx | 6 +- client-react/src/PipecatClientVideo.tsx | 24 ++- client-react/src/index.ts | 4 + .../src/usePipecatClientMediaTrack.ts | 119 +++------- .../src/usePipecatClientParticipant.ts | 16 ++ .../src/usePipecatClientParticipantIds.ts | 28 +++ 8 files changed, 390 insertions(+), 112 deletions(-) create mode 100644 client-react/src/PipecatClientParticipantManager.tsx create mode 100644 client-react/src/usePipecatClientParticipant.ts create mode 100644 client-react/src/usePipecatClientParticipantIds.ts diff --git a/client-react/src/PipecatClientAudio.tsx b/client-react/src/PipecatClientAudio.tsx index d1f061b..68dc35a 100644 --- a/client-react/src/PipecatClientAudio.tsx +++ b/client-react/src/PipecatClientAudio.tsx @@ -5,38 +5,112 @@ */ import { RTVIEvent } from "@pipecat-ai/client-js"; -import { useCallback, useEffect, useRef } from "react"; +import React, { useCallback, useEffect, useRef } from "react"; import { usePipecatClientMediaTrack } from "./usePipecatClientMediaTrack"; +import { usePipecatClientParticipantIds } from "./usePipecatClientParticipantIds"; import { useRTVIClientEvent } from "./useRTVIClientEvent"; -export const PipecatClientAudio = () => { - const botAudioRef = useRef(null); - const botAudioTrack = usePipecatClientMediaTrack("audio", "bot"); +interface AudioElementProps + extends React.AudioHTMLAttributes { + participantId: string; + track: MediaStreamTrack | null; +} + +const AudioElement = ({ + participantId, + track, + ...props +}: AudioElementProps) => { + const audioRef = useRef(null); useEffect(() => { - if (!botAudioRef.current || !botAudioTrack) return; - if (botAudioRef.current.srcObject) { + if (!audioRef.current || !track) return; + if (audioRef.current.srcObject) { const oldTrack = ( - botAudioRef.current.srcObject as MediaStream + audioRef.current.srcObject as MediaStream ).getAudioTracks()[0]; - if (oldTrack.id === botAudioTrack.id) return; + if (oldTrack.id === track.id) return; } - botAudioRef.current.srcObject = new MediaStream([botAudioTrack]); - }, [botAudioTrack]); + audioRef.current.srcObject = new MediaStream([track]); + }, [track]); useRTVIClientEvent( RTVIEvent.SpeakerUpdated, useCallback((speaker: MediaDeviceInfo) => { - if (!botAudioRef.current) return; - if (typeof botAudioRef.current.setSinkId !== "function") return; - botAudioRef.current.setSinkId(speaker.deviceId); + if (!audioRef.current) return; + if (typeof audioRef.current.setSinkId !== "function") return; + audioRef.current.setSinkId(speaker.deviceId); }, []) ); + return ( +