Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MediaStreamTrackProcessor -> WebCodecs #191

Closed
chcunningham opened this issue Apr 24, 2021 · 12 comments
Closed

MediaStreamTrackProcessor -> WebCodecs #191

chcunningham opened this issue Apr 24, 2021 · 12 comments
Labels

Comments

@chcunningham
Copy link
Collaborator

In the recent editors call, @aboba mentioned receiving questions on using MediaStreamTrackProcessor w/ WebCodecs. We have some code demonstrating this in the explainer. Maybe its just a matter of sharing that more broadly? Or is there some aspect of the integration we need to highlight?

The explainer is somewhat psuedo-code, so I've made a similar demo here that folks can actually run in Chrome Canary (92).

The code looks like this

// Setup trackProcessor to grab every VideoFrame from the camera
let stream = await navigator.mediaDevices.getUserMedia({
  audio: false,
  video: true
});
let trackProcessor = new MediaStreamTrackProcessor(
  stream.getVideoTracks()[0]
);

// Log encoder outputs. A real app would do something like send to a remote peer.
function handleChunk(chunk, metadata) {
  console.log("handleChunk( timestamp=" + chunk.timestamp + " )");
}

// Setup VideoEncoder
let videoEncoder = new VideoEncoder({
  output: handleChunk,
  error: e => {
    console.log(e.message);
  }
});

// Read frames and encode()!
const reader = trackProcessor.readable.getReader();
while (true) {
  const { done, value } = await reader.read();

  if (done) return;

  if (videoEncoder.state == "unconfigured") {
    videoEncoder.configure({
      codec: "vp8",
      width: value.cropWidth,
      height: value.cropHeight
    });
  }

  videoEncoder.encode(value);
  value.close();
}
@aboba
Copy link
Collaborator

aboba commented Apr 24, 2021

@chcunningham A frequent question relates to writableControl. From the sample, it would appear that this doesn't need to be hooked up. Is there any situation in which it would be useful to do something with it?

@chcunningham
Copy link
Collaborator Author

WebCodecs users are free to entirely ignore writableControl. They may opt-in to sending control signals if they find them useful.

Currently the only control signal is "request-frame". I'm guessing this is used when you need a new frame on-demand from a low fps stream (@guidou @alvestrand, do I have that right?). If so, WebCodecs users could send that signal if needed a frame. The camera's I've tested with push frames at a steady 20-30fps, so I've not needed this.

@alvestrand mentioned that they may eventually define a signal that allows request a different frame size from the camera. This could be useful if apps are adapting to congestion or viewport size changes.

@guidou
Copy link

guidou commented Apr 24, 2021 via email

@aboba aboba added the Question label Apr 30, 2021
@aboba
Copy link
Collaborator

aboba commented Apr 30, 2021

One other question that came up today in the editor's meeting. Is there a way to reuse buffers, rather than continually allocating/freeing them?

@padenot
Copy link
Collaborator

padenot commented May 5, 2021

One other question that came up today in the editor's meeting. Is there a way to reuse buffers, rather than continually allocating/freeing them?

This is now being discussed in #212

@chcunningham
Copy link
Collaborator Author

I think we can track the remaining question in #212. Feel free to re-open if more to discuss.

@vitaly-castLabs
Copy link

That piece of sample code Chris posted in the beginning of this thread was very helpful to me, however I ran into a problem with Firefox - it does not support MediaStreamTrackProcessor. I wonder if there's a way to tie MediaStreamTrack from getUserMedia to a VideoEncoder, well, "manually"? I can't find any API to extract a frame from a track and pass it to the encoder or anything similar I can use in the absence of MediaStreamTrackProcessor

@padenot
Copy link
Collaborator

padenot commented Aug 21, 2024

https://jan-ivar.github.io/polyfills/mediastreamtrackprocessor.js is a polyfill written by us that you can use. We're planning to implement MediaStreamTrackProcessor, video-only, worker-only (as it's the only thing that has been agreed upon).

@vitaly-castLabs
Copy link

@padenot Brilliant! Hugely appreciated! I had to remove the curly brackets from constructor({track}) to make it work, just FYI.

I suspected I had to something like that - not pretty performance-wise, but does the job for now for Safari (works pretty well actually) and Firefox (probably not fast enough and causes the encoder to compress the heck out of it, combined with very low frame rate):
https://vitaly-castlabs.github.io/webcodecs-mse-player/

@jan-ivar
Copy link
Member

jan-ivar commented Aug 22, 2024

Chrome works with or without the curly brackets but they're needed per spec.

Glad you got it working! Not sure what's up with the encoder. This unencoded demo seems to run well for me in Firefox.

@vitaly-castLabs
Copy link

You're right, it was the problem of the original code calling the MSTP constructor wrong: vitaly-castLabs/webcodecs-mse-player@183504a

@vitaly-castLabs
Copy link

One more thing. @jan-ivar shouldn't controller.enqueue(new VideoFrame(canvas, {timestamp: t1})) use t1 * 1000 instead? performance.now() returns milliseconds which need to be converted to microseconds. I'm definitely getting wrong timestamps in Firefox...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants