Java无插件集成海康摄像头并在浏览器展示

需求背景:

最近公司项目有了一个需求,接入摄像头到系统中并进行实时监控。要进行接入的摄像头是海康品牌的,首先我在官网上进行了一系列的查询,发现官网上提供的有web开发插件和以及提供了海康开发的SDK包,在一番尝试后,发现这些并不能使用于银河麒麟V4电脑系统,故又在网上进行了一番探索,最终选择了ffmpeg对摄像头进行拉流,然后推送到配置了rtmp模块的nginx上,然后通过flv.js进行拉取直播流。由于本人技术有限,目前是可以实现在网页上进行访问,故在此记录一下。

当有了这个想法的时候,在海康官网了解到了有个api开发接口,不过这个海康官方要求必须签订保密合同才可以获取相关API文件,由于我在公司的职位有限,不能得到公司的相关允许,故联系海康开发者邮箱,感谢海康开发者提供的通过rtsp访问摄像头的API。

举例说明:

通道01主码流:

rtsp://admin:abc12345@172.6.22.234:554/Streaming/Channels/101?transportmode=unicast

通道01子码流:

rtsp://admin:abc12345@172.6.22.234:554/Streaming/Channels/102?transportmode=unicast(单播)

rtsp://admin:abc12345@172.6.22.106:554/Streaming/Channels/102?transportmode=multicast (多播)

rtsp://admin:abc12345@172.6.22.106:554/Streaming/Channels/102 (?后面可省略,默认单播)

通道01第3码流:

rtsp://admin:abc12345@172.6.22.234:554/Streaming/Channels/103?transportmode=unicast

零通道主码流(零通道无子码流):

rtsp://admin:12345@172.6.22.106:554/Streaming/Channels/001

注:新版本URL,通道号全部按顺序从1开始。

以上是官方提供的模板案例。

实际操作:

这套操作实现的逻辑是:在点进直播显示页面时,首先前端对后台发送一个请求,传入摄像头的信息(包含用户名、密码、ip等一系列信息),接着在后端调用新的线程启动ffmpeg拉取摄像头的流并转换为flv格式的视频发送到Nginx服务器上,接着在前创建flv.js访问Nginx上的流媒体资源在页面进行显示。

注意!!! 首先要将nginx跑起来,我这边选择了对nginx配置成了开机自启。也可以选择将nginx放到项目中,在调用直播页面时通过命令跑起来。

后端核心代码:

@RequestMapping("/dev/getRTMPUrlAndStartTranscode") 复制代码
public String getRTMPUrlAndStartTranscode(Device device){
	ReturnDataForJSON returnJson=new ReturnDataForJSON();
	long startTime=new Date().getTime();
	Thread thread = ThreadUtill.getThreadByName("video" + (Integer.parseInt(device.getPort()) - 1));
	if(thread != null) {
		System.out.println("找到该线程,Name:" + thread.getName());
		thread.stop();
	}else {
		System.out.println("未找到该线程!");
	}
    try {
        String ffmpegPath = "D:\\enviorment\\ffmpeg-master-latest-win64-gpl\\bin\\ffmpeg";
		String rtspUrl = "rtsp://" + device.getAccount() + ":" + device.getPassword() + "@" + device.getIp() + ":554/Streaming/Channels/102";
	    String rtmpUrl = "rtmp://127.0.0.1:1935/myapp/mystream" + device.getPort();
 
	    // 构建 FFmpeg 命令行
		List<String> command = new ArrayList<>();
		command.add(ffmpegPath);
		command.add("-rtsp_transport");
		command.add("tcp");
		command.add("-i");
		command.add(rtspUrl);
		command.add("-c:a");
		command.add("aac");
		command.add("-ar");
		command.add("22050");
		command.add("-c:v");
		command.add("copy");
		command.add("-preset");
		command.add("veryfast");
		command.add("-f");
		command.add("flv");
		command.add(rtmpUrl);
    
		HaiKangService haiKangService = new HaiKangService(command,"video" + device.getPort());
		haiKangService.start();
        returnJson=CommonUtil.returnSuccess();
			
	} catch (MyException e) {
        returnJson=CommonUtil.returnFail(e.getErrorCode(),e.getMessage());
	}
}
 
 
 
