I'm trying to create a function which would stop camera transmition and add this function to my exisitng code. This is the component that I am trying to change into a function:
class WebcamCapture extends React.Component {
setRef = (webcam) => {
this.webcam = webcam;
}
stop = () => {
let stream = this.webcam.video.srcObject;
const tracks = stream.getTracks();
tracks.forEach(track => track.stop());
this.webcam.video.srcObject = null;
};
render() {
return (
<div>
<Webcam
audio={false}
ref={this.setRef}
/>
<br />
<button
onClick={this.stop}
>Stop</button>
</div>
);
}
}
Below is how I tried to incorporate the stop function from the WebcamCapture component, but I get the following error: Cannot read properties of undefined (reading 'srcObject').
const Camera = () => {
const webcamRef = React.useRef(null);
const [imgSrc, setImgSrc] = React.useState(null);
const stop = React.useCallback(() => { //This is how I tried using stop function
let stream = webcamRef.video.srcObject;
const tracks = stream.getTracks();
tracks.forEach(track => track.stop());
webcamRef.video.srcObject = null;
},[webcamRef, setImgSrc]);
const capture = React.useCallback(() => {
const imageSrc = webcamRef.current.getScreenshot();
setImgSrc(imageSrc);
stop() //I would like to call stop() here
}, [webcamRef, setImgSrc]);
return (
<>
<Webcam
audio={false}
ref={webcamRef}
screenshotFormat="image/jpeg"
/>
<button onClick={capture} >Capture photo</button>
{imgSrc && (
<img
src={imgSrc}
/>
)}
</>
)
}
export default Camera
How could I get the stop function from the WebcamCapture component? Any help would be appreciated!
First, there is no need to add Ref in dependency array.
Second, if you are using ref and using "current" for getScreenshot function then you should use current for video object as well.
const Camera = () => {
const webcamRef = React.useRef(null);
const [imgSrc, setImgSrc] = React.useState(null);
const stop = React.useCallback(() => {
let stream = webcamRef.current.video.srcObject;
const tracks = stream.getTracks();
tracks.forEach(track => track.stop());
webcamRef.current.video.srcObject = null;
},[setImgSrc]);
const capture = React.useCallback(() => {
const imageSrc = webcamRef.current.getScreenshot();
setImgSrc(imageSrc);
stop()
}, [webcamRef, setImgSrc]);
return (
<>
<Webcam
audio={false}
ref={webcamRef}
screenshotFormat="image/jpeg"
/>
<button onClick={capture} >Capture photo</button>
{imgSrc && (
<img
src={imgSrc}
/>
)}
</>
)
}
export default Camera
Related
This question already has answers here:
Typescript, how to pass "Object is possibly null" error?
(11 answers)
Closed last month.
I'm trying to learn Typescript and for that I came back on some components to convert them into an errorless Typescript file.
I have here a bunch of errors that can't find the cause.I trying different options, but can't figure it out.
The first one is on refs. When I use theme with ref.current, I receive the error
"may be null"
Here is the component code :
import React, { useState, useRef, useEffect } from "react";
const AudioPlayer = () => {
// state
const [isPlaying, setIsPLaying] = useState(false);
const [duration, setDuration] = useState(0);
const [currentTime, setCurrentTime] = useState(0);
const [currentTrack, setCurrentTrack] = useState(null);
// references
let audioPlayer = useRef(null); //reference to our audioplayer
let progressBar = useRef(); //reference to our progressBar
let animationRef = useRef(); //reference the animation
The ref audioPlayer is declared here and used below to get the duration of the track
useEffect(() => {
const seconds = Math.floor(audioPlayer.current.duration);
console.log(audioPlayer);
setDuration(seconds);
progressBar.current.max = seconds;
}, [
audioPlayer?.current?.onloadedmetadata,
audioPlayer?.current?.readyState,
]);
const togglePlayPause = () => {
const prevValue = isPlaying;
setIsPLaying(!prevValue);
if (!prevValue) {
audioPlayer.current.play();
animationRef.current = requestAnimationFrame(whilePlaying);
} else {
audioPlayer.current.pause();
cancelAnimationFrame(animationRef.current);
}
};
const whilePlaying = () => {
progressBar.current.value = audioPlayer.current.currentTime;
setCurrentTime(progressBar.current.value);
animationRef.current = requestAnimationFrame(whilePlaying);
};
const calculateTime = (secs) => {
const minutes = Math.floor(secs / 60);
const returnedMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
const seconds = Math.floor(secs % 60);
const returnedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`;
return `${returnedMinutes} : ${returnedSeconds}`;
};
const changeRange = () => {
audioPlayer.current.currentTime = progressBar.current.value;
setCurrentTime(progressBar.current.value);
};
const changeTrack = (e) => {
setCurrentTrack(e.target.value);
console.log(e.target.value);
togglePlayPause();
};
return (
<>
<div className="relative flex justify-center my-10 mx-4">
<img src="/sphere_3D.jpg" alt="sph" width="600" />
<p className="huit absolute">8</p>
<input
className="dots top-40"
value="/piste1.mp3"
onClick={(e) => changeTrack(e)}
></input>
<input
className="dots top-20 left-2/3"
value="/piste2.mp3"
onClick={(e) => changeTrack(e)}
></input>
</div>
<div>
<audio
ref={audioPlayer}
src={currentTrack}
preload="metadata"
onCanPlay={(e) => e.target.play()}
></audio>
<button className="mx-5" onClick={togglePlayPause}>
{isPlaying ? "Pause" : "Play"}
</button>
{/* Current time */}
<div>{calculateTime(currentTime)}</div>
{/* progress bar */}
<div>
<input
type="range"
defaultValue="0"
ref={progressBar}
onChange={changeRange}
/>
</div>
{/* duration */}
<div>{duration && !isNaN(duration) && calculateTime(duration)}</div>
</div>
</>
);
};
export default AudioPlayer;
Add a type to the ref
const audioElem = useRef<HTMLAudioElement>(null);
make sure you check for a null value before using current:
if (audioElem.current !== null) {
audioElem.current.focus();
}
I am using the react-webcam to capture images and videos in my react app. I have implemented the Screenshot (via Ref) example:
const videoConstraints = {
width: 1280,
height: 720,
facingMode: "user"
};
const WebcamCapture = () => {
const webcamRef = React.useRef(null);
const capture = React.useCallback(
() => {
const imageSrc = webcamRef.current.getScreenshot();
},
[webcamRef]
);
return (
<>
<Webcam
audio={false}
height={720}
ref={webcamRef}
screenshotFormat="image/jpeg"
width={1280}
videoConstraints={videoConstraints}
/>
<button onClick={capture}>Capture photo</button>
</>
);
};
Works nicely, however it seems to take a few seconds for the stream to start and video to start showing. I want to be able to disable the button whilst this is happening. I have found there is a flag in the webcamRef that states if it is loading:
useEffect(() => {
if (webcamRef.current) {
const camStarted = webcamRef.current.state.hasUserMedia;
debugger;
}
}, [webcamRef]);
In the above useEffect, whilst the video is initialising, the hasUserMedia is false, once it has loaded it changes to true. This sounds like exactly what I need however as it is in a useRef, it doesn't hit the useEffect when it changes.
Is there any kind of neat trick I can implement to be able to identify when this value in the useRef is changed, or anything else that might help me get the functionality I am after?
If you just want to disable the button until the camera has started streaming, you can use a check that can be triggered using the onUserMedia prop of Webcam.
const videoConstraints = {
width: 1280,
height: 720,
facingMode: "user"
};
const WebcamCapture = () => {
const [loadingCam, setLoadingCam] = useState(true);
const webcamRef = React.useRef(null);
const capture = React.useCallback(
() => {
const imageSrc = webcamRef.current.getScreenshot();
},
[webcamRef]
);
const handleUserMedia = () => {
setTimeout(() => {
// timer is optional if the loading is taking some time.
setLoadingCam(false);
}, 1000);
};
return (
<>
<Webcam
audio={false}
height={720}
ref={webcamRef}
screenshotFormat="image/jpeg"
width={1280}
videoConstraints={videoConstraints}
onUserMedia={handleUserMedia}
/>
<button disable={loadingCam} onClick={capture}>Capture photo</button>
</>
);
};
Edit
I fixed it by adding permissions during token generation:
permissions: ['allow_join', 'allow_mod'],
Reproducible demo here
Whenever I try to toggle the mic, webcam or screenshare system, I keep receiving the error:
TypeError: this._mediasoupDevice is null
I've looked everywhere online, I wasn't able to get a single relevant result.
Looking into the source code, the program attempts to to call canProduce on _mediasoupDevice, which obviously fails. I'm not sure why this is happening. Is something wrong with my code or is it something else?
Here's my whole relevant file (backend routes excluded, but you can see them in the above source code):
import * as React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { MeetingProvider, MeetingConsumer, useMeeting, useParticipant } from '#videosdk.live/react-sdk';
import { startLiveStream } from 'src/features/streaming/redux/actions';
import { IState } from 'src/features/core/redux/reducers';
// Helper function for participant loop.
const chunk = (arr) => {
const newArr = [];
while (arr.length) newArr.push(arr.splice(0, 3));
return newArr;
};
const ParticipantView = ({ participantId }) => {
const webcamRef = React.useRef(null);
const micRef = React.useRef(null);
const screenShareRef = React.useRef(null);
const { displayName, webcamStream, micStream, screenShareStream, webcamOn, micOn, screenShareOn } =
useParticipant(participantId);
React.useEffect(() => {
if (webcamRef.current) {
if (webcamOn) {
const mediaStream = new MediaStream();
mediaStream.addTrack(webcamStream.track);
webcamRef.current.srcObject = mediaStream;
webcamRef.current.play().catch((error) => console.error('videoElem.current.play() failed', error));
} else {
webcamRef.current.srcObject = null;
}
}
}, [webcamStream, webcamOn]);
React.useEffect(() => {
if (micRef.current) {
if (micOn) {
const mediaStream = new MediaStream();
mediaStream.addTrack(micStream.track);
micRef.current.srcObject = mediaStream;
micRef.current.play().catch((error) => console.error('videoElem.current.play() failed', error));
} else {
micRef.current.srcObject = null;
}
}
}, [micStream, micOn]);
React.useEffect(() => {
if (screenShareRef.current) {
if (screenShareOn) {
const mediaStream = new MediaStream();
mediaStream.addTrack(screenShareStream.track);
screenShareRef.current.srcObject = mediaStream;
screenShareRef.current.play().catch((error) => console.error('videoElem.current.play() failed', error));
} else {
screenShareRef.current.srcObject = null;
}
}
}, [screenShareStream, screenShareOn]);
return (
<div key={participantId}>
<audio ref={micRef} autoPlay />
{webcamRef || micOn ? (
<div>
<h2>{displayName}</h2>
<video height={'100%'} width={'100%'} ref={webcamRef} autoPlay />
</div>
) : null}
{screenShareOn ? (
<div>
<h2>Screen Shared</h2>
<video height={'100%'} width={'100%'} ref={screenShareRef} autoPlay />
</div>
) : null}
<br />
<span>
Mic:{micOn ? 'Yes' : 'No'}, Camera: {webcamOn ? 'Yes' : 'No'}, Screen Share:{' '}
{screenShareOn ? 'Yes' : 'No'}
</span>
</div>
);
};
const MeetingView = React.memo(() => {
const [joined, setJoined] = React.useState(false);
const { join, leave, toggleMic, toggleWebcam, toggleScreenShare, participants } = useMeeting();
if (!joined) {
return (
<button
onClick={() => {
join();
setJoined(true);
}}
>
join meeting
</button>
);
}
return (
<div>
<div>
<button onClick={leave}>Leave</button>
<button onClick={toggleMic}>toggleMic</button>
<button onClick={toggleWebcam}>toggleWebcam</button>
<button onClick={toggleScreenShare}>toggleScreenShare</button>
</div>
{chunk([...participants.keys()]).map((k) => (
<div key={k} style={{ display: 'flex' }}>
{k.map((l) => (
<ParticipantView key={l} participantId={l} />
))}
</div>
))}
</div>
);
});
const Landing = React.memo(() => {
const [loading, setLoading] = React.useState(false);
const meetingId = useSelector((state: IState) => state.app.streaming.streamConfig.meetingId);
const videoToken = useSelector((state: IState) => state.app.streaming.streamConfig.videoToken);
const dispatch = useDispatch();
const handleClick = React.useCallback(() => {
dispatch(startLiveStream({ payload: { onStart: () => setLoading(true), onEnd: () => setLoading(false) } }));
}, []);
if (!meetingId || !videoToken)
return (
<div>
<button onClick={handleClick}>Go Live</button>
{loading && <div>LOADING</div>}
</div>
);
return (
<MeetingProvider
config={{
meetingId,
name: '<Name-of-participant>',
// participantId: 'Id-of-participant', // optional, auto-generated
micEnabled: true,
webcamEnabled: true,
// maxResolution: '<Maximum-resolution>',
}}
token={videoToken}
>
<MeetingConsumer>{() => <MeetingView />}</MeetingConsumer>
</MeetingProvider>
);
});
export default Landing;
P.S. I'm aware my public and secret keys are exposed here -- new keys will be generated whenever I deploy.
Also, if it matters, I'm using Chrome, but the same issue transpires in Firefox.
This is my component of imageslider with button next and previous
I need som help how i can get individual img_src values and add them into another array and them use them in my image slider.
I welcome every solution corresponding to my aproach
const ImageSlider = () => {
const dispatch = useDispatch();
const ImageList = useSelector((state) => state.ImageList);
const { loading, error, Images } = ImageList;
useEffect(() => {
dispatch(ListImages());
}, [dispatch]);
var items = [Images.photos];
console.log(Images);
const classes = useStyles();
function Item(props) {
return (
<Paper>
{props.item.map(data => (
<img src={data.img_src} />
))}
{({ onClick, className, style, next, prev }) => {
return (
<Button onClick={onClick} className={classes.button} style={style}>
{next && "Next"}
{prev && "Previous"}
</Button>
);
}}
</Paper>
);
}
return (
<>
{loading ? (
<Loader />
) : error ? (
<h1>{error}</h1>
) : (
<Carousel>
{items.map((item, i) => (
<Item key={i} item={item} />
))}
</Carousel>
)}
</>
);
};
export default ImageSlider;
```
First of all you should move the Item component out of the ImageSlider. It is being redefined every render. You can use localState to keep track of the index.
const useImageIndexer = (maxIndex) => {
const [index, setIndex] = useState(0);
const nextImage = () => {
setIndex((current) => Math.min(maxIndex, current + 1));
};
const prevImage = () => {
setIndex((current) => Math.max(0, current - 1));
};
return [index, nextImage, prevImage];
}
Then to use inside the slider
const ImageSlider = () => {
const dispatch = useDispatch();
const ImageList = useSelector((state) => state.ImageList);
const photos = ImageList.Images.photos;
const [index, nextImage, prevImage] = useImageIndexer(photos.length);
const currentPhoto = photos[index];
// Further down in the code
if(loading) {
return (<Loader />);
}
if (error) {
return (<div>Oh no!</div>);
}
return (<div>
<img src={img.src} />
<button onClick={prevImage}>Previous</button>
<button onClick={nextImage}>Next</button>
</div>);
It seemed like you were wrapping photos which sounds like an array inside another array, that doesn't look right.
var items = [Images.photos];
how to set intersection observer correctly.
When I ask for img it only works on the last element, how to set for each element? So that they load one after another in turn
function useOnScreen(options:any){
const ref:any = React.useRef()
const[visible, setVisible] = React.useState(false)
useEffect(()=>{
const observer = new IntersectionObserver(([entry])=>{
setVisible(entry.isIntersecting)
}, options)
if(ref.current){
observer.observe(ref.current)
}
return ()=>{
if (ref.current){
observer.unobserve(ref.current)
}
}
}, [ref, options])
return [ref, visible]
}
const [ref, visible] = useOnScreen({threshold: '0.68'})
console.log(visible,ref)
const data:any = state.data.map((item:any) => {
return (<SectionHome key={item.id}>
<picture >
<img src={item.pictures} alt={item.show_name} key={item.pictures}/>
</picture>
<a href={item.show_name} key={item.show_name}><p key={item.id}>{item.show_name}</p></a>
</SectionHome>)
})
const data2:any = state.data.map((item:any) => {
return (
<div>
<a href={item.show_name} key={item.show_name}>
<picture ref={ref}>
{visible ? <img src={item.pictures} alt={item.show_name} key={item.pictures}/> : <section></section>}
</picture>
<p key={item.id}>{item.show_name}</p></a>
</div> )
})
You can create a new React Component to hold the intersection observer logic for each picture element.
// This function hook is the same.
function useOnScreen(options: any) {
const ref: any = React.useRef()
const [visible, setVisible] = React.useState(false)
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
setVisible(entry.isIntersecting)
}, options)
if (ref.current) {
observer.observe(ref.current)
}
return () => {
if (ref.current) {
observer.unobserve(ref.current)
}
}
}, [ref, options])
return [ref, visible]
}
// This component holds the intersection observer logic and forwards all the props to the picture element.
// It accepts a function as children and the function is given whether it is visible and should return the content to render inside the picture element.
function SmartPicture(props: any) {
const { children, ...pictureProps } = props
const [ref, visible] = useOnScreen({ threshold: '0.68' })
return (
<picture {...pictureProps} ref={ref}>
{children(visible)}
</picture>
)
}
// In your render function
const data: any = state.data.map((item: any) => {
return (
<SectionHome key={item.id}>
<picture >
<img src={item.pictures} alt={item.show_name} key={item.pictures} />
</picture>
<a href={item.show_name} key={item.show_name}><p key={item.id}>{item.show_name}</p></a>
</SectionHome>
)
})
const data2: any = state.data.map((item: any) => {
return (
<div>
<a href={item.show_name} key={item.show_name}>
<SmartPicture>
{(visible) => visible ? <img src={item.pictures} alt={item.show_name} key={item.pictures} /> : <section></section>}
</SmartPicture>
<p key={item.id}>{item.show_name}</p></a>
</div>
)
})