diff --git a/.storybook/public/images/share-screen.jpg b/.storybook/public/images/share-screen.jpg new file mode 100644 index 000000000..ea7523b17 Binary files /dev/null and b/.storybook/public/images/share-screen.jpg differ diff --git a/.storybook/public/images/sintel-cover.jpg b/.storybook/public/images/sintel-cover.jpg new file mode 100644 index 000000000..314d231d0 Binary files /dev/null and b/.storybook/public/images/sintel-cover.jpg differ diff --git a/.storybook/stories/useVideoTexture.stories.tsx b/.storybook/stories/useVideoTexture.stories.tsx index 951ac45e2..45ce50226 100644 --- a/.storybook/stories/useVideoTexture.stories.tsx +++ b/.storybook/stories/useVideoTexture.stories.tsx @@ -1,26 +1,28 @@ import * as React from 'react' +import { useState } from 'react' import * as THREE from 'three' import { Setup } from '../Setup' -import { Plane, useVideoTexture } from '../../src' +import { Plane, useVideoTexture, useTexture } from '../../src' export default { - title: 'Loaders/VideoTexture', + title: 'Misc/useVideoTexture', component: useVideoTexture, decorators: [(storyFn) => {storyFn()}], } +// +// simple +// + function VideoTexturedPlane() { - const videoTexture = useVideoTexture( - 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4', - {} - ) + const texture = useVideoTexture('http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4') return ( <> - - + + ) @@ -38,3 +40,79 @@ export const UseVideoTextureSceneSt = () => UseVideoTextureSceneSt.story = { name: 'Default', } + +// +// Suspense +// + +function VideoTexturedPlane2() { + return ( + <> + + }> + + + + + ) +} + +function VideoMaterial({ src }) { + const texture = useVideoTexture(src) + return +} + +function FallbackMaterial({ url }: { url: string }) { + const texture = useTexture(url) + return +} + +function UseVideoTextureScene2() { + return ( + + + + ) +} + +export const UseVideoTextureSceneSt2 = () => +UseVideoTextureSceneSt2.story = { + name: 'Suspense', +} + +// +// getDisplayMedia (Screen Capture API) +// + +function VideoTexturedPlane3() { + const [mediaStream, setMediaStream] = useState(new MediaStream()) + + return ( + <> + { + const mediaStream = await navigator.mediaDevices.getDisplayMedia({ video: true }) + setMediaStream(mediaStream) + }} + > + }> + + + + + ) +} + +function UseVideoTextureScene3() { + return ( + + + + ) +} + +export const UseVideoTextureSceneSt3 = () => +UseVideoTextureSceneSt3.story = { + name: 'MediaStream', +} diff --git a/src/core/useVideoTexture.tsx b/src/core/useVideoTexture.tsx index 3f3a65a7d..e9fdad535 100644 --- a/src/core/useVideoTexture.tsx +++ b/src/core/useVideoTexture.tsx @@ -8,7 +8,7 @@ interface VideoTextureProps extends HTMLVideoElement { start?: boolean } -export function useVideoTexture(src: string, props: Partial) { +export function useVideoTexture(src: string | MediaStream, props?: Partial) { const { unsuspend, start, crossOrigin, muted, loop, ...rest } = { unsuspend: 'loadedmetadata', crossOrigin: 'Anonymous', @@ -19,11 +19,12 @@ export function useVideoTexture(src: string, props: Partial) ...props, } const gl = useThree((state) => state.gl) - const texture = suspend<[url: string], () => Promise>( + const texture = suspend( () => new Promise((res, rej) => { const video = Object.assign(document.createElement('video'), { - src, + src: (typeof src === 'string' && src) || undefined, + srcObject: (src instanceof MediaStream && src) || undefined, crossOrigin, loop, muted, @@ -34,7 +35,7 @@ export function useVideoTexture(src: string, props: Partial) video.addEventListener(unsuspend, () => res(texture)) }), [src] - ) + ) as THREE.VideoTexture useEffect(() => void (start && texture.image.play()), [texture, start]) return texture }