前言
本文是之前文章的一篇完善文,如果你是才接觸??低晹z像頭的二次開發(fā)請(qǐng)先閱讀入門篇
攝像頭預(yù)覽
1.什么是rtsp
在實(shí)現(xiàn)攝像頭的預(yù)覽的時(shí)候需要搞懂什么是rtsp。
rtsp是一種實(shí)時(shí)流傳輸協(xié)議(Real Time Streaming Protocol,RTSP),主要使用TCP和UDP完成數(shù)據(jù)的傳輸。
1.1 了解??低時(shí)tsp的url規(guī)范
【老版本】URL規(guī)定:
rtsp://username:password@[ipaddress]/[videotype]/ch[number]/[streamtype]
注:VLC可以支持解析URL里的用戶名密碼,實(shí)際發(fā)給設(shè)備的RTSP請(qǐng)求不支持帶用戶名密碼。
詳細(xì)描述:
舉例說明:
通道01主碼流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch01/main/av_stream
通道01子碼流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch01/sub/av_stream
通道01第3碼流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch01/stream3/av_stream
IP通道01的主碼流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch33/main/av_stream
IP通道01的子碼流:
rtsp://admin:test1234@172.6.22.106:554/h264/ch33/sub/av_stream
零通道主碼流(零通道無子碼流):
rtsp://admin:test1234@172.6.22.106:554/h264/ch0/main/av_stream
注:老版本URL,64路以下的NVR的IP通道的通道號(hào)從33開始,64路以及以上路數(shù)的NVR的IP通道的通道號(hào)從1開始。
【新版本】URL規(guī)定:
rtsp://username:password@[address]:[port]/Streaming/Channels/[id](?parm1=value1&parm2-=value2…)
注:VLC可以支持解析URL里的用戶名密碼,實(shí)際發(fā)給設(shè)備的RTSP請(qǐng)求不支持帶用戶名密碼。
詳細(xì)描述:
舉例說明:
通道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 (?后面可省略,默認(rèn)單播)
通道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,通道號(hào)全部按順序從1開始。
針對(duì)上面的單播多播做一個(gè)解釋
單播:用網(wǎng)絡(luò)技術(shù)的術(shù)語來描述就是“單播”,此時(shí)信息的接收和傳遞只在兩個(gè)節(jié)點(diǎn)之間進(jìn)行,
網(wǎng)絡(luò)節(jié)點(diǎn)之間的通信就好像是人們之間的對(duì)話一樣。如果一個(gè)人對(duì)另外一個(gè)人說話。
多播:多播也稱為“組播”:將網(wǎng)絡(luò)中同一業(yè)務(wù)類型主機(jī)進(jìn)行了邏輯上的分組,進(jìn)行數(shù)據(jù)收發(fā)的時(shí)候其數(shù)據(jù)僅僅在同一分組中進(jìn)行,其他的主機(jī)沒有加入此分組不能收發(fā)對(duì)應(yīng)的數(shù)據(jù)。
大概了解了rtsp和??低暤膗rl規(guī)范之后我們來拼接一個(gè)可以播放的地址。
1.2 下載(VLC media player)VLC多媒體播放器
能成功播放就說明成功了
注:沒有成功的可以檢查一下攝像頭的狀態(tài)和rtsp地址
2.用FFmpeg+Nginx-rtmp推流
2.1 使用FFmpeg工具+Nginx-rtmp模塊
上面我們說到了rtsp,但是這種格式前端是不能直接播放的。
所以我們需要使用FFmpeg把流進(jìn)行轉(zhuǎn)換,再用Nginx-rtmp模塊把流推到我們要播放的地址。
邏輯比較簡單,但是這里需要用到2個(gè)插件。
注:用這種方式推出來的流是rtmp格式的流,需要Flash插件,后面寫一個(gè)不需要Flash插件的
這種方式的好處是延遲低可能只有0.2延遲左右,缺點(diǎn)是需要Flash的插件支持。
2.2 下載FFmpeg
下載穩(wěn)定版的
下載解壓到指定目錄
配置ffmpeg的環(huán)境變量
計(jì)算機(jī)右鍵屬性——>高級(jí)系統(tǒng)設(shè)置——>環(huán)境變量——>系統(tǒng)變量——>Path——>放入ffmpeg解壓之后的目錄的bin文件夾地址
輸入下面的命令
ffmpeg -version
2.3 在Java里面操作FFmpeg
這里需要下載一個(gè)三方集成的jar包,來操作和執(zhí)行FFmpeg命令。
阿里云盤地址:https://www.aliyundrive.com/s/zjEKF198tNE
提取碼:2vn4
這里是下載解壓之后的存放jar包的位置,jar包中配置文件的路徑是作者的FFmpeg的安裝路徑。
注:可以更改源碼自行編譯jar包。
或者在自己的Springboot項(xiàng)目中建一個(gè)loadFFmpeg.properties配置文件來覆蓋
這里也是建議把這個(gè)jar包打進(jìn)自己的maven倉庫,然后引入這個(gè)jar包。
注:這三個(gè)方法需要注意,后面會(huì)用
因?yàn)檫@個(gè)第三方包默認(rèn)是以u(píng)dp推流的,所以需要修改源碼中的CommandAssemblyImpl類
2.4 在Nginx中配置rtmp模塊
這里有一個(gè)源碼地址,需要自己編譯(有能力的可以在windows自行編譯)
https://github.com/arut/nginx-rtmp-module
這個(gè)是已經(jīng)編譯好的(但是版本不是最新的)
https://github.com/illuspas/nginx-rtmp-win32
打開文件夾找到配置文件
重點(diǎn)注意這里的配置
2.5 得到rtmp流
復(fù)習(xí)一下前面的流程
rtsp流——>ffmpeg+Nginx-rtmp——>rtmp流——>前端播放
下面的方法會(huì)涉及到SDK的一些接口,沒看過的可以看之前的入門篇
之前的入門篇講過,在需要執(zhí)行操作的時(shí)候都需要登錄攝像頭。
所以這里的這個(gè)獲取通道號(hào)的時(shí)候前面是需要登錄的,方法中的lUserID就是登錄接口的返回值
注:通道號(hào)可以理解為相機(jī)的標(biāo)識(shí),用來區(qū)分調(diào)用的是哪一個(gè)攝像頭
/**
* 獲取相機(jī)通道號(hào)
*/
public int getChannelNumber() {
//獲取IP接入配置參數(shù)
IntByReference ibrBytesReturned = new IntByReference(0);
boolean bRet;
int iChannelNum = -1;
ipparacfg = new HCNetSDK.NET_DVR_IPPARACFG();
ipparacfg.write();
Pointer lpIpParaConfig = ipparacfg.getPointer();
bRet = hcNetSDK.NET_DVR_GetDVRConfig(lUserID, HCNetSDK.NET_DVR_GET_IPPARACFG,
0, lpIpParaConfig, ipparacfg.size(), ibrBytesReturned);
ipparacfg.read();
String devices = "";
if (!bRet) {
//設(shè)備不支持,則表示沒有IP通道
for (int iChannum = 0; iChannum < deviceiInfo.byChanNum; iChannum++) {
devices = "Camera" + (iChannum + deviceiInfo.byStartChan);
}
} else {
for (int iChannum = 0; iChannum < HCNetSDK.MAX_IP_CHANNEL; iChannum++) {
if (ipparacfg.struIPChanInfo[iChannum].byEnable == 1) {
devices = "IPCamera" + (iChannum + deviceiInfo.byStartChan);
}
}
}
if (StringUtils.isNotEmpty(devices)) {
//Camara開頭表示模擬通道
if (devices.charAt(0) == 'C') {
//子字符串中獲取通道號(hào)
iChannelNum = Integer.parseInt(devices.substring(6));
} else {
//IPCamara開頭表示IP通道
if (devices.charAt(0) == 'I') {
//子字符創(chuàng)中獲取通道號(hào),IP通道號(hào)要加32
iChannelNum = Integer.parseInt(devices.substring(8)) + 32;
} else {
log.info("獲取通道號(hào)失敗");
}
}
}
return iChannelNum;
}
推流的方法
private static final FFmpegManager manager =new FFmpegManagerImpl();
/**
* 開始推流
* @param appName 進(jìn)程名稱,為相機(jī)ip去"."
* @param account 登錄相機(jī)賬號(hào)
* @param password 密碼
* @param ip 相機(jī)ip
* @param channelNumber 相機(jī)的通道號(hào)
* @return String appName
*/
public static String startPlugFlow(String appName, String account, String password, String ip, int channelNumber) {
//如果進(jìn)程存在,則直接返回進(jìn)程名
if (MinitorUtil.taskerIsRun(appName)) {
return appName;
}
Map<String, String> map = new HashMap<>(10);
//嘗試用TCP方式 進(jìn)程名
map.put("appName", appName);
// 自定義的參數(shù),需要修改ffmpeg源代碼
map.put("rtspTransport", "tcp");
//組裝rtsp流
map.put("input", "rtsp://" + account + ":" + password + "@" + ip + "/Streaming/Channels" + channelNumber);
//rtmp流.live為nginx-rtmp的配置
map.put("output", "rtmp://" + "本機(jī)ip" + ":1935/live/");
map.put("codec", "copy");
//只推元碼流
map.put("twoPart", "1");
map.put("fmt", "flv");
// 執(zhí)行任務(wù),就是appName,如果執(zhí)行失敗返回為null
return manager.start(map);
}
注:上面這個(gè)方法執(zhí)行之前需要啟動(dòng)Nginx,控制臺(tái)會(huì)打印開始推流的命令,
命令執(zhí)行無誤并一直輸出紅色的推流信息,說明是成功的。
執(zhí)行完命令之后組裝rtmp的url地址
String rtmpurl="rtmp://" + "本機(jī)ip" + ":1935/live/" + appName);
這個(gè)地址就是最后的rtmp地址了,同樣的使用VLC播放器進(jìn)行測(cè)試。
能夠正確播放成功說明是沒有問題的。
2.6 關(guān)閉推流
3.用FFmpeg+Nginx-FLV推流
3.1 Nginx中配置flv模塊
阿里云盤地址:https://www.aliyundrive.com/s/oK9SgRquVvp
提取碼:gy20
這里也是一個(gè)編譯好的模塊了。
大概方式和上一種是差不多的
private static final FFmpegManager manager =new FFmpegManagerImpl();
/**
* 開始推流
* @param appName 進(jìn)程名稱,為相機(jī)ip去"."
* @param account 登錄相機(jī)賬號(hào)
* @param password 密碼
* @param ip ip 相機(jī)ip
* @param nginxIp nginxIp nginx的ip
* @param nginxPort nginxIp nginx的端口
* @param channelNumber 相機(jī)的通道號(hào)
* @return String appName
*/
public static String startPlugFlow(String appName, String account, String password, String ip,
String nginxIp, String nginxPort, int channelNumber) {
//如果進(jìn)程存在,則直接返回進(jìn)程名
if (MinitorUtil.taskerIsRun(appName)) {
return appName;
}
Map<String, String> map = new HashMap<>(10);
//嘗試用TCP方式 進(jìn)程名
map.put("appName", appName);
// 自定義的參數(shù),需要修改ffmpeg源代碼
map.put("rtspTransport", "tcp");
//組裝rtsp流
map.put("input", "rtsp://" + account + ":" + password + "@" + ip + "/Streaming/Channels" + channelNumber);
//rtmp流.live為nginx-rtmp的配置
map.put("output", "rtmp://" + nginxIp + ":" + nginxPort + "/live/");
map.put("codec", "copy");
//只推元碼流
map.put("twoPart", "1");
map.put("fmt", "flv");
// 執(zhí)行任務(wù),就是appName,如果執(zhí)行失敗返回為null
return manager.start(map);
}
注:上面這個(gè)方法執(zhí)行之前需要啟動(dòng)Nginx,控制臺(tái)會(huì)打印開始推流的命令,
命令執(zhí)行無誤并一直輸出紅色的推流信息,說明是成功的。
3.2 返回的http地址
執(zhí)行完命令之后組裝rtmp的url地址
String httpUrl="https://" + "nginxIp" + ":" + "nginxPort" + "/live?port=1935&app=live&stream=" + appName);
成功推流
成功播放
關(guān)閉推流:這里的流程和上面是一樣的
注:這種方式的優(yōu)點(diǎn)是不需要Flash插件就能播放
但是存在的問題是延遲較大接近8s左右的延遲和丟包的風(fēng)險(xiǎn)。
4.后續(xù)優(yōu)化
4.1 前端解決方案
前端下載??低暤那岸碎_發(fā)包文章來源:http://www.zghlxwxcb.cn/news/detail-401934.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-401934.html
4.2 后端解決方案
- 優(yōu)化FFmpeg的命令和方法。更新并編譯更高Nginx的版本。
- 使用WVP+流媒體服務(wù)器實(shí)現(xiàn)推流。
到了這里,關(guān)于SpringBoot+海康威視攝像頭實(shí)現(xiàn)在前端的預(yù)覽的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!