最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why is the 'ended' event not firing for this MediaStreamTrack? - Stack Overflow

programmeradmin1浏览0评论

I'd like to be informed about a MediaStreamTrack's end. According to MDN an ended event is

Sent when playback of the track ends (when the value readyState changes to ended). Also available using the onended event handler property.

So I should be able to setup my callback(s) like:

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

and I expect those to be invoked, once I stop the track via:

tracks.forEach(track => track.stop());
// for good measure? See
// 
videoElem.srcObject = null;

The problem I'm having is that the callbacks are not invoked. I built the following JSFiddle, where 3 MediaStreams are created in 3 different ways:

  1. getUserMedia
  2. getDisplayMedia
  3. getCaptureStream (canvas element)

I also have 3 buttons which stop all tracks for the respective MediaStream. The behaviour is as follows:

  • All 3 streams are inactive, the MediaStream's oninactive callback is triggered (in Chrome, seems like Firefox doesn't support this).
  • All tracks have a readyState of ended after being stopped.
  • If I stop the screen stream (2. getDisplayMedia) via Chrome's UI, the track ended callback(s) are invoked.

I know that you have to watch out for the track being used by multiple sources, but that shouldn't be the case here, right? Am I missing something obvious?

Since multiple tracks may use the same source (for example, if two tabs are using the device's microphone), the source itself isn't necessarily immediately stopped. It is instead disassociated from the track and the track object is stopped. Once no media tracks are using the source, the source may actually be completely stopped.

I'd like to be informed about a MediaStreamTrack's end. According to MDN an ended event is

Sent when playback of the track ends (when the value readyState changes to ended). Also available using the onended event handler property.

So I should be able to setup my callback(s) like:

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

and I expect those to be invoked, once I stop the track via:

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;

The problem I'm having is that the callbacks are not invoked. I built the following JSFiddle, where 3 MediaStreams are created in 3 different ways:

  1. getUserMedia
  2. getDisplayMedia
  3. getCaptureStream (canvas element)

I also have 3 buttons which stop all tracks for the respective MediaStream. The behaviour is as follows:

  • All 3 streams are inactive, the MediaStream's oninactive callback is triggered (in Chrome, seems like Firefox doesn't support this).
  • All tracks have a readyState of ended after being stopped.
  • If I stop the screen stream (2. getDisplayMedia) via Chrome's UI, the track ended callback(s) are invoked.

I know that you have to watch out for the track being used by multiple sources, but that shouldn't be the case here, right? Am I missing something obvious?

Since multiple tracks may use the same source (for example, if two tabs are using the device's microphone), the source itself isn't necessarily immediately stopped. It is instead disassociated from the track and the track object is stopped. Once no media tracks are using the source, the source may actually be completely stopped.

Share Improve this question edited May 2, 2019 at 13:25 wpp asked May 2, 2019 at 12:58 wppwpp 7,3034 gold badges37 silver badges66 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 24

Why is the 'ended' event not firing for this MediaStreamTrack?

Because ended is explicitly not fired when you call track.stop() yourself. It only fires when a track ends for other reasons. From the spec:

Fired when...

The MediaStreamTrack object's source will no longer provide any data, either because the user revoked the permissions, or because the source device has been ejected, or because the remote peer permanently stopped sending data.

This is by design. The thinking was you don't need an event when you stop it yourself. To work around it do:

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

MediaStream's oninactive callback is triggered (in Chrome, seems like Firefox doesn't support this).

stream.oninactive and the inactive event are deprecated, and no longer in the spec.

As a workaround for that, you can use the similar ended event on a media element:

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

Alas, that does not appear to work in Chrome yet, but it works in Firefox.

发布评论

评论列表(0)

  1. 暂无评论