Java+ffmpeg+rstp实现视频监控截图功能

75 阅读2分钟

1.开发准备

Maven依赖

  <properties>
        <java.version>1.8</java.version>
        <mybatisplus.version>3.2.0</mybatisplus.version>
        <druid.version>1.1.20</druid.version>
        <shiro.version>1.4.0</shiro.version>
        <log4j.version>1.2.17</log4j.version>
        <hutool.version>4.6.8</hutool.version>
        <pinyin4j.version>2.5.1</pinyin4j.version>
        <fastjson.version>1.2.62</fastjson.version>
        <javacv.version>1.5.3</javacv.version>
        <javacv.windows-x86_64>windows-x86_64</javacv.windows-x86_64>
    </properties>
 <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv</artifactId>
            <version>${javacv.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>flycapture</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>libdc1394</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>libfreenect</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>libfreenect2</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>librealsense</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>librealsense2</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>videoinput</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>artoolkitplus</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>flandmark</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>leptonica</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.bytedeco</groupId>
                    <artifactId>tesseract</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg</artifactId>
            <version>4.2.2-${javacv.version}</version>
            <classifier>${javacv.windows-x86_64}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacpp</artifactId>
            <version>${javacv.version}</version>
            <classifier>${javacv.windows-x86_64}</classifier>
        </dependency>

        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>openblas</artifactId>
            <version>0.3.9-${javacv.version}</version>
            <classifier>${javacv.windows-x86_64}</classifier>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>opencv</artifactId>
            <version>4.3.0-${javacv.version}</version>
            <classifier>${javacv.windows-x86_64}</classifier>
        </dependency>

2.定时任务

从数据库中查询视频监控参数 设置文件路径和截图文件名称 开始通过ffmpeg推流,截取其中一帧保存

    /***
     * 定时获取video数据
     */
    @Scheduled(fixedRate = 10800000)//一小时执行一次    
    @Async
    public void getVideoPic() {
        log.info("定时任务开始:获取VIDEO数据");
        Date now=new Date();
        try {
            //获取可用VIDEO
            List<VideoExtvideoList = videoService.getStationNameByDeptId(19, null);
            if (videoList.size() > 0) {
                //遍历VIDEO
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                String dayStr = sdf.format(now);
                int hour = now.getHours();
                int minute = now.getMinutes();

                for (VideoExt video : videoList) {
                    String ip = video.getCameraIp();
                    if (StringUtils.isNotBlank(ip)){
                        //存储图片目录路径
                        String path = "D:\download\" + video.getLineName() + "\" + video.getStationName() + "\" + dayStr + "\";
                        File file=new File(path);
                        if (!file.exists()) {
                            file.mkdirs();
                        }
                        //视频配置
                        VideoConfig config = JSONObject.parseObject(video.getConfig(), VideoConfig.class);
                        //绝对路径
                       String targetName =path+ip + "-1-" + hour+minute + ".jpg";
                       //推流rtsp命令
                        String url=VideoUtil.replaceRtspUrl(video.getCameraType(),video.getCameraMode(),video.getCameraUserName(),video.getCameraPassword(),
                       video.getCameraIp(),String.valueOf(config.getPic1()+1));
                        //开始推流和截图
                        VideoUtil.getVideoImagePathByRSTP(url,targetName);


                    }
                }
            }
        } catch (Exception e) {

           log.error(":获取VIDEO数据错误"+e.getMessage());

        }
        log.info("定时任务结束:获取video数据");
    }

推流url

private static String HK_NVR_RTSP_URL="rtsp://{username}:{password}@{ip}:554/Streaming/Channels/{channel}01";
    private static String HK_CAMERA_RTSP_URL="rtsp://{username}:{password}@{ip}:554/Streaming/Channels/{channel}01";
    private static String DH_RTSP_URL="rtsp://{username}:{password}@{ip}:554/cam/realmonitor?channel={channel}&subtype=0";

推流url替换参数

public static String replaceRtspUrl(String cj,String mode,String userName,String password,String ip,String channel){
        if(cj.equals("海康")){
            if(mode.equals("NVR")){
                return HK_NVR_RTSP_URL.replace("{username}",userName)
                        .replace("{password}",password)
                        .replace("{ip}",ip).replace("{channel}",channel);
            }else {
                return HK_CAMERA_RTSP_URL.replace("{username}",userName)
                        .replace("{password}",password)
                        .replace("{ip}",ip).replace("{channel}",channel);
            }

        }else if(cj.equals("大华")){
            return DH_RTSP_URL.replace("{username}",userName)
                    .replace("{password}",password)
                    .replace("{ip}",ip).replace("{channel}",channel);
        }
        return "";
}

核心方法

FFmpegFrameGrabber rstp流对象 原理

 /**
     * 解析视频地址并截图
     * @param path rstp 流地址
     * @param picPath 图片存放地址
     * @throws Exception
     */
    public static String getVideoImagePathByRSTP(String path, String picPath) throws Exception {
        log.info("开始拉流"+path+picPath);
        System.setProperty("org.bytedeco.javacpp.maxphysicalbytes""0");
        System.setProperty("org.bytedeco.javacpp.maxbytes""0");
        String ret=picPath;
        //创建rstp流对象
        FFmpegFrameGrabber grabber = createGrabber(path);
        try {
            //开启流获取
            grabber.startUnsafe();
            //由于视频第一帧的流可能为黑屏 为了确保实时能截取到准确图像
            // 故此做了个for循环用于覆盖生成图片
            for (int i=0;i<count;i++){
                //获取流视频框内的图像
                Frame frame = grabber.grabFrame();
                //转换图像
                Java2DFrameConverter converter = new Java2DFrameConverter();
                BufferedImage srcImage = converter.getBufferedImage(frame);
                //自定义画图
            if (srcImage!=null) {
                    //创建文件
                    File file = new File(picPath);
                    //输出文件
                    ImageIO.write(srcImage, "jpg", file);
                }
            }
            log.info("结束拉流");
            return ret.replaceAll("\\","//");
        }catch (Exception e){
            ret="异常";
           log.error(e.getMessage());
        }finally {
            if(grabber!=null) {
                grabber.stop();
                grabber.close();
                grabber.release();
            }
        }
     return ret;

    }

原文链接:blog.csdn.net/m0_38110240…