Skip to content

Instantly share code, notes, and snippets.

@alex-cory
Last active January 7, 2022 22:53
Show Gist options
  • Save alex-cory/7ef7c4e37ca4af2c6ec810a288d0cf32 to your computer and use it in GitHub Desktop.
Save alex-cory/7ef7c4e37ca4af2c6ec810a288d0cf32 to your computer and use it in GitHub Desktop.
Spec for react video hook

Next Video

Goals

  • make video event listeners behave the same across all browsers
  • simplify the syntax
  • reduce bundle size compared to libraries like video.js, and others with sizes of ~15MB
  • improve performance
    • reduce renders
    • in PWAs using videos - ex: storing local videos

Ideas

  • in order to keep next package size smaller could have the package at @next/video

useVideo

This hook does not manage state in order to keep renders low. The only exception for this is when passing the timeUpdate: true option to useVideo which causes a re-render each timeupdate of the playing video. This will automatically remove event listeners when a hook is unmounted.

Property/Method Description
await video.play() This will automatically wait until the video has loaded enough data to play. It will then attempt to play the video and if it cannot play, it will attempt to play the video again with volume 0 and the video muted. This will ensure a higher success rate of actually playing the video, especially when attempting to autoplay the video.
video.playing Handy for checking if the video is actually playing. Should give correct value cross browser.
await video.canplay Instead of attaching event listeners, makes this asynchronous.
video.on('play', callback) Easy way to attach listeners to the video. Will remove these listeners if the hook unmounts.
await video.captureImage() Captures a video frame/image for the current time of the video.
await video.getFrames() Get's a list of video frames to show the video as a list of images.
video.seek()
video.hasAudio Will accurately tell if a video has audio cross browser.
video.set() This is similar to useState's setState. It will cause a re-render when setting video attributes.
video.save('url-path', blob) Will store a video locally in cache storage to be accessed in various parts of your app via the provided url-path. Ex: https://my-app.com/url-path.mp4 Only available if a service worker is enabled.
video.remove('url-path') Will remove a locally cached video from the specified url-path. Only available if a service worker is enabled.
video.el This will provide the actual current <video /> element
All Every other attribute/method you don't see here that is native to the HTML <video /> element can be accessed on the video variable returned from useVideo

Example

Below shows an example of autoplaying a video on initial mount.

import useVideo from '@next/video'

const Video = () => {
  const video = useVideo('my-video')
  const [loading, setLoading] = useState(true)
  useEffect(() => {
    ;(async () => {
      const { error } = await video.play({ src: 'https://my-video.com/video.mp4' })
      // if error, handle error 
      setLoading(false)
    })()
  }, [video])
  return <>
    <video id='my-video' />
    {loading && <div>Loading...</div>}
  </>
}

useWebcam

This hook is made to simplify using the webam in react. If the user currently has the webcam on, it will automaticaly turn off the webcam if the page focus is lost, and will restart the webcam if the page focus is brought back into view. Unlike useVideo, this hook does manage state, but in a very meticulous way as to not cause extra re-renders.

Property/Method Description
await webcam.startCamera() This will begin streaming a camera feed to the specified video element.
await webcam.stopCamera() Stops streaming a camera feed (and stop recording if currently recording) for the specified video element.
await webcam.startRecording() Starts recording the camera feed for the specified video element.
await webcam.stopRecording() Stops recording the camera feed for the specified video element.
await webcam.playRecording() Plays the recorded video by setting the webcam.recording.url as the video.src
await webcam.clearRecording() Clears the recorded video.
webcam.recording.file Blob that was recorded.
webcam.recording.url Object URL of the currently recorded video.
webcam.recording.duration Duration of the video recording.
await webcam.flipCamera() If on a mobile device, will automatically detect the rear camera and will toggle between the two cameras.
await webcam.chooseCamera(callbck) Not implemented**, but will allow you to choose a camera.
webcam.isOn Tells us if we're streaming or recording a video.
webcam.isOff Inverse of webcam.isOn
webcam.isStreaming Tells us if you can see a camera stream in the current video element.
webcam.isRecording Tells us if we are currently recording a video.
All useVideo Attributes/Methods useWebcam is a built on top of useVideo, so everything that useVideo offers comes out of the box in useWebcam.
All HTML Video Attributes/Methods Every other attribute/method you don't see here that is native to the HTML <video /> element can be accessed on the video variable returned from useVideo

Example

import { useWebcam } from '@next/video'

const Video = () => {
  const webcam = useWebcam('my-webcam')
  const toggleRecord = async () => {
    if (webcam.isRecording) {
      const { file: recordedVideo, url, duration } = await webcam.stopRecording()
      // do whatever you want with the recorded video
      
      // this will play the recorded video in the <video id='my-webcam' />
      await webcam.playRecording()
    } else {
      await webcam.startRecording()
    }
  }
  return <>
    <video id='my-webcam' />
    <button onClick={toggleRecord}>
      {webcam.isRecording ? 'Stop Recording' : 'Start Recording'}
    </button>
  </>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment