Re: [mediacapture-record] Ability to record non-realtime / frame-by-frame (#213)

> Thanks for a great lib, [@Vanilagy](https://github.com/Vanilagy). Helps a ton! I can confirm that using it with MediaStreamTrackProcessor#getReader works as expected.
> 
> Here's my example (wrapping [@theatrejs](https://github.com/theatrejs)):
> 
> import { createRafDriver, ISheet, val } from '@theatre/core'
> import { useRef } from 'react'
> import WebMMuxer from 'webm-muxer'
> 
> export const rafDriver = createRafDriver({ name: 'Hubble rAF driver' })
> 
> export const useRenderer = ({
>   sheet,
>   fps = 30,
>   width = 1280,
>   height = 720,
>   bitrate = 1e6,
> }: {
>   sheet: ISheet
>   fps?: number
>   width?: number
>   height?: number
>   bitrate?: number
> }) => {
>   const { sequence } = sheet
>   const duration = val(sequence.pointer.length)
>   const totalFrames = duration * fps
> 
>   // A way for the renderer to signal that the frame has finished drawing
>   const renderDone = useRef((_a?: unknown) => {})
>   const frameReady = () => {
>     return new Promise((resolve) => (renderDone.current = resolve))
>   }
> 
>   const captureFrame = () => {
>     renderDone.current()
>   }
> 
>   const startCapture = async ({ canvas }: { canvas: HTMLCanvasElement }) => {
>     let i = 0
> 
>     let videoEncoder = new VideoEncoder({
>       output: (chunk, meta) => muxer.addVideoChunk(chunk, meta, i * fps * 1000),
>       error: (e) => console.error(e),
>     })
> 
>     videoEncoder.configure({
>       codec: 'vp09.00.10.08',
>       width,
>       height,
>       bitrate,
>       bitrateMode: "constant"
>     })
> 
>     async function encodeFrame(data: VideoFrame) {
>       const keyFrame = i % 60 === 0
>       videoEncoder.encode(data, { keyFrame })
>     }
> 
>     async function finishEncoding() {
>       await videoEncoder.flush()
>       muxer.finalize()
>       reader.releaseLock()
>       await fileWritableStream.close()
>     }
> 
>     const fileHandle = await window.showSaveFilePicker({
>       suggestedName: `video.webm`,
>       types: [
>         {
>           description: 'Video File',
>           accept: { 'video/webm': ['.webm'] },
>         },
>       ],
>     })
> 
>     const fileWritableStream = await fileHandle.createWritable()
> 
>     const muxer = new WebMMuxer({
>       target: fileWritableStream,
>       video: {
>         codec: 'V_VP9',
>         width,
>         height,
>         frameRate: fps,
>       },
>     })
> 
>     await sheet.project.ready
>     const track = canvas.captureStream(0).getVideoTracks()[0]
>     // @ts-expect-error
>     const mediaProcessor = new MediaStreamTrackProcessor(track)
>     const reader = mediaProcessor.readable.getReader()
> 
>     for (i = 0; i < totalFrames; i++) {
>       sequence.position = i / fps
>       rafDriver.tick(performance.now())
> 
>       console.log(`capturing frame ${i}/${totalFrames} at simtime ${i / fps}`)
> 
>       await frameReady()
> 
>       // @ts-expect-error
>       track.requestFrame()
>       const result = await reader.read()
>       const frame = result.value
> 
>       await encodeFrame(frame)
> 
>       frame.close()
>     }
>     finishEncoding()
>   }
> 
>   return { startCapture, captureFrame }
> }

@akre54 solution was almost perfect for me, but Safari, despite what MDN and caniuse.com says, still has not implemented MediaStreamTrackProcessor, so I created this workaround which works perfectly in Safari:
```ts
const frame = new VideoFrame(canvas, {
  timestamp: currentFrame * frameInterval * 1000,
  duration: frameInterval * 1000,
})
```
In that case you don't even need MediaStreamTrackProcessor at all

-- 
GitHub Notification of comment by VityaSchel
Please view or discuss this issue at https://github.com/w3c/mediacapture-record/issues/213#issuecomment-2648025322 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Monday, 10 February 2025 13:42:10 UTC