调用摄像机播放画面,并且实现录制GIF动图预览和下载

简介: 调用摄像机播放画面,并且实现录制GIF动图预览和下载

在线运行可以看这里

最近遇到个开发需求:在显示器显示监控摄像头的实时画面,要求可以录制GIF动图,然后现在它,这里只是个基本的先行demo

准备工作

本文主要是借助navigator.mediaDevices对象下的getUserMedia方法实现的,他会返回MediaStream数据流,将它接入video标签就可以展示实时监控的画面了。

不过如果你需要授予浏览器相机权限,尤其windows,仅仅在这个标签页授予权限是没有用的

定义MediaStream函数

import { useState, useEffect } from "react";
export const useMediaStream = (options: MediaStreamConstraints) => {
  const [mediaStream, setMediaStream] = useState<MediaStream>();
  const [error, setError] = useState<any>();
  useEffect(() => {
    navigator.mediaDevices
      .getUserMedia(
        options ?? {
          audio: false,
          video: { width: 600, height: 300 }
        }
      )
      .then(function (mediaStream) {
        setMediaStream(mediaStream);
      })
      .catch(function (err) {
        setError(err);
      });
  }, []);
  return {
    mediaStream,
    error
  };
};

接入Video

我试过多种写法,这种是最稳妥的,如果你的video标签一直闪烁,那可能是一直被初始化导致的,这样写就可以避免这种问题。

export const App = () => {
  const [dom, setDom] = useState<HTMLVideoElement>();
  const { mediaStream } = useMediaStream({
    audio: false,
    video: {
      width: 1920,
      height: 1080
    }
  });
  // 初始化播放器
  useEffect(() => {
    if (!dom) return;
    dom.srcObject = mediaStream;
    dom.onloadeddata = function () {
      dom.play();
    };
  }, [mediaStream, dom]);
  // 初始化dom
  useEffect(() => {
    if (document.querySelector("video")) {
      setDom(document.querySelector("video"));
    }
  }, []);
  return <video width={400} height={300} />
}

实现播放和暂停

这是HTMLVideoElement的方法,直接调用就好了

return (
    <>
      <video poster={posterUrl} width={400} height={300} />
      <br />
      <button onClick={() => dom.pause()}>暂停</button>
      <button onClick={() => dom.play()}>播放</button>
     </>
)

此时如下

image.png

实现截图和预览截图功能

截图的功能是通过获取当前帧的数据,然后创建img,生成base64 URL。接着,展示它就可以预览了

  const [cuttImgRUL, setCurrentRUL] = useState<string>(posterUrl);
  // 获取当前帧画面的dataURL
  const getCurrenScreen = () => {
    const canvas = document.createElement("canvas");
    canvas.width = dom.videoWidth;
    canvas.height = dom.videoHeight;
    const ctx = canvas.getContext("2d");
    ctx.drawImage(dom, 0, 0, canvas.width, canvas.height);
    const dataURL = canvas.toDataURL("image/png");
    return dataURL;
  };
  // 设置当前帧
  const getPic = () => {
    setCurrentRUL(getCurrenScreen());
    return getCurrenScreen();
  };
  // 展示截图
  const showPic = () => {
    const dom = document.querySelector("img");
    if (!dom) return;
    dom.src = cuttImgRUL as string;
  };
  // 每当当前帧的地址发生变化就展示它
  useEffect(() => showPic(), [cuttImgRUL]);
  <button onClick={() => downPic()}>下载截图</button>
  <img width="500px" alt="img" />

实现下载截图

基础的下载操作,就不多说了。

  // 下载截图
  const downPic = () => {
    const a = document.createElement("a");
    a.href = getCurrenScreen();
    a.download = "img";
    a.click();
    a.remove();
  };
  <button onClick={() => downPic()}>下载截图</button>

下载如下

image.png

录制GIF

