ZLMediaKit实现海康监控摄像头实时播放+RTSP流浏览器可播放转码方案调研+获取视频截图

需求背景

我司的合作公司采购了海康的摄像头做室内外监控,需要实现在内网大屏上播放海康视频流,因为不是直接采买,没有直接对接海康的技术人员,自己摸索了大半个月方案。

我的系统:MAC

播放端:Windows电视

服务器:Ubuntu

难点:

  1. 内网不方便调试
  2. 没有海康的技术对接,官方文档不清晰
  3. 音视频技术栈过多过杂,坑点巨多
  4. 内网因为种种原因不支持https(后来通过询问知道的)
  5. 需求不清晰明确

最终解决方案

ZLMediaKit

ZLMediaKit-gitHub

快速开始

安装启动过程

参考这一篇文章: blog.csdn.net/qq_53200007...

相关命令

以守护进程启动: ./MediaServer -d &
// 如果554端口被占用

查找554端口的使用进程 sudo lsof -i :554
// 杀死554端口的进程

sudo kill -9 <进程ID>

配置文件

参见: 配置文件详解

如果是通过docker安装的,配置文件在conf里,如果非docker,配置文件的路径在release/你的系统/Debug下。

RTSP转其他流需要修改的配置项

js 复制代码
[rtc]
externIP=192.168.11.102

[rtsp]
directProxy=0

跑通DEMO

重要: 按照API说明文档,先在ApiFox或者ApiPost等调试工具中访问通了,确保服务可用.

  1. ZLM全面支持H265/H264/AAC/G711/OPUS,检查一下视频编码是否在其中。
  2. 确定流可用,可通过VLC播放器进行播放。(VLC->添加流->来自网络->rtsp流直接播放不需要改配置)
  3. 拿到秘钥secret,在配置文件前几行。
  4. 确保服务在运行中,随时监控日志。
  5. stream需要是唯一的值,推荐时间戳。

主要的接口就是通过/index/api/addStreamProxy推流,成功之后可以通过webRTC拉取,也可以flv拉取。我们的服务器不支持https,所以使用flv拉取。

出现如下响应就是推流成功了,如果没成功,检查一下配置文件,看一下错误日志,多搜搜gitHub的issue,也可以评论区提问。

把下面的代码复制到HTML文件中就可以看到画面啦~

xml 复制代码
<script src="https://cdn.bootcss.com/flv.js/1.5.0/flv.min.js"></script>
<video id="videoElement"></video>
<button class="btn">播放</button>
<script>
    if (flvjs.isSupported()) {
        var videoElement = document.getElementById('videoElement');
        var flvPlayer = flvjs.createPlayer({
            type: 'flv',
            url: 'http://这是服务的主机地址/这里写app的值/这里写stream的值.live.flv'
        });
        flvPlayer.attachMediaElement(videoElement);
        flvPlayer.load();
        document.querySelector('.btn').addEventListener('click', () => {
            flvPlayer.play();
        })
    }
</script>

可通用Hook(拿去就能用)

  1. 这里的stream是流的唯一标识,我选择了用dayJS产生的时间戳,也可以用其他标识。
  2. 这里的容器我写死了document.getElementById('videoElement'),也可以改成通过ref传进来的方式。
  3. rtsp地址可能有特殊字符,所以我进行了编码。
javascript 复制代码
import axios from 'axios';
import dayjs from 'dayjs';

const useFlvVideo = (
  rtspUrl,
  serverConfig = {
    serverSecret: '',
    serverUrl: '',
    app: 'live'
  }
) => {
  const { serverSecret, serverUrl, app } = serverConfig;

  let flvPlayer = null;
  let stream = null;

  const checkParams = () => {
    if (!serverSecret) {
      throw new Error('serverSecret is required');
    }

    if (!serverUrl) {
      throw new Error('serverUrl is required');
    }

    if (!app) {
      throw new Error('app is required');
    }
    
    if (!rtspUrl) {
      throw new Error('rtspUrl is required');
    }
  };

  checkParams();

  const checkFlvIsSupported = () => {
    return window?.flvjs.isSupported();
  };

  const startPlay = async () => {


    stream = dayjs().valueOf();

    await addStreamProxy();

    await startVideoPlay();

    return stream;
  };

  const addStreamProxy = async () => {
    const url = `http://${serverUrl}/index/api/addStreamProxy`;

    const result = await axios.get(url, {
      params: {
        secret: serverSecret,
        vhost: serverUrl,
        app,
        url: encodeURI(rtspUrl?.value),
        stream: stream
      }
    });

    console.log(result, 'result');
  };

  const startVideoPlay = () => {
    var videoElement = document.getElementById('videoElement');

    flvPlayer = window?.flvjs.createPlayer({
      type: 'flv',
      url: `http://${serverUrl}/${app}/${stream}.live.flv`
    });

    console.log('videoElement', videoElement, flvPlayer, `http://${serverUrl}/${app}/${stream}.live.flv`);

    flvPlayer.attachMediaElement(videoElement);
    flvPlayer.load();
    flvPlayer.play();
  };

  const stopPlay = async () => {
    const url = `http://${serverUrl}/index/api/close_streams`;
    const result = await axios.get(url, {
      params: {
        secret: serverSecret,
        vhost: serverUrl,
        app,
        stream,
        force: true
      }
    });

    flvPlayer.stop();
    console.log(result, 'result');
  };

  return {
    startPlay,
    stopPlay,
    flvIsSupported: checkFlvIsSupported()
  };
};

