我们正在使用 html img 标签显示 mjpeg 视频流。为此,我们有多种实现,具体取决于流的提供者。
流媒体视频工作正常。切换流很好。但是切换到不同类型的流并不好。此时当前流没有关闭,第二个流被打开。在几次这样的切换之后,无法再发出 http 请求并且浏览器选项卡冻结。
我可以用这段代码重现它:
interface StreamProps {
mjpegUrl: string;
}
export const StreamType1: React.FC<StreamProps> = ({ mjpegUrl }) => {
return <img src={mjpegUrl} />;
};
export const StreamType2: React.FC<StreamProps> = ({ mjpegUrl }) => {
return <img src={mjpegUrl} />;
};
interface Props {
}
export const VideoStream: React.FC<Props> = () => {
const [toggle, setToggle] = useState(true);
return (
<div>
<button onClick={() => setToggle((current) => !current)}>
Switch!
</button>
{toggle
? <StreamType1 mjpegUrl="http://localhost/stream1" />
: <StreamType2 mjpegUrl="http://localhost/stream2" />}
</div>
);
};
需要注意的一件事是,如果我对两者使用相同的流类型,则不会发生此问题。类似于更新
mjpegUrl
属性。
{toggle
? <StreamType1 mjpegUrl="http://localhost/stream1" />
: <StreamType1 mjpegUrl="http://localhost/stream2" />}
为什么会发生这种情况,如何解决?
window.stop() 将结束所有当前流。 将它放在 useEffect 钩子中以在离开页面时停止流。
useEffect(() => {
return () => {
// This stops the stream
window.stop()
}
}, [])
我想我明白了。
更新
src
的 img
属性将停止当前请求并发送新请求。这就是为什么更新同一个组件没有问题。
当使用相同类型的不同组件时,React 足够智能以重用现有组件。这意味着
src
属性已更新并且请求已停止。
当使用不同类型的不同组件时,React 无法重用该组件。因此
src
的 img
属性不会更新,当前请求不会停止。
要解决此问题,可以使用两步更新。首先更新现有组件并设置一个空字符串。然后更新到新的组件和值。困难在于当你有一个很深的组件树并且其中一个被替换或删除时你仍然有一个开放的连接。
最后我的解决方案基于https://stackoverflow.com/a/52328525/9271844.
const [frame, setFrame] = useState("");
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then(response => {
// Read stream and store images in 'setFrame' using URL.createObjectURL.
// Don't forget to call URL.revokeObjectURL when image is no longer needed.
});
return () => controller.abort();
});
return <img src={frame} />
edit:这确实解决了问题,但事实证明这样做会影响性能。决定使用 https://github.com/ryanseddon/react-frame-component 将 img 放入 iframe 中,这解决了这两个问题。