public HaiKangService(List<String> command,String name) {
		super(name);
		this.command = command;
		this.name = name;
	}
	
	@Override
	public void run() {
		ProcessBuilder builder = new ProcessBuilder(command);
	    builder.redirectErrorStream(true);  // 将标准错误输出与标准输出合并
	    // 启动进程
	    Process process;
		try {
			process = builder.start();
			// 获取进程的输入流(用于读取输出信息)
		    InputStream inputStream = process.getInputStream();
		    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
 
		    // 读取输出信息
		    String line;
		    while ((line = reader.readLine()) != null) {
		        //System.out.println(line);
		    }
		    
		    // 不再打印监控状态,即ffmpeg异常终止,停止线程。
		    //Thread.currentThread().interrupt();
		
		} catch (IOException e) {
			e.printStackTrace();
		}
 
	}
 

前端核心代码:

复制代码
    <canvas id="h5_canvas_1" style="width:100%;height:100%;"></canvas>
    <video id="h5_video_1" style="width:100%;height:100%"></video>
</div>
 
function initMovie() {
        	var videoElement = document.getElementById('h5_video_1');
        	var flvPlayer = flvjs.createPlayer({
        	    type: 'flv',
        	    isLive: true,
        	    hasAudio: false,
        	    hasVideo: true,
        	    enableWorker: true,
        		enableStashBuffer: true,
        	    stashInitialSize: 128,
        	    url: 'http://127.0.0.1:86/live?port=1935&app=myapp&stream=mystream' + videoIndex
        	});
        	flvPlayer.attachMediaElement(videoElement);
        	flvPlayer.load();
        	flvPlayer.play();
        	videoElement.addEventListener("progress", () => {
       		 let end = flvPlayer.buffered.end(0); //获取当前buffered值(缓冲区末尾)
       		 let delta = end - flvPlayer.currentTime; //获取buffered与当前播放位置的差值
       		 console.info("delta:",delta);
       		 // 延迟过大,通过跳帧的方式更新视频
       		 if (delta > 10 || delta < 0) {
       		 this.flvPlayer.currentTime = this.flvPlayer.buffered.end(0) - 1;
       		 return;
       		 }
       		 // 追帧
       		 if (delta > 1) {
       		 videoElement.playbackRate = 1.1;
       		 } else {
       		 videoElement.playbackRate = 1;
       		 }
       		});
        }

麒麟电脑下配置nginx:

配置nginx

第一步: 将openssl、pcre、zlib、nginx-http-flv-module-master和nginx放到同一目录下。并在nginx目录中执行如下指令(确保电脑存在gcc、g++环境)

下方指令中 将user改为电脑的用户,否则执行时会产生报错不存在用户, --prefix指令指定的是输入指令时的前缀,目前具体不了解。

./configure --with-openssl=../openssl-1.0.2s --with-pcre=../pcre-8.45 --with-zlib=../zlib-1.3 --with-pcre-jit --user=nginx --prefix=/home/nginx --with-http_ssl_module --with-http_v2_module --add-module=../nginx-http-flv-module-master

第二步: 执行 make & makeinstall 指令,以管理员身份运行,在执行install过程会创建文件夹,未以管理员身份会报错。

第三步: 进入到/home/nginx/conf/下,修改nginx.conf文件,将其中内容替换为下方内容

worker_processes 1; #运行在 Windows 上时,设置为 1,因为 Windows 不支持 Unix domain socket #worker_processes auto; #1.3.8 和 1.2.5 以及之后的版本

#worker_cpu_affinity 0001 0010 0100 1000; #只能用于 FreeBSD 和 Linux #worker_cpu_affinity auto; #1.9.10 以及之后的版本

error_log logs/error.log error;