export default useFlvVideo;

外网调试解决方案

花生壳内网穿透

内网不方便调试,每次都需要把包丢上去看效果,这里用了花生壳,有1G试用流量限制,穿透设置TCP协议,554端口,穿透出来的流能通过VLC播放器播放就行。

和向日葵同一家厂商,远程调试必备。

我的思路

一开始,我接到的需求是,摄像头采集视频后会经过一台外网算法服务器计算之后再推出来,但是在VLC播放器太卡了,需要看看为什么卡顿。于是我去简单了解了音视频播放原理:

前端处理实时监控直播协议和流媒体服务器的调研记录

猜测可能是 1.带宽不够 2.传输过程损耗太大 3.经过算法服务器处理之后有损耗, 需求就变成了"春花同学,你开发一个流媒体服务器!我要一个流媒体服务器!",尝试进一步沟通失败后,我的目标变成了流媒体服务器。

发现前端可用的流媒体服务器真的不多,基本只有media-soup和node-media-server,前任前段用node-media-serve写了一个简单的DEMO,就是把mp4推成rtsp(推流),再转成flv(拉流),面对摄像头的复杂场景真的没法用。但是我知道了ffmpeg是可以实现推流和转码的。我用media-soup也实现了一遍这个DEMO,资料非常少,跑是跑通了,还是一头雾水。

node-media-soup跑通过程记录

后来去了现场之后发现现场的视频播放延迟在正常范围,讨论之后决定把服务器布到现场去,需求就变成了"把这个RTSP在浏览器播放"。于是我研究了一番,尝试使用webrtc-stream,也是一番折腾,本地跑通了,但是局域网内跑不通,gitHub上也没有找到合适的解决办法。

这时候又得知短期内用的只有海康摄像头,询问海康智能客服得到对接指南,海康提供的SDK和DEMO只能在Windows上用,Linux上没什么能迅速跑通的方案,内嵌到VUE项目中的方案,文档较为复杂麻烦。Chrome的session禁止共享安全策略等等问题,尝试过了均不能内嵌。一般解决方案都是跳转到海康的预览页面。

最后尝试ZLM方案成功。

其他方案

尝试的其他解决方案

node-media-server

media-soup

media-soup-demo 跑通过程记录

webrtc-stream(跑通本地)

海康的SDK

FFmpeg

总结

  1. 应该找一个方案一直做下去到走通为止
  2. 环境要想办法提前了解(比如不能用https)
  3. 想办法挖掘真正的需求 现在
相关推荐
uzong34 分钟前
半小时打造七夕传统文化网站:Qoder AI编程实战记录
后端·ai编程
快乐就是哈哈哈38 分钟前
从传统遍历到函数式编程:彻底掌握 Java Stream 流
后端
WebInfra1 小时前
Rspack 1.5 发布:十大新特性速览
前端·javascript·github
雾恋1 小时前
我用 trae 写了一个菜谱小程序(灶搭子)
前端·javascript·uni-app
ningqw2 小时前
JWT 的使用
java·后端·springboot
烛阴2 小时前
TypeScript 中的 `&` 运算符:从入门、踩坑到最佳实践
前端·javascript·typescript
追逐时光者2 小时前
精选 2 款 .NET 开源、实用的缓存框架,帮助开发者更轻松地处理系统缓存!
后端·.net
David爱编程3 小时前
指令重排与内存屏障:并发语义的隐形守护者
java·后端
Java 码农3 小时前
nodejs koa留言板案例开发
前端·javascript·npm·node.js
ZhuAiQuan3 小时前
[electron]开发环境驱动识别失败
前端·javascript·electron