diff --git a/app/components/Participant.tsx b/app/components/Participant.tsx index 65a3eb98..61cb51ec 100644 --- a/app/components/Participant.tsx +++ b/app/components/Participant.tsx @@ -95,8 +95,16 @@ export const Participant = forwardRef< preferredRid ) const audioTrack = isSelf ? userMedia.audioStreamTrack : pulledAudioTrack + // For your own screenshare there is no server echo to pull (Cloudflare + // Calls doesn't loop a track back to its sender), so pulledVideoTrack is + // always undefined and the tile renders empty. Use the local capture + // track for the self screenshare instead. const videoTrack = - isSelf && !isScreenShare ? userMedia.videoStreamTrack : pulledVideoTrack + isSelf && !isScreenShare + ? userMedia.videoStreamTrack + : isSelf && isScreenShare + ? userMedia.screenShareVideoTrack + : pulledVideoTrack useDeadPulledTrackMonitor( user.tracks.video, diff --git a/app/hooks/useUserMedia.ts b/app/hooks/useUserMedia.ts index 1fd43960..ec582476 100644 --- a/app/hooks/useUserMedia.ts +++ b/app/hooks/useUserMedia.ts @@ -24,7 +24,12 @@ export const camera = getCamera({ broadcasting: true, constraints: { width: { ideal: 1280 }, height: { ideal: 720 } }, }) -export const screenshare = getScreenshare({ audio: false }) +// audio: true → pass an audio constraint to getDisplayMedia so Chrome/Edge +// render the "Share tab audio" checkbox in the picker. This only affects +// the picker UI; the resulting track is NOT pushed through PartyTracks +// (nothing subscribes to screenshare.audio.broadcastTrack$), so no +// additional encrypted stream is created and E2EE is unaffected. +export const screenshare = getScreenshare({ audio: true }) function useNoiseSuppression() { const [suppressNoise, setSuppressNoise] = useLocalStorage(