Почему событие «закончено» не запускается для этого MediaStreamTrack?

Я хотел бы получить информацию о конце MediaStreamTrack. Согласно MDN, событием ended является

Отправляется, когда заканчивается воспроизведение трека (когда значение readyState меняется на ended). Также доступно с использованием свойства обработчика событий onended.

Поэтому я должен иметь возможность настроить свои обратные вызовы, например:

const [track] = stream.getVideoTracks();
track.addEventListener('ended', () => console.log('track ended'));
track.onended = () => console.log('track onended');

и я ожидаю, что они будут вызваны, как только я остановлю трек через:

tracks.forEach(track => track.stop());
// for good measure? See
// https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack/stop#Stopping_a_video_stream
videoElem.srcObject = null;

У меня проблема в том, что обратные вызовы не вызываются. Я создал следующий JSFiddle, где 3 MediaStream создаются тремя разными способами:

  1. getUserMedia
  2. getDisplayMedia
  3. getCaptureStream (элемент холста)

У меня также есть 3 кнопки, которые останавливают все треки для соответствующего MediaStream. Поведение выглядит следующим образом:

  • Все 3 потока inactive, запускается обратный вызов MediaStream oninactive (в Chrome кажется например, Firefox не поддерживает это).
  • Все треки имеют readyState из ended после остановки.
  • Если я останавливаю поток экрана (2. getDisplayMedia) через пользовательский интерфейс Chrome, вызываются обратные вызовы завершения трека.

Кнопка прекращения обмена в Chrome

Я знаю, что вам нужно следить за тем, чтобы трек не использовался несколькими источниками, но здесь этого быть не должно, верно? Я упускаю что-то очевидное?

Поскольку несколько дорожек могут использовать один и тот же источник (например, если две вкладки используют микрофон устройства), сам источник не обязательно сразу останавливается. Вместо этого он отсоединяется от дорожки, и объект дорожки останавливается. Как только ни одна дорожка мультимедиа не использует источник, источник может быть фактически полностью остановлен.


person wpp    schedule 02.05.2019    source источник


Ответы (1)


Почему событие «закончено» не запускается для этого MediaStreamTrack?

Поскольку Завершено явно не срабатывает при вызове track.stop () себя. Он срабатывает только тогда, когда трек заканчивается по другим причинам. Из спецификации:

Уволен, когда...

Источник объекта MediaStreamTrack больше не будет предоставлять никаких данных либо потому, что пользователь отозвал разрешения, либо потому, что исходное устройство было отключено, либо потому, что удаленный одноранговый узел навсегда прекратил отправку данных.

Это по дизайну. Мысль заключалась в том, что вам не нужно событие, когда вы сами его останавливаете. Чтобы обойти это, сделайте:

track.stop();
track.dispatchEvent(new Event("ended"));

Запускается обратный вызов oninactive MediaStream (в Chrome похоже, что Firefox не поддерживает это).

stream.oninactive и событие inactive устарели и больше не входят в спецификацию.

В качестве обходного пути вы можете использовать аналогичный завершено событие для медиа-элемента:

video.srcObject = stream;
await new Promise(resolve => video.onloadedmetadata = resolve);
video.addEventListener("ended", () => console.log("inactive!")); 

Увы, это пока не работает в Chrome, но работает в Firefox.

person jib    schedule 02.05.2019
comment
Большое спасибо за разъяснения и обходной путь. Вместо этого я мог бы отключить остановку, чтобы избежать дальнейшей путаницы. Думаю, я зациклился на том, когда значение readyState изменяется на завершено, а окончание IMO может быть не самым ясным названием для события. - person wpp; 03.05.2019
comment
@wpp Что ж, "ended" по-прежнему будет срабатывать, когда пользователь нажимает кнопку Прекратить совместное использование в самом пользовательском интерфейсе браузера или когда удаленные треки от одноранговых соединений заканчиваются, поэтому используйте другое имя, только если вы этого не сделаете. заботиться об этих событиях или нуждаться в том, чтобы отличать их от своих собственных. - person jib; 03.05.2019
comment
Спасибо за подробный ответ. Это действительно прискорбно. :-( Я думаю, что рассуждения о событии "завершение" ошибочны. Теперь мы должны управлять состоянием отдельно и делать что-то дважды. Очень жаль, что событие inactive устарело... это единственный способ наблюдать за потоком, созданным другой код, если не считать все исправления обезьяны.Знаете ли вы причину удаления события inactive для потоков? - person Brad; 02.04.2020
comment
@Brad Фокус сместился на треки, для которых потоки являются сосудами. Вы можете полифиллировать inactive, используя stream.onremovetrack и проверяя readyState оставшихся треки. Определение также было ограничено, поскольку audioElement.srcObject был технически активным даже если он содержит только видеодорожки. - person jib; 02.04.2020
comment
@jib Ах, круто, я не знал, что трек был удален, когда он был остановлен. Я буду экспериментировать с этим. Спасибо за дополнительную информацию! Очень ценится. - person Brad; 02.04.2020
comment
@jib К сожалению, кажется, что removetrack не запускается, когда треки из getUserMedia останавливаются. Я взломаю его другими способами. Спасибо за информацию. - person Brad; 02.04.2020
comment
@Brad Ты прав, тебе тоже нужно послушать track.onended. Даже тогда вам нужно было бы поймать JS, вызывающий track.stop(), так как это не срабатывает ended. - person jib; 02.04.2020
comment
@jib В моем проекте я хочу отправлять аудио с демонстрацией экрана, поэтому я инициировал 2 потока и добавил звуковую дорожку в поток демонстрации экрана. Он работает нормально, но теперь я не могу отслеживать событие, когда поток становится неактивным. Раньше без запуска звуковой дорожки работала функция screensharestream.oninactive, но теперь она не работает. Не могли бы вы помочь мне решить эту проблему? - person Arjun; 05.05.2020
comment
@Arjun oninactive не совместим с Интернетом, поэтому, пожалуйста, прекратите его использовать. Вы пробовали событие ended, как показано в этом ответе? Если это не помогло, рассмотрите возможность публикации нового вопроса. - person jib; 07.05.2020