Воспроизведение удалённых дорожек
Согласование описаний и кандидатов подготавливает передачу, но пользователь оценивает консультацию по слышимому звуку и видимому собеседнику. Между этими событиями есть отдельный браузерный слой воспроизведения. В этом уроке подключим удалённый MediaStream к элементу video, обработаем отказ автоматического play и разберём, почему сообщение connected само по себе не является доказательством работающего плеера.
Состояние lesson-07 сохраняет peer, signaling и ICE-конфигурацию. При событии track удалённые дорожки собираются в собственный контейнер, а затем назначаются remote.srcObject. На странице появилась явная кнопка воспроизведения собеседника. Локальное превью по-прежнему беззвучно, а удалённый элемент не имеет muted, поскольку именно его аудио пользователь должен слышать.
Дорожка и воспроизведение
Событие track предоставляет объект получаемой дорожки. Оно помогает организовать представление, но дорожка может сначала не иметь поступающих кадров. Кроме того, браузер способен ограничить автоматическое воспроизведение аудио независимо от сетевого состояния. Поэтому мы не пытаемся вывести окончательное «Собеседник слышен» только на основании появления track. Это было бы обещанием, которое событие не подтверждает.
Собственный MediaStream собирает дорожки по их ID. Если обработчик получил аудио и видео отдельно, обе попадают в один контейнер. Такой приём не зависит от предположения, что event.streams[0] обязательно существует. Поле streams отражает связи, заданные отправляющей стороной; приложение может выбирать собственную модель представления, особенно когда источники переключаются или дорожки согласуются отдельно.
Ниже показаны точные фрагменты приёма и playRemote из src/app.js. Состояние state.remote создано в createPeer, элементы страницы уже существуют в HTML. Функция playRemote обрабатывает обещание браузерного play. Полный файл каталога содержит вызов той же функции по кнопке пользователя и очистку srcObject при завершении peer.
pc.ontrack=({track})=>{
if(pc!==state.pc)return;
if(!state.remote.getTracks().some(x=>x.id===track.id))state.remote.addTrack(track);
if(LESSON>=7){$('remote').srcObject=state.remote;playRemote();}else show(`Удалённая дорожка: ${track.kind}`);
};
async function playRemote(){try{await $('remote').play();}catch{show('Нажмите «Воспроизвести собеседника»');}}
Автоматическое play
Современный браузер может потребовать пользовательское действие для звукового воспроизведения. Наличие атрибута autoplay выражает намерение страницы, но не отменяет такую политику. Поэтому вызов play ожидается и при отказе показывает понятную кнопку. Повтор по явному нажатию имеет другой контекст пользовательского действия и позволяет человеку решить, когда начать слушать собеседника.
Мы не открываем новое разрешение камеры ради исправления autoplay. Устройство и плеер относятся к разным слоям. Если локальная камера работает и remote tracks получены, повторный getUserMedia не устраняет политику воспроизведения удалённого аудио. Диагностика должна проверять привязанный поток, состояние дорожек и результат play, а не бесконечно переполучать уже пригодный локальный источник.
После самостоятельного запуска с пригодным маршрутом удалённый элемент должен показать получаемое видео и воспроизводить аудио, если браузер разрешил play. При отказе должна появиться просьба нажать кнопку. Это ожидаемые ветви листинга; при авторстве видео не захватывалось, звуковой вывод не наблюдался. На странице материала нет фиктивного скриншота состоявшейся консультации.
Локальный и удалённый звук
Локальный video имеет muted, чтобы человек не слышал собственный микрофон через динамик страницы. Это не выключает его аудиодорожку и не препятствует sender передавать её. Удалённый элемент воспроизводит звук другой стороны. Если случайно назначить muted обоим элементам, транспорт может быть исправен, а консультация будет беззвучной только из-за свойств UI.
Обратная связь также зависит от физического оборудования. Две вкладки одного компьютера с открытыми динамиками могут создать неприятный акустический цикл. На будущей ручной проверке полезно использовать разные устройства или гарнитуры. Но нельзя автоматически называть любой шум сетевым jitter: физическая акустика и цифровой транспорт требуют разных наблюдений. Учебный код не записывает звук для анализа.
Изменение удалённого источника
Когда Борис заменит камеру экраном, sender может продолжить работу с той же удалённой дорожкой. Новое событие track не обязано быть единственным способом увидеть изменение кадров. Поэтому интерфейс не должен ждать повторного добавления элемента видео при каждом replaceTrack. Уже подключённый поток способен начать отображать другой источник в рамках согласованной дорожки.
При отключении видео отправитель может оставить дорожку и изменить её enabled; получатель увидит соответствующее медиаповедение, но не обязательно получит ended. Завершение дорожки, временное отсутствие данных и пользовательский mute — разные состояния. Следующий урок введёт управляющую метку через signaling, чтобы UI мог сообщить намерение собеседника, сохраняя осторожность относительно фактического состояния медиа.
Границы статуса
Состояние connectionState описывает объединённое состояние транспортных компонентов peer. Оно помогает показать подключение, обрыв или отказ. Но даже connected не доказывает, что конкретный microphone содержит речь, камера направлена на нужный объект или пользователь слышит динамики. Полная диагностика консультации должна учитывать transport, дорожки, воспроизведение и устройства вывода.
В примере статус остаётся компактным и может обновляться событиями разных слоёв. Для выпуска полезно выделить отдельные сообщения: соединение, разрешение autoplay и состояние собеседника. Иначе позднее транспортное событие способно заменить подсказку «Нажмите воспроизведение», хотя проблема плеера всё ещё актуальна. Это интерфейсное улучшение следует делать на основе настоящих наблюдений, а не придуманного скриншота.
При освобождении remote stream ссылка у элемента очищается. Старые кадры не должны выглядеть как продолжающаяся консультация после выхода другого участника. На странице можно показать нейтральную подпись ожидания, но не изображение, которое выдаётся за живой поток. Очистка контейнера также помогает новым дорожкам новой пары не смешиваться с прежними объектами.
Для самостоятельного анализа сравните отключённый remote autoplay, muted у локального превью и отсутствие пригодной ICE-пары. Они приводят к различным состояниям и требуют разных исправлений. Результат этого урока — управляемое воспроизведение удалённого потока с явной обработкой браузерного отказа. Далее добавим кнопки отключения микрофона и видео, не разрушая саму консультацию.
Есть и простой вариант ошибки разметки: элемент remote отсутствует или переименован, тогда даже пригодная дорожка не попадёт на страницу. Поэтому карта элементов входит в полный HTML каждого состояния. При собственном оформлении сохраняйте их идентификаторы или обновляйте контроллер согласованно. Это проверка связи кода и представления, которую не следует маскировать повторным ICE restart. Сначала убедитесь, что поток назначен нужному плееру, затем исследуйте сетевой этап.
Оглавление серии · Исходные состояния проекта.
API сверены с первичной документацией 11 октября 2026 года. Пример имеет статус not_run: фактический запуск и наблюдение результата читателю предстоит выполнить самостоятельно.