Skip to content

Commit

Permalink
feat: WebcamVideoTexture and ScreenVideoTexture (#2240)
Browse files Browse the repository at this point in the history
* 1st commit

* ScreenVideoTexture

* doc

* doc

* src/web
  • Loading branch information
abernier authored Dec 1, 2024
1 parent 56b7182 commit f759344
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 0 deletions.
39 changes: 39 additions & 0 deletions .storybook/stories/ScreenVideoTexture.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as THREE from 'three'
import * as React from 'react'
import { Suspense } from 'react'
import { Meta, StoryObj } from '@storybook/react'

import { Setup } from '../Setup'

import { Plane, ScreenVideoTexture } from '../../src'

export default {
title: 'Misc/ScreenVideoTexture',
component: ScreenVideoTexture,
decorators: [
(Story) => (
<Setup>
<Story />
</Setup>
),
],
} satisfies Meta<typeof ScreenVideoTexture>

type Story = StoryObj<typeof ScreenVideoTexture>

function ScreenVideoTextureScene(props: React.ComponentProps<typeof ScreenVideoTexture>) {
return (
<Plane args={[4, 2.25]}>
<Suspense fallback={<meshBasicMaterial color="gray" />}>
<ScreenVideoTexture {...props}>
{(texture) => <meshBasicMaterial side={THREE.DoubleSide} map={texture} toneMapped={false} />}
</ScreenVideoTexture>
</Suspense>
</Plane>
)
}

export const ScreenVideoTextureSt = {
render: (args) => <ScreenVideoTextureScene {...args} />,
name: 'Default',
} satisfies Story
39 changes: 39 additions & 0 deletions .storybook/stories/WebcamVideoTexture.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as THREE from 'three'
import * as React from 'react'
import { Suspense } from 'react'
import { Meta, StoryObj } from '@storybook/react'

import { Setup } from '../Setup'

import { Plane, WebcamVideoTexture } from '../../src'

export default {
title: 'Misc/WebcamVideoTexture',
component: WebcamVideoTexture,
decorators: [
(Story) => (
<Setup>
<Story />
</Setup>
),
],
} satisfies Meta<typeof WebcamVideoTexture>

type Story = StoryObj<typeof WebcamVideoTexture>

function WebcamVideoTextureScene(props: React.ComponentProps<typeof WebcamVideoTexture>) {
return (
<Plane args={[4, 2.25]}>
<Suspense fallback={<meshBasicMaterial color="gray" />}>
<WebcamVideoTexture {...props}>
{(texture) => <meshBasicMaterial side={THREE.DoubleSide} map={texture} toneMapped={false} />}
</WebcamVideoTexture>
</Suspense>
</Plane>
)
}

export const WebcamVideoTextureSt = {
render: (args) => <WebcamVideoTextureScene {...args} />,
name: 'Default',
} satisfies Story
26 changes: 26 additions & 0 deletions docs/loaders/screen-video-texture.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: ScreenVideoTexture
sourcecode: src/web/ScreenVideoTexture.tsx
---

[![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/misc-screenvideotexture) ![](https://img.shields.io/badge/-suspense-brightgreen)

<Intro>Create a video texture from [`getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia)</Intro>

```tsx
export type ScreenVideoTextureProps = Omit<VideoTextureProps, 'src'> & {
options?: DisplayMediaStreamOptions
}
```
```jsx
<ScreenVideoTexture>
{(texture) => <meshBasicMaterial map={texture} />}
```
or exposed via `ref`:
```jsx
const textureRef = useRef()
<ScreenVideoTexture ref={textureRef} />
```
26 changes: 26 additions & 0 deletions docs/loaders/webcam-video-texture.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: WebcamVideoTexture
sourcecode: src/web/WebcamVideoTexture.tsx
---

[![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/misc-webcamvideotexture) ![](https://img.shields.io/badge/-suspense-brightgreen)

<Intro>Create a video texture from [`getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)</Intro>

```tsx
export type WebcamVideoTextureProps = Omit<VideoTextureProps, 'src'> & {
constraints?: MediaStreamConstraints
}
```
```jsx
<WebcamVideoTexture>
{(texture) => <meshBasicMaterial map={texture} />}
```
or exposed via `ref`:
```jsx
const textureRef = useRef()
<WebcamVideoTexture ref={textureRef} />
```
26 changes: 26 additions & 0 deletions src/web/ScreenVideoTexture.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as React from 'react'
import { forwardRef, useEffect } from 'react'
import { suspend, clear } from 'suspend-react'
import { VideoTexture, VideoTextureProps } from '..'

export type ScreenVideoTextureProps = Omit<VideoTextureProps, 'src'> & {
options?: DisplayMediaStreamOptions
}

/**
* Create a video texture from [`getDisplayMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia)
*/
export const ScreenVideoTexture = /* @__PURE__ */ forwardRef<THREE.VideoTexture, ScreenVideoTextureProps>(
({ options = { video: true }, ...props }, fref) => {
const mediaStream = suspend(() => navigator.mediaDevices.getDisplayMedia(options), [])

useEffect(() => {
return () => {
mediaStream?.getTracks().forEach((track) => track.stop())
clear([])
}
}, [mediaStream])

return <VideoTexture ref={fref} {...props} src={mediaStream} />
}
)
35 changes: 35 additions & 0 deletions src/web/WebcamVideoTexture.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react'
import { forwardRef, useEffect } from 'react'
import { suspend, clear } from 'suspend-react'
import { VideoTexture, VideoTextureProps } from '..'

export type WebcamVideoTextureProps = Omit<VideoTextureProps, 'src'> & {
constraints?: MediaStreamConstraints
}

/**
* Create a video texture from [`getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)
*/
export const WebcamVideoTexture = /* @__PURE__ */ forwardRef<THREE.VideoTexture, WebcamVideoTextureProps>(
(
{
constraints = {
audio: false,
video: { facingMode: 'user' },
},
...props
},
fref
) => {
const mediaStream = suspend(() => navigator.mediaDevices.getUserMedia(constraints), [])

useEffect(() => {
return () => {
mediaStream?.getTracks().forEach((track) => track.stop())
clear([])
}
}, [mediaStream])

return <VideoTexture ref={fref} {...props} src={mediaStream} />
}
)
4 changes: 4 additions & 0 deletions src/web/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export * from './View'
// Gizmos
export * from './pivotControls'

// Loaders
export * from './ScreenVideoTexture'
export * from './WebcamVideoTexture'

// Controls
export * from './FaceControls'
export { DragControls } from './DragControls'
Expand Down

0 comments on commit f759344

Please sign in to comment.