• Home
    • 普通市民付先生 photo

      普通市民付先生

      世界并非非黑即白

    • Email
    • Github
    • Steam
    • Weibo
  • Posts
    • All Posts
    • All Tags
  • Projects

海康威视取流的那些事

11 Sep 2021

Reading time ~2 minutes

​ 首先,先描述下需求吧。因业务需要需要将海康威视摄像头获取到的视频流展示至网页端…听起来很简单对不对?Naive!三周了!你知道这三周我都是怎么过来的吗!

​ 其次,感谢一下三周以来陪我一起Debug的祁昆仑老师,天知道如果我一个人搞这个得花多长时间。

​ 最后吐槽一下海康威视做的SDK,要么好好写文档,要么好好测试做Debug,贵司的产品真的不好用…

​ 仅以此博文记录踩过的坑,以及流逝的三周岁月。

1.海康威视Web SDK(有插件版的和无插件版的)

​ 海康威视有插件版的SDK,用不起来我认为很大程度上海康威视的写文档的和开发人员要背锅,没有详细的文档,甚至得靠自己Debug js代码……

​ 有插件版的SDK,下载完成解压后->运行其中的exe安装视频插件->进入Demo->在Demo中输入IP、帐号、密码。就会出现第一个问题:

	{项目文件夹}/ISAPI/Security/sessionLogin/capabilities?username=admin Failed to load resource: the server responded with a status of 404 (Not Found)

​ 当然这个错误十分好理解,项目文件夹下的确是没有这么大串所描述的文件或者文件夹的;理应我们也不应该向这个文件夹进行这个请求,而应当向我们输入的IP地址进行请求。为什么会这样呢,在检查了源码经过尝试后,我们发现当不进行任何参数的传递时,的的确确是向根目录进行请求的。(在这里怀疑一下这个SDK直接用在了录像柜后台)

​ 那我们传的参数去哪里了呢?总之检查过了Demo里的代码,没有问题;尝试直接在js库文件中将localhost修改为请求的IP出现了跨域问题;最后在搜索你nginx 海康威视后,发现海康威视有自带Nginx的Web SDK以及Demo,它还不需要插件!

​ 下载解压无插件版Web SDK->启动Nginx->访问Demo->在Demo中输入IP、帐号、密码,然后…然后出现了新的问题。

:/ISAPI/Security/sessionLogin/capabilities?username=admin Failed to load resource: the server responded with a status of 400 (too many headers)

​ 在各种面向百度、谷歌、StackoverFlow编程无果后,事情变得有点绝望。接着我突然想到自己前面的猜测——这个sdk可能录像柜80端口也在使用。使用浏览器通过对网络进行记录发现:确实无插件版的SDK中的请求Cookies比录像柜中的请求的Cookie多出不止一项。

​ 在各种搜索引擎的帮助下,定位到了nginx的配置上,在nginx对请求进行转发时加了一堆没有必要的头,注释掉这些头即可。

​ (ps:最新的版本中已修复这一问题)

​ 注释完没有必要的头之后能够成功登录,但你翻过了一座山还有另一座山等着你,爬过一个坑你就会踩进另一个坑…是的我们的设备不支持Socket取流。

2.RTSP取流FFMPEG转流RTMP浏览器播放

​ 咨询了海康威视的客服(在这里夸一下客服,回复的还蛮及时)后,海康威视的客服给出了这个解决方案,在继续面向搜索引擎编程的指引下,成功地完成了第一步尝试。具体步骤如下:

