likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 4

这篇文章适合想要把海康的rtsp转码成浏览器可播放的格式,要求只是播放出来,有视频截图(可选),又不太了解音视频大坑的前端同学(服务自己部署就好了,不需要后端介入)。

支持通过接口访问,非常友好,也不需要在前端项目里写多余的东西,只需要有个rtsp原始流,接口推流->获取拉流参数->flv播放,结束。

需求背景

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

我的系统: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转其他流需要修改的配置项

[rtc]
externIP=改成你自己的服务器ip

[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,也可以评论区提问。

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

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

<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地址可能有特殊字符,所以我进行了编码。
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;

坑n+1: 接口报错500问题

github.com/ZLMediaKit/…

发现偶现推流接口500问题,经过测试发现超过3个流就不行了,看到这个issue, 结!论!是!这个服务器只支持3路。

访问/index/api/getMediaList接口查看流列表。

那么

  1. 可以关闭不需要转码的协议避免浪费资源
  2. 因为每次打开都是新的推流,所以可以在不看的时候调用 /index/api/close_streams关闭流。
  3. 开启无人观看自动关闭配置。酌情修改配置时间(可以根据播放器超时时间)。
  4. 还可以不用时间戳去开启流,用视频的唯一标识,这样多个客户端可以拉同一个流。(不过这不解决根本问题)

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

坑n+2: ZLM视频实时截图

截图的API是这一个:视频截图

如果出现了截图返回默认图片的问题,解法如下

  1. 修改配置文件截图FFmpeg模板 加上-rtsp_transport tcp
  2. 核对配置文件里的FFmpeg 路径 MAC可以用命令 where FFmpeg来查找位置
  3. 只能使用已经被推送的流 我使用addStreamProxy返回结果的路径就可以,其他的没经过推送的流就不行(比如我的服务器只支持3路,那就只能截这3路。。)

外网调试解决方案

花生壳内网穿透

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

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

我的思路

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

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

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

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

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

最后尝试ZLM方案成功。

其他方案

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

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

尝试的其他解决方案

node-media-server

media-soup

webrtc-stream(跑通本地)

海康的SDK

FFmpeg

总结

  1. 应该找一个方案一直做下去到走通为止
  2. 环境要想办法提前了解(比如不能用https,比如所有摄像头都是海康的,比如可以使用插件)
  3. 想办法挖掘真正的需求,需求工期都过一半了,我才知道原来要在大屏上用,且只需要考虑海康的摄像头,且可以用插件。

后端Leader从 “它太卡了!我要它不卡!”“我要一个流媒体服务器!我不管我就要!”“不然你用原来的那个DEMO!” (那个DEMO非常简单根本完全不能用)再到 “你自己写一个推拉流逻辑吧”

最后使用ZLM方案部署出问题 “你看看,那咋没有呢”

就。

还是要有自己的节奏,很多时候你听到的需求并不是真正的需求,连提出需求的人也不知道真正的需求是什么,虽然走了很多弯路,也学到了很多东西,这是在不混乱的团队里得不到的体验。