diff options
| author | Mohammad Amoush <muhammedamoush@gmail.com> | 2019-09-13 16:43:09 -0400 |
|---|---|---|
| committer | Mohammad Amoush <muhammedamoush@gmail.com> | 2019-09-13 16:43:09 -0400 |
| commit | ee03fa6e04dd9dba3099f75154de6ffab566ff5c (patch) | |
| tree | a4c79f9d871211fc3c15d443c67fdf7cc2aee04a /src/client/views/webcam | |
| parent | 68e554cafb6107bfde9526773b3e0e667d582c88 (diff) | |
Trial 5(Most succesful yet)
Diffstat (limited to 'src/client/views/webcam')
| -rw-r--r-- | src/client/views/webcam/DashWebCam.tsx | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/src/client/views/webcam/DashWebCam.tsx b/src/client/views/webcam/DashWebCam.tsx new file mode 100644 index 000000000..a158938fe --- /dev/null +++ b/src/client/views/webcam/DashWebCam.tsx @@ -0,0 +1,302 @@ +import React = require("react"); +import { observer } from "mobx-react"; +import { FieldViewProps, FieldView } from "../nodes/FieldView"; +import { observable } from "mobx"; + + + +function hasGetUserMedia() { + return !!( + (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)); +} + +interface WebcamProps { + audio: boolean; + audioConstraints?: MediaStreamConstraints["audio"]; + imageSmoothing: boolean; + minScreenshotHeight?: number; + minScreenshotWidth?: number; + onUserMedia: () => void; + onUserMediaError: (error: string) => void; + screenshotFormat: "image/webp" | "image/png" | "image/jpeg"; + screenshotQuality: number; + videoConstraints?: MediaStreamConstraints["video"]; +} + +@observer +export class DashWebCam extends React.Component<FieldViewProps & WebcamProps & React.HTMLAttributes<HTMLVideoElement>, WebcamState> { + static defaultProps = { + audio: true, + imageSmoothing: true, + onUserMedia: () => { }, + onUserMediaError: () => { }, + screenshotFormat: "image/webp", + screenshotQuality: 0.92 + }; + + private static mountedInstances: DashWebCam[] = []; + private static userMediaRequested = false; + private canvas: HTMLCanvasElement | undefined; + private ctx: CanvasRenderingContext2D | null = null; + private stream: MediaStream | undefined; + private video: HTMLVideoElement | null | undefined; + + @observable private hasUserMedia: boolean | undefined; + @observable private src: string | undefined; + + // constructor(props: any) { + // super(props); + // this.state = { + // hasUserMedia: false + // }; + // } + + componentDidMount() { + if (!hasGetUserMedia()) return; + + const { state } = this; + + DashWebCam.mountedInstances.push(this); + + if (!state.hasUserMedia && !DashWebCam.userMediaRequested) { + this.requestUserMedia(); + } + } + + componentDidUpdate(nextProps: WebcamProps) { + const { props } = this; + if ( + JSON.stringify(nextProps.audioConstraints) !== + JSON.stringify(props.audioConstraints) || + JSON.stringify(nextProps.videoConstraints) !== + JSON.stringify(props.videoConstraints) + ) { + this.requestUserMedia(); + } + } + + componentWillUnmount() { + //const { state } = this; + const index = DashWebCam.mountedInstances.indexOf(this); + DashWebCam.mountedInstances.splice(index, 1); + + DashWebCam.userMediaRequested = false; + if (DashWebCam.mountedInstances.length === 0 && this.hasUserMedia) { + if (this.stream!.getVideoTracks && this.stream!.getAudioTracks) { + this.stream!.getVideoTracks().map(track => track.stop()); + this.stream!.getAudioTracks().map(track => track.stop()); + } else { + ((this.stream as unknown) as MediaStreamTrack).stop(); + } + + if (this.src) { + window.URL.revokeObjectURL(this.src); + } + } + } + + getScreenshot() { + const { props } = this; + + if (!this.hasUserMedia) return null; + + const canvas = this.getCanvas(); + return ( + canvas && + canvas.toDataURL(props.screenshotFormat, props.screenshotQuality) + ); + } + + getCanvas() { + const { props } = this; + + if (!this.video) { + return null; + } + + if (!this.hasUserMedia || !this.video.videoHeight) return null; + + if (!this.ctx) { + const canvas = document.createElement("canvas"); + const aspectRatio = this.video.videoWidth / this.video.videoHeight; + + let canvasWidth = props.minScreenshotWidth || this.video.clientWidth; + let canvasHeight = canvasWidth / aspectRatio; + + if ( + props.minScreenshotHeight && + canvasHeight < props.minScreenshotHeight + ) { + canvasHeight = props.minScreenshotHeight; + canvasWidth = canvasHeight * aspectRatio; + } + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + + this.canvas = canvas; + this.ctx = canvas.getContext("2d"); + } + + const { ctx, canvas } = this; + + if (ctx) { + ctx.imageSmoothingEnabled = props.imageSmoothing; + ctx.drawImage(this.video, 0, 0, canvas!.width, canvas!.height); + } + + return canvas; + } + + requestUserMedia() { + const { props } = this; + + navigator.getUserMedia = + navigator.mediaDevices.getUserMedia; + + const sourceSelected = (audioConstraints, videoConstraints) => { + const constraints: MediaStreamConstraints = { + video: typeof videoConstraints !== "undefined" ? videoConstraints : true + }; + + if (props.audio) { + constraints.audio = + typeof audioConstraints !== "undefined" ? audioConstraints : true; + } + + navigator.mediaDevices + .getUserMedia(constraints) + .then(stream => { + DashWebCam.mountedInstances.forEach(instance => + instance.handleUserMedia(null, stream) + ); + }) + .catch(e => { + DashWebCam.mountedInstances.forEach(instance => + instance.handleUserMedia(e) + ); + }); + }; + + if ("mediaDevices" in navigator) { + sourceSelected(props.audioConstraints, props.videoConstraints); + } else { + const optionalSource = id => ({ optional: [{ sourceId: id }] }); + + const constraintToSourceId = constraint => { + const { deviceId } = constraint; + + if (typeof deviceId === "string") { + return deviceId; + } + + if (Array.isArray(deviceId) && deviceId.length > 0) { + return deviceId[0]; + } + + if (typeof deviceId === "object" && deviceId.ideal) { + return deviceId.ideal; + } + + return null; + }; + + // @ts-ignore: deprecated api + MediaStreamTrack.getSources(sources => { + let audioSource = null; + let videoSource = null; + + sources.forEach(source => { + if (source.kind === "audio") { + audioSource = source.id; + } else if (source.kind === "video") { + videoSource = source.id; + } + }); + + const audioSourceId = constraintToSourceId(props.audioConstraints); + if (audioSourceId) { + audioSource = audioSourceId; + } + + const videoSourceId = constraintToSourceId(props.videoConstraints); + if (videoSourceId) { + videoSource = videoSourceId; + } + + sourceSelected( + optionalSource(audioSource), + optionalSource(videoSource) + ); + }); + } + + DashWebCam.userMediaRequested = true; + } + + handleUserMedia(err, stream?: MediaStream) { + const { props } = this; + + if (err || !stream) { + this.setState({ hasUserMedia: false }); + props.onUserMediaError(err); + + return; + } + + this.stream = stream; + + try { + if (this.video) { + this.video.srcObject = stream; + } + this.setState({ hasUserMedia: true }); + } catch (error) { + this.setState({ + hasUserMedia: true, + src: window.URL.createObjectURL(stream) + }); + } + + props.onUserMedia(); + } + + + + + + public static LayoutString() { return FieldView.LayoutString(DashWebCam); } + + + render() { + const { props } = this; + + const { + audio, + onUserMedia, + onUserMediaError, + screenshotFormat, + screenshotQuality, + minScreenshotWidth, + minScreenshotHeight, + audioConstraints, + videoConstraints, + imageSmoothing, + ...rest + } = props; + + + return ( + <video + autoPlay + src={this.src} + muted={audio} + playsInline + ref={ref => { + this.video = ref; + }} + {...rest} + /> + ); + } +}
\ No newline at end of file |
