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 缓存,减少首屏等待时间
    }
}

}

相关推荐
yyycqupt32 分钟前
多路转接之poll
服务器·c++·后端·网络协议
泰山小张只吃荷园2 小时前
通过SpringTask模拟打印机定时向数据库传入模拟数据
java·开发语言·后端·spring·mybatis
小技与小术2 小时前
go环境搭建
开发语言·后端·golang
伍肆伍柒2 小时前
SpringBoot 2.2.10 无法执行Test单元测试
spring boot·后端·单元测试
hummhumm2 小时前
第 14 章 -Go语言 错误处理
java·开发语言·前端·后端·python·sql·golang
Yanbin_Q3 小时前
从 Rust 官方文档理解 Ownership
开发语言·后端·rust
jooLs薯薯熹3 小时前
项目测试 - 哪些工具可以实现测试 Mock?
java·后端
徐寿春3 小时前
SpringBoot -- 自动化装配源码
spring boot·后端·自动化
摆烂工程师3 小时前
GPT4变笨了?教你解决GPT4降智问题!同时封装了个Chrome扩展程序进行检查GPT
前端·后端·程序员
yanessa_yu3 小时前
Maven项目报错:Blocked mirror for repositories
后端·maven