​ 首先下载nginx Gryphon,这个版本非常贴心地内置了ffmpeg的模块;解压,修改nginx-win.conf的sever如下:

 server {
        listen       8088;
        server_name  localhost;
        add_header Access-Control-Allow-Origin *;
 
        location /stat {
            rtmp_stat all;
            rtmp_stat_stylesheet stat.xsl;
        }
        location /stat.xsl {
            root nginx-rtmp-module/;
        }
        location /control {
            rtmp_control all;
        }
 

​ rtmp修改如下:

 
rtmp {
    server {
        listen 1935;
        chunk_size 4000;
        application live {
             live on;
        }
    }
}

​

​ 第二步下载ffmpeg,进入bin目录,使用以下命令进行转码:

ffmpeg -re  -rtsp_transport tcp -i "rtsp://admin:{password}@{ipaddress}:554/Streaming/Channels/101?transportmode=unicast" -f flv -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -s 1280x720 -q 10 "rtmp://localhost:1935/live/test2"

​ 其中password处换成自己的密码,ip地址处换成海康威视设备的ip;”rtmp://localhost:1935/live/test2”中的1935已在conf内配置;test2是视频流的临时地址。

​ 第三步在nginx Gryphon内新建test.html,写入如下代码:

<html>
     <head>
          <title>video</title>
           <!-- 引入css --> 
           <link href="//cdn.bootcss.com/video.js/7.0.0-alpha.1/alt/video-js-cdn.css" rel="stylesheet" /> 
        </head> 
        <body> 
            <div class="videoBox"> 
                <video id="my_video_1" class="video-js vjs-default-skin" controls>
                     <source src="http://192.168.1.129:8088/video/test.m3u8" type="application/x-mpegURL"> 
                    </video> 
                </div> 
        </body> 
        </html> 
        <script src="//cdn.bootcss.com/video.js/7.0.0-alpha.1/video.min.js">
                    </script> 
                    <script src="https://unpkg.com/videojs-contrib-hls/dist/videojs-contrib-hls.js"></script>
                    </script> 
                    <script> videojs.options.flash.swf = "./videojs/video-js.swf"; var player = videojs('my_video_1', {"autoplay":true}); player.play();
                     </script>

​ 第四步使用win-conf启动Nginx。

​ 然后就大功告成啦~才怪。

​ Chorme等一众主流浏览器已停止了对flash的支持因此无法用这种方法进行视频的转流播放,查找后决定使用苹果的方案。将命令改成:

ffmpeg -re  -rtsp_transport tcp -i "rtsp://admin:{password}@{ipaddress}:554/Streaming/Channels/101?transportmode=unicast" -f flv -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -s 1280x720 -q 10 "F:/nginx/html/video/test.m3u8"

​ html中的视频地址改为:

               <script src="https://unpkg.com/videojs-contrib-hls/dist/videojs-contrib-hls.js"></script>

​ 启动nginx,大功告成,撒花!

3.flvjs

​ 上面的方案挺好的,就是不太好。

​ 摄像头采集的画面转码后分辨率极低,并且采集到的画面如果有大幅度的晃动会直接花屏,另外非常不能接受的是它的延迟——虽然可能也和我的电脑配置有关——长达半分钟。在祁老师的激情调研下,决定采用这一节描述的方案。

​ 使用ffmpeg将RTSP转为flv,通过websocket取流后,使用flvjs对视频流再播放。

​ 这里直接使用找到的项目,项目地址:https://github.com/LorinHan/flvjs_test

​ 将整个项目扒下来,解压。在项目的根目录安装fluent-ffmpeg,websocket-stream。

npm install express express-ws fluent-ffmpeg websocket-stream -S -D

​

​ 修改index.js将ffmpeg的目录放置替换ffmpeg.setFfmpegPath中的路径。

ffmpeg.setFfmpegPath(put_your_ffmpeg_path_here);

​ 使用node index.js启动项目。

​ 在front/src/component中修改HellWorld.vue中的rtsp地址。

    data () {
        return {
          id: "1",
          rtsp: "put_your_rtsp_path_here",
            player: null
        }
    },

​ 回到front,运行npm install安装相关依赖,安装完毕后运行npm run dev 运行项目,在浏览器输入vue项目的地址,你就能在你的浏览器中欣赏到一片纯白。

​ fluent-ffmpeg报错:

ffmpeg exited with code 1

​ 出现这个问题是因为ffmpeg传入的参数不对,在看了stackoverflow等论坛的相关问题的相关描述以及相关的解决方案后找到了ffmpeg-fluent的项目地址,在项目地址中恰好有websocket取流的实例,参照示例将代码修改如下:


var express =  require("express");
var expressWebSocket = require("express-ws");
var ffmpeg = require("fluent-ffmpeg");
ffmpeg.setFfmpegPath("C:/ffmpeg/bin/ffmpeg");
var webSocketStream = require("websocket-stream/stream");
var WebSocket = require("websocket-stream");
var http = require("http");
function localServer() {
    let app = express();
    app.use(express.static(__dirname));
    expressWebSocket(app, null, {
        perMessageDeflate: true
    });
    app.ws("/rtsp/:id/", rtspRequestHandle)
    app.listen(8888);
    console.log("express listened")
}
 

function rtspRequestHandle(ws, req) {
    console.log("rtsp request handle");
    const stream = webSocketStream(ws, {
        binary: true,
        browserBufferTimeout: 1000000
    }, {
        browserBufferTimeout: 1000000
    });
    let url = req.query.url;
    console.log("rtsp url:", url);
    console.log("rtsp params:", req.params);
    ffmpeg('rtsp://admin:{password}@{ip}:554/Streaming/Channels/201?transportmode=unicast', { timeout: 432000 })
  .preset('flashvideo')
  .addOption('-hls_time', 1)
  .addOption('-hls_list_size',10)
  .on('end', function() {
    console.log('file has been converted succesfully');
  })
  .on('error', function(err) {
    console.log('an error happened: ' + err.message);
  }).noAudio().pipe(stream);
}
localServer();

​ 再重新启动,成功!

​ 说明下这里的参数,其中preset(‘flashvideo’)是指使用fluent-ffmpeg中以内置的预设即等同于:

 ffmpeg
    .format('flv')
    .flvmeta()
    .size('320x?')
    .videoBitrate('512k')
    .videoCodec('libx264')
    .fps(24)
    .audioBitrate('96k')
    .audioCodec('aac')
    .audioFrequency(22050)
    .audioChannels(2);

​ -hls_time指的是缓存时间,即缓存到多少s开始播放;hls_list_size为缓存列表,表示最多缓存多少个hls。

##

4.总结

​ 在flash插件各大主流浏览器不在支持的情况今天实现rtsp流播放的方式有两种:1.RTSP取流FFMPEG转m3u8推流;2.使用ffmpeg将RTSP转为flv,通过websocket取流后,使用flvjs对视频流再播放。



海康威视RTSPffmpeg取流 Share Tweet +1