#如果此模块被编译为动态模块并且要使用与 RTMP 相关的功 #能时,必须指定下面的配置项并且它必须位于 events 配置 #项之前,否则 NGINX 启动时不会加载此模块或者加载失败

#load_module modules/ngx_http_flv_live_module.so;

events { worker_connections 4096; }

http { include mime.types; default_type application/octet-stream;

bash 复制代码
keepalive_timeout  65;

server {
    listen       86;

    location / {
        root   /var/www;
        index  index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }

    location /live {
        flv_live on; #打开 HTTP 播放 FLV 直播流功能
        chunked_transfer_encoding on; #支持 'Transfer-Encoding: chunked' 方式回复

        add_header 'Access-Control-Allow-Origin' '*'; #添加额外的 HTTP 头
        add_header 'Access-Control-Allow-Credentials' 'true'; #添加额外的 HTTP 头
    }

    location /hls {
        types {
            application/vnd.apple.mpegurl m3u8;
            video/mp2t ts;
        }

        root /tmp;
        add_header 'Cache-Control' 'no-cache';
    }

    location /dash {
        root /tmp;
        add_header 'Cache-Control' 'no-cache';
    }

    location /stat {
        #推流播放和录制统计数据的配置

        rtmp_stat all;
        rtmp_stat_stylesheet stat.xsl;
    }

    location /stat.xsl {
        root /var/www/rtmp; #指定 stat.xsl 的位置
    }

    #如果需要 JSON 风格的 stat, 不用指定 stat.xsl
    #但是需要指定一个新的配置项 rtmp_stat_format

    #location /stat {
    #    rtmp_stat all;
    #    rtmp_stat_format json;
    #}

    location /control {
        rtmp_control all; #rtmp 控制模块的配置
    }
}

}

rtmp_auto_push on; rtmp_auto_push_reconnect 1s; rtmp_socket_dir /tmp;

rtmp { out_queue 4096; out_cork 8; max_streams 128; timeout 15s; drop_idle_publisher 15s;

csharp 复制代码
log_interval 5s; #log 模块在 access.log 中记录日志的间隔时间,对调试非常有用
log_size     1m; #log 模块用来记录日志的缓冲区大小

server {
    listen 1935;
    server_name www.test.*; #用于虚拟主机名后缀通配

    application myapp {
        live on;
        gop_cache on; #打开 GOP 缓存,减少首屏等待时间
    }

}

server {
    listen 1935;
    server_name *.test.com; #用于虚拟主机名前缀通配

    application myapp {
        live on;
        gop_cache on; #打开 GOP 缓存,减少首屏等待时间
    }
}

server {
    listen 1935;
    server_name www.test.com; #用于虚拟主机名完全匹配

    application myapp {
        live on;
        gop_cache on; #打开 GOP 缓存,减少首屏等待时间
    }
}

}

相关推荐
摇滚侠1 天前
Spring Boot 3零基础教程,IOC容器中组件的注册,笔记08
spring boot·笔记·后端
程序员小凯1 天前
Spring Boot测试框架详解
java·spring boot·后端
你的人类朋友1 天前
什么是断言?
前端·后端·安全
程序员小凯1 天前
Spring Boot缓存机制详解
spring boot·后端·缓存
i学长的猫1 天前
Ruby on Rails 从0 开始入门到进阶到高级 - 10分钟速通版
后端·ruby on rails·ruby
用户21411832636021 天前
别再为 Claude 付费!Codex + 免费模型 + cc-switch,多场景 AI 编程全搞定
后端
茯苓gao1 天前
Django网站开发记录(一)配置Mniconda,Python虚拟环境,配置Django
后端·python·django
Cherry Zack1 天前
Django视图进阶:快捷函数、装饰器与请求响应
后端·python·django
爱读源码的大都督1 天前
为什么有了HTTP,还需要gPRC?
java·后端·架构
码事漫谈1 天前
致软件新手的第一个项目指南:阶段、文档与破局之道
后端