Отключение звука и видео
Пользователь «Разговора» должен уметь временно выключить микрофон или видео, оставаясь в консультации. Остановка всего peer ради такого действия слишком груба: она разрывает транспорт и требует нового согласования. В этом уроке будем менять MediaStreamTrack.enabled и отражать намерение в интерфейсе. Состояние источника, воспроизведения и комнаты останется различимым, поэтому кнопка микрофона не превратится в скрытое завершение встречи.
Состояние lesson-08 добавляет две кнопки и служебное сообщение media через собственный signaling. Дорожки получены ранее, sender уже связан с ними. Сервер допускает только булевы поля аудио и видео и подставляет подтверждённого отправителя. Эти поля описывают заявленное управление интерфейсом, а не измерение наличия звука в удалённом микрофоне.
enabled и stop
Когда enabled равно false, дорожка остаётся существовать, но создаёт соответствующее отключённое медиаповедение. Включение возвращает использование того же ресурса. stop имеет другой смысл: дорожка завершается и не становится пригодной от повторного присваивания enabled=true. Для возврата после stop потребуется новый захват или другой существующий источник. Поэтому временный mute реализуется первым способом.
Это различие заметно и для жизненного цикла. Нажатие «Выключить микрофон» не освобождает автоматически все ресурсы консультации. Транспорт, удалённое видео и signaling продолжают работать. Пользователь получает возможность слушать Бориса и затем включиться снова. Если требуется полностью перестать использовать оборудование, интерфейс должен предложить отдельное действие завершения или удаления источника с соответствующим объяснением.
Ниже дан полный toggle из src/app.js. Поля state.audio и state.video хранят намерение пользователя, а state.stream содержит текущие локальные tracks. В будущей демонстрации экрана видеосостояние применяется также к screen track. Функция send добавляет epoch текущей комнаты, определённый сервером.
function toggle(kind){
state[kind]=!state[kind];const enabled=state[kind],trackKind=kind==='audio'?'audio':'video';
state.stream?.getTracks().filter(t=>t.kind===trackKind).forEach(t=>{t.enabled=enabled;});
state.screen?.getVideoTracks().forEach(t=>{if(kind==='video')t.enabled=enabled;});
$(kind).textContent=`${enabled?'Выключить':'Включить'} ${kind==='audio'?'микрофон':'видео'}`;
send({type:'media',audio:state.audio,video:state.video});
}
Отображение собственного решения
Текст кнопки описывает следующее действие: когда микрофон включён, она предлагает выключить его. Это уменьшает неопределённость по сравнению с постоянной надписью «Микрофон». Булево поле сохраняется независимо от выбранного устройства. После смены камеры или микрофона новый track получает прежнее пользовательское намерение, поэтому замена оборудования не должна самопроизвольно вернуть отключённый звук.
После самостоятельного нажатия на аудиокнопку локальная аудиодорожка должна получить enabled=false, а кнопка — предложить включение. При повторном нажатии та же модель возвращается в состояние true. Консультация при этом не должна заново создавать комнату. Это ожидаемая последовательность; реальные звук и интерфейс при подготовке не проверялись, поэтому статья не утверждает отсутствие слышимых артефактов.
У видео действует та же логика, но физический индикатор камеры управляется браузером и системой. Не следует обещать конкретный цвет аппаратного светодиода или немедленное выключение оборудования по одному стандартному свойству. API описывает дорожку, а устройство и браузер имеют собственное поведение. Для точного пользовательского обещания нужна ручная проверка поддерживаемых платформ.
Сведения собеседнику
Служебное сообщение сообщает намерение: «микрофон выключен» или «видео выключено». Это помогает Анне отличить молчащего Бориса от человека, который сознательно отключил звук. Но сообщение по signaling может доставляться независимо от медиа и с собственной задержкой. Интерфейс не должен считать его криптографическим доказательством того, что источник фактически не передаёт полезные данные.
Сервер проверяет структуру и identity отправителя, но не анализирует физический сигнал микрофона. Человек способен заменить клиентскую программу или прислать булево значение вручную. Поэтому статус подходит для обычного удобства общения, а не для чувствительных обещаний вроде гарантированной приватности другого устройства. Собственный пользователь управляет локальными tracks; состояние удалённой стороны остаётся сообщением и наблюдением в установленных границах.
Позднее при появлении обязательной политики mute, например у ведущего, потребуется другой контракт. Сервер может управлять разрешёнными действиями продукта, но peer-to-peer источник всё равно контролируется браузером отправителя. Модераторский запрет, доверенная запись и анализ медиа имеют более сложную архитектуру. Наш двухместный пример не объявляет такие функции реализованными только из-за булевого сообщения.
Смена устройства
Значения аудио и видео применяются к новому потоку в acquire до замены sender. Это предотвращает короткий неожиданный возврат включённого микрофона при выборе гарнитуры. Но последовательная замена нескольких sender может частично отказать. Для выпуска нужно продумать rollback: вернуть прежнюю дорожку успешному sender или выполнять независимую смену источника и явно сообщать результат каждой операции.
Состояние enabled необходимо применять именно к используемому источнику. После замены камеры экраном отправляющий video sender держит другую дорожку. Если переключать только старую camera track, экран продолжал бы передаваться. Поэтому будущая функция toggle учитывает одновременно локальный поток и активный screen stream. Отдельные источники требуют явного владения, даже когда интерфейс показывает одну общую кнопку видео.
Ошибка и восстановление UI
Если локальный поток отсутствует, переключатель не должен выдавать успешную передачу аудио. В общем контроллере методы обращаются к имеющемуся потоку через безопасную проверку. Для окончательного интерфейса стоит дополнительно запрещать кнопки до появления ресурса и после завершения. Наличие булевого поля true ещё не означает, что getUserMedia ранее вернул дорожку.
Также muted у элемента video не является заменой enabled у track. Если Анна выключит только звук удалённого плеера, Борис всё ещё может говорить и отправлять аудио; просто Анна не слышит его на странице. Такой отдельный локальный регулятор громкости может быть полезен, но он должен иметь отличимое имя. Кнопка собственного микрофона управляет другой точкой тракта.
Для самостоятельного анализа сопоставьте три сценария: временный mute, остановка camera track и выключение аудиовывода remote element. Объясните, какой ресурс остаётся пригодным и какие действия нужны для возвращения. Этот урок завершает управление существующими дорожками. Следующий использует ту же модель sender, чтобы передавать выбранное пользователем окно вместо камеры без скрытого захвата экрана.
Когда другой участник только вернулся, текущие значения управляющего состояния полезно повторно сообщить ему вместе с готовностью UI. Учебный пример отправляет событие при переключении; полноценная панель должна определить также начальное объявление после нового epoch. Иначе человек может увидеть нейтральную подпись, хотя микрофон был отключён раньше. Это вопрос синхронизации пользовательских меток, отдельный от фактических RTP-дорожек и их передачи.
Оглавление серии · Исходные состояния проекта.
API сверены с первичной документацией 11 октября 2026 года. Пример имеет статус not_run: фактический запуск и наблюдение результата читателю предстоит выполнить самостоятельно.