每过一段时间获取当前帧的数据,并创建成img对象,将它们的base64 URL缓存起来。

  / 录制标记
  const [flag, setFlag] = useState<boolean>(false);
  // GIF的缓存
  const [imgs, setImgs] = useState([]);
  // 录制GIF
  const getGif = () => {
    setFlag(true);
    setImgs([]);
    const id = setInterval(() => {
      const img = getPic();
      setImgs((curr) => [...curr, img]);
    }, 16.67);
    setTimeout(() => {
      setFlag(false);
      clearInterval(id);
    }, 5000);
  };
  <button onClick={() => getGif()}>{flag ? "录制中..." : "录制GIF"}</button>

预览GIF

将缓存的图片快照在图片容器中依次播放,所以这里是个障眼法

   // 预览GIF
  const preViewGif = () => {
    const newImage = document.querySelector("img");
    let idx = 0;
    const id = setInterval(() => {
      newImage.src = imgs[idx];
      idx++;
      if (idx === imgs.length - 1) clearInterval(id);
    }, 200);
  };
  <button onClick={() => preViewGif()}>预览GIF</button>

预览如下

image.png

下载GIF

下载GIF的原理是将多个png的图片放入Canvas里,然后在合并成Gif文件并下载,这里用到了工具库gifshot

  // 下载GIF
  const downGif = () => {
    const config = {
      fps: 10,
      width: 1920,
      height: 1080,
      images: imgs
    };
    gifshot.createGIF(config, function (obj) {
      if (!obj.error) {
        const url = obj.image;
        // 下载GIF
        const a = document.createElement("a");
        a.href = url;
        a.download = "img";
        a.click();
        a.remove();
      }
    });
  };
  <button onClick={() => downGif()}>下载GIF</button>

下面是下载的其中一个GIF

image.png

好了,这就是个简单的demo,比较简陋,希望能给大家一点思路。最后附上完整的代码

完整代码如下

import { useEffect, useState } from "react";
import { useMediaStream } from "./useMediaStream";
import gifshot from "gifshot";
const posterUrl =
  "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ebdfb2583e3f4a33a72bc6ffd44b42f2~tplv-k3u1fbpfcp-zoom-crop-mark:1512:1512:1512:851.awebp?";
