fix(room): в Chrome пропадал звук после возврата из мини-окна

RoomAudioRenderer жил внутри RoomStage и рендерился дважды — в ветке
variant="pip" и в основной. При открытии Document PiP сцена
размонтируется в основном окне и монтируется в PiP-окне, поэтому
скрытые <audio> с чужими аудиотреками физически переезжали в ДРУГОЙ
документ, а при возврате — обратно. После такого переезда Chrome
теряет аудиовыход у remote-трека: пакеты продолжают приходить
(packetsReceived растёт), но totalSamplesDuration и totalAudioEnergy
замирают, и трек молчит даже в свежесозданном <audio> со свежим
MediaStream. Тот же цикл detach/attach в пределах одного документа
безвреден — дело именно в переезде между документами. В Safari бага не
было: там Document PiP не используется (video-фолбэк), сцена остаётся
в основном окне.

Рендерер вынесен в RoomPage — один экземпляр, всегда в основном
документе. В PiP-окне аудиоэлементов теперь нет вовсе, цикл «открыл
мини-окно → вернул» их не касается.
This commit is contained in:
2026-08-03 18:19:19 +03:00
parent 8b63c24332
commit a9e24f6692
2 changed files with 16 additions and 4 deletions

View File

@@ -4,7 +4,6 @@ import { Track, type Participant } from 'livekit-client'
import {
CarouselLayout,
FocusLayoutContainer,
RoomAudioRenderer,
isTrackReference,
useRoomContext,
useSpeakingParticipants,
@@ -400,7 +399,6 @@ export function RoomStage({
<section className="stage room-single-tile">
{focusTrack && <RoomParticipantTile trackRef={focusTrack} onStopSharing={handleStopSharing} />}
<PipMicToggle />
<RoomAudioRenderer />
</section>
)
}
@@ -496,7 +494,6 @@ export function RoomStage({
<span>Показать остальных ({sideTracks.length})</span>
</button>
)}
<RoomAudioRenderer />
</section>
)
}

View File

@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { PictureInPicture2 } from 'lucide-react'
import { LiveKitRoom, usePersistentUserChoices } from '@livekit/components-react'
import { LiveKitRoom, RoomAudioRenderer, usePersistentUserChoices } from '@livekit/components-react'
import type { RoomOptions } from 'livekit-client'
import '@livekit/components-styles'
import '@/styles/room.css'
@@ -285,6 +285,21 @@ export function RoomPage() {
options={roomOptions}
onDisconnected={handleDisconnected}
>
{/* Звук комнаты рендерится ЗДЕСЬ, а не внутри `RoomStage`, и ровно
одним экземпляром на всю страницу. `RoomAudioRenderer` — это набор
скрытых `<audio>`, к которым LiveKit привязывает чужие аудиотреки
(`track.attach(el)`). Пока он жил в сцене, открытие мини-плеера
переносило эти элементы в ДРУГОЙ документ (Document PiP — отдельное
окно со своим `document`), а возврат — обратно, и после возврата
звук чужих участников пропадал: Chrome теряет аудиовыход у
remote-трека, переехавшего между документами. Замерено: пакеты
продолжают приходить (`packetsReceived` растёт), а
`totalSamplesDuration`/`totalAudioEnergy` замирают, и трек молчит
даже в свежесозданном `<audio>` со свежим `MediaStream`. Тот же
цикл detach/attach В ПРЕДЕЛАХ ОДНОГО документа безвреден — дело
именно в переезде. Здесь элементы живут в основном документе
непрерывно, весь цикл «открыл мини-окно → вернул» их не касается. */}
<RoomAudioRenderer />
<div data-lk-theme="default" className="room-shell">
<RoomTopbar title={joinState.title ?? null} slug={slug} number={joinState.number} />
<div className="room-main">