diff --git a/client-react/CHANGELOG.md b/client-react/CHANGELOG.md index e62f116..d7a0b89 100644 --- a/client-react/CHANGELOG.md +++ b/client-react/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to **Pipecat Client React** will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- **Multi-participant support**: Full support for handling multiple participants in sessions + - New `usePipecatClientParticipant()` hook to access individual participant data + - New `usePipecatClientParticipantIds()` hook to get all participant IDs with filtering options + - Support for both regular and screen share media tracks per participant + +### Changed + +- `PipecatClientVideo` component now requires `participantId` prop when `participant` is set to `"remote"` +- Enhanced `PipecatClientAudio` component now renders audio for all participants automatically +- Enhanced `PipecatClientVideo` component now supports remote participants +- `usePipecatClientMediaTrack()` hook signature updated to support remote participants + ## [1.0.1] - Fixed state synchronization between different instances of `usePipecatClientCamControl()`, `usePipecatClientMicControl()` and `usePipecatClientTransportState()` ([#125](https://github.com/pipecat-ai/pipecat-client-web/pull/125)) 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 ( +