export default () => {
  const [dom, setDom] = useState<HTMLVideoElement>();
  // 录制标记
  const [flag, setFlag] = useState<boolean>(false);
  const [cuttImgRUL, setCurrentRUL] = useState<string>(posterUrl);
  // GIF的缓存
  const [imgs, setImgs] = useState([]);
  // 获取数据流
  const { mediaStream } = useMediaStream({
    audio: false,
    video: {
      width: 1920,
      height: 1080
    }
  });
  // 获取当前帧画面的dataURL
  const getCurrenScreen = () => {
    const canvas = document.createElement("canvas");
    canvas.width = dom.videoWidth;
    canvas.height = dom.videoHeight;
    const ctx = canvas.getContext("2d");
    ctx.drawImage(dom, 0, 0, canvas.width, canvas.height);
    const dataURL = canvas.toDataURL("image/png");
    return dataURL;
  };
  // 设置当前帧
  const getPic = () => {
    setCurrentRUL(getCurrenScreen());
    return getCurrenScreen();
  };
  // 下载截图
  const downPic = () => {
    const a = document.createElement("a");
    a.href = getCurrenScreen();
    a.download = "img";
    a.click();
    a.remove();
  };
  // 下载GIF
  const downGif = () => {
    const config = {
      fps: 10,
      width: 1920,
      height: 1080,
      images: imgs
    };
    gifshot.createGIF(config, function (obj) {
      if (!obj.error) {
        const url = obj.image;
        // 下载GIF
        const a = document.createElement("a");
        a.href = url;
        a.download = "img";
        a.click();
        a.remove();
      }
    });
  };
  // 展示截图
  const showPic = () => {
    const dom = document.querySelector("img");
    if (!dom) return;
    dom.src = cuttImgRUL as string;
  };
  // 录制GIF
  const getGif = () => {
    setFlag(true);
    setImgs([]);
    const id = setInterval(() => {
      const img = getPic();
      setImgs((curr) => [...curr, img]);
    }, 16.67);
    setTimeout(() => {
      setFlag(false);
      clearInterval(id);
    }, 5000);
  };
  // 预览GIF
  const preViewGif = () => {
    const newImage = document.querySelector("img");
    let idx = 0;
    const id = setInterval(() => {
      newImage.src = imgs[idx];
      idx++;
      if (idx === imgs.length - 1) clearInterval(id);
    }, 200);
  };
  // 初始化播放器
  useEffect(() => {
    if (!dom) return;
    dom.srcObject = mediaStream;
    dom.onloadeddata = function () {
      dom.play();
    };
  }, [mediaStream, dom]);
  // 初始化dom
  useEffect(() => {
    if (document.querySelector("video")) {
      setDom(document.querySelector("video"));
    }
  }, []);
  // 每当当前帧的地址发生变化就展示它
  useEffect(() => showPic(), [cuttImgRUL]);
  return (
    <>
      <video poster={posterUrl} width={400} height={300} />
      <br />
      <button onClick={() => dom.pause()}>暂停</button>
      <button onClick={() => dom.play()}>播放</button>
      <button onClick={() => getPic()}>截屏</button>
      <button onClick={() => downPic()}>下载截图</button>
      <button onClick={() => getGif()}>{flag ? "录制中..." : "录制GIF"}</button>
      {imgs.length && !flag ? (
        <>
          <button onClick={() => preViewGif()}>预览GIF</button>
          <button onClick={() => downGif()}>下载GIF</button>
        </>
      ) : (
        ""
      )}
      <br />
      <br />
      <img width="500px" alt="img" />
      <canvas width="500px" height="300px" />
    </>
  );
};
相关文章
|
2月前
|
计算机视觉 Python
OpenCV实现视频的暂停播放和继续播放功能实战(附Python源码)
OpenCV实现视频的暂停播放和继续播放功能实战(附Python源码)
173 0
|
Web App开发 移动开发 前端开发
移动端图片操作(二)——预览、旋转、合成
在上一节中已经提到了预览,预览可以通过data: URL格式或URL对象。
移动端图片操作(二)——预览、旋转、合成
|
计算机视觉
【方便的Opencv】实现播放有声音的视频+附带图片生成gif
【方便的Opencv】实现播放有声音的视频+附带图片生成gif
【方便的Opencv】实现播放有声音的视频+附带图片生成gif
|
10月前
|
Ubuntu Linux 人机交互
快速实现摄像头视频画面的远程预览
通过阿里云生活物联网平台的智能视频服务Link Visual来快速的搭建并实现摄像头视频画面的远程预览功能。
190 0
uniapp上传预览大图-带删除按钮-摄像机-相册
uniapp上传预览大图-带删除按钮-摄像机-相册
|
安全
【实用工具】如何录制电脑屏幕gif动图?
【实用工具】如何录制电脑屏幕gif动图?
224 0
【实用工具】如何录制电脑屏幕gif动图?
|
Android开发
打开相机,相册,裁剪图片
打开相机,相册,裁剪图片
|
编解码 算法 计算机视觉
案例分享:Qt内窥镜相机录像程序(打开摄像头、支持多种摄像头、分辨率调整、翻转、旋转、亮度调整、拍照、录像、回放图片、回放录像)
案例分享:Qt内窥镜相机录像程序(打开摄像头、支持多种摄像头、分辨率调整、翻转、旋转、亮度调整、拍照、录像、回放图片、回放录像)
案例分享:Qt内窥镜相机录像程序(打开摄像头、支持多种摄像头、分辨率调整、翻转、旋转、亮度调整、拍照、录像、回放图片、回放录像)
|
移动开发 JavaScript
h5 实现调用系统拍照或者选择照片并预览
调用手机相机拍照或者是调用手机相册选择照片,这个功能在 手机端页面 或者 webApp 应该是常用到的,就拿个人或会员资料录入那块来说就已经是经常会碰到的, 每当看到这块功能的时候,前端的小伙伴就得去找各种各样的插件。
2952 0