vue+flv.js+SpringBoot+websocket实现视频监控与回放

这篇具有很好参考价值的文章主要介绍了vue+flv.js+SpringBoot+websocket实现视频监控与回放。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

vue+flv.js+SpringBoot+websocket实现视频监控与回放

需求:vue+springboot的项目,需要在页面展示出海康的硬盘录像机连接的摄像头的实时监控画面以及回放功能.

  1. 之前项目里是纯前端实现视频监控和回放功能.但是有局限性.就是ip地址必须固定.新的需求里设备ip不固定.所以必须换一种思路.
  2. 通过设备的主动注册,让设备去主动连接服务器后端通过socket推流给前端实现实时监控和回放功能;

思路:

1:初始化设备.后端项目启动时就调用初始化方法.
2:开启socket连接.前端页面加载时尝试连接socket.
3:点击播放,调用后端推流接口.并且前端使用flv.js实现播放.

准备工作:

1:vue项目引入flv.js。
npm install --save flv.js
main.js里面引入
import flvjs from ‘flv.js’;
Vue.use(flvjs)
但是这里我遇见一个坑.开发模式没有问题.但是打包之后发现ie浏览器报语法错误.不支持此引用.所以修改引用地址.
在webpack.base.conf.js的module.exports下添加

  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'flvjs':'flv.js/dist/flv.js'
    }
  },

plugins下添加

  plugins: [
    new webpack.ProvidePlugin({
      flvjs:'flvjs',
      $: "jquery",
      jQuery: "jquery",
      "window.jQuery": "jquery"
    })
  ],

最后页面引入时:

import flvjs from "flv.js/dist/flv.js";

2.准备一个硬盘录像机,并添加一个摄像头设备以做测试使用.
硬盘录像机设置为主动注册模式.并配置好ip和端口以及子设备ID
vue+flv.js+SpringBoot+websocket实现视频监控与回放
在设置里的网络设置里面
vue+flv.js+SpringBoot+websocket实现视频监控与回放

3.后端搭建好websocket工具类
包含通用的OnOpen,onClose,onError等方法.

实现:
1.项目启动开启设备服务.这个SDKLIB里面都有就不介绍了.
2.页面加载尝试开启socket连接.

//尝试连接websocket
    startSocket(channelnum, device_value) {
      try {
        let videoWin = document.getElementById(this.currentSelect);
        if (flvjs.isSupported()) {
          let websocketName =
            "/device/monitor/videoConnection/" + channelnum + device_value;
          console.log("进入连接websocket", this.ipurl + websocketName);
          const flvPlayer = flvjs.createPlayer(
            {
              type: "flv",
              //是否是实时流
              isLive: true,
              //是否有音频
              hasAudio: false,
              url: this.ipurl + websocketName,
              enableStashBuffer: true,
            },
            {
              enableStashBuffer: false,
              stashInitialSize: 128,
            }
          );
          flvPlayer.on("error", (err) => {
            console.log("err", err);
          });
          flvjs.getFeatureList();
          flvPlayer.attachMediaElement(videoWin);
          flvPlayer.load();
          flvPlayer.play();
          return true;
        }
      } catch (error) {
        console.log("连接websocket异常", error);
        return false;
      }
    },

这里传的参数是通道号和设备信息.无需在意.只要是唯一key就可以.

2.socket连接成功后.调用后端推流方法实现播放.
这里说一下后端的推流方法.
调用SDK里的CLIENT_RealPlayByDataType方法

/**
     * 实时预览拉流
     *
     * @param loginHandler 登录句柄
     * @param channel      通道号
     * @param emDataType   回调拉出的码流类型,{@link NetSDKLib.EM_REAL_DATA_TYPE}
     */
    public long preview(long loginHandler, int channel, NetSDKLib.fRealDataCallBackEx realDataCallBackEx, fRealDataCallBackEx2 realPlayDataCallback, int emDataType, int rType, boolean saveFile, int emAudioType) {
        NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE inParam = new NetSDKLib.NET_IN_REALPLAY_BY_DATA_TYPE();
        NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE outParam = new NetSDKLib.NET_OUT_REALPLAY_BY_DATA_TYPE();
        inParam.nChannelID = channel;
        inParam.rType = rType;
        if(realDataCallBackEx!=null){
            inParam.cbRealData=realDataCallBackEx;
        }
        if(realPlayDataCallback!=null){
            inParam.cbRealDataEx = realPlayDataCallback;
        }
        inParam.emDataType = emDataType;
        inParam.emAudioType=emAudioType;
        if (saveFile) {
            inParam.szSaveFileName = UUID.randomUUID().toString().replace(".", "").replace("-", "") + "." + EMRealDataType.getRealDataType(emDataType).getFileType();
        }
        NetSDKLib.LLong realPlayHandler = netsdk.CLIENT_RealPlayByDataType(new NetSDKLib.LLong(loginHandler), inParam, outParam, 3000);
        if (realPlayHandler.longValue() != 0) {
            netsdk.CLIENT_MakeKeyFrame(new NetSDKLib.LLong(loginHandler),channel,0);
            RealPlayInfo info = new RealPlayInfo(loginHandler, emDataType, channel, rType);
            realPlayHandlers.put(realPlayHandler.longValue(), info);
        } else {
            log.error("realplay failed.error is " + ENUMERROR.getErrorMessage(), this);
        }
        return realPlayHandler.longValue();
    }

注意:这里的码流类型选择flv.
回调函数里面:

// 回调建议写成单例模式, 回调里处理数据,需要另开线程
    @Autowired
    private WebSocketServer server;
    private Log log = Log.get(WebSocketRealDataCallback.class);

    @Override
    public void invoke(NetSDKLib.LLong lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, int param, Pointer dwUser) {

        RealPlayInfo info = DeviceApi.realPlayHandlers.get(lRealHandle.longValue());
        if (info != null && info.getLoginHandler() != 0) {
            //过滤码流
            byte[] buffer = pBuffer.getByteArray(0, dwBufSize);
            if (info.getEmDataType() == 0 || info.getEmDataType() == 3) {
                //选择私有码流或mp4码流,拉流出的码流都是私有码流
                if (dwDataType == 0) {
                    log.info(dwDataType + ",length:" + buffer.length + " " + Arrays.toString(buffer), WebSocketRealDataCallback.class);
                    sendBuffer(buffer, lRealHandle.longValue());
                }
            } else if ((dwDataType - 1000) == info.getEmDataType()) {
                log.info(dwDataType + ",length: " + buffer.length + Arrays.toString(buffer), WebSocketRealDataCallback.class);
                sendBuffer(pBuffer.getByteArray(0, dwBufSize), lRealHandle.longValue());
            }
        }


    }

以及调用Websocket里面的sendMessageToOne发送给指定客户端

    /**
     * 发送数据
     * @param bytes
     * @param realPlayHandler
     */
    private static void sendBuffer(byte[] bytes, long realPlayHandler) {
        /**
         * 发送流数据
         * 使用pBuffer.getByteBuffer(0,dwBufSize)得到的是一个指向native pointer的ByteBuffer对象,其数据存储在native,
         * 而webSocket发送的数据需要存储在ByteBuffer的成员变量hb,使用pBuffer的getByteBuffer得到的ByteBuffer其hb为null
         * 所以,需要先得到pBuffer的字节数组,手动创建一个ByteBuffer
         */
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        server.sendMessageToOne(realPlayHandler, buffer);
    }

这里传的参数是设备初始化的时候得到的登录句柄.以及流数据.

/**
     * 发送binary消息给指定客户端
     *
     * @param realPlayHandler 预览句柄
     * @param buffer          码流数据
     */
    public void sendMessageToOne(long realPlayHandler, ByteBuffer buffer) {
        //登录句柄无效
        if (realPlayHandler == 0) {
            log.error("loginHandler is invalid.please check.", this);
            return;
        }
        RealPlayInfo realPlayInfo = AutoRegisterEventModule.findRealPlayInfo(realPlayHandler);
        if(realPlayInfo == null){
            //连接已断开
        }
        String key = realPlayInfo.getChannel()+realPlayInfo.getSbbh();
        Session session = sessions.get(key);
        if (session != null) {
            synchronized (session) {
                try {
                    session.getBasicRemote().sendBinary(buffer);
                    byte[] bytes=new byte[buffer.limit()];
                    buffer.get(bytes);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else {

            //log.error("session is null.please check.", this);
        }
    }

这样就实现了视频监控.

效果:
vue+flv.js+SpringBoot+websocket实现视频监控与回放
分享一下websocket代码:

package com.dahuatech.netsdk.webpreview.websocket;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @description websocket实现类
 */
@ServerEndpoint("/websocket/{realPlayHandler}")
@Component
public class WebSocketServer {
    private static Log log = LogFactory.get(WebSocketServer.class);
    private FileOutputStream outputStream;
    /**
     * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全
     */
    private final AtomicInteger onlineCount = new AtomicInteger(0);
    /**
     * 存放每个客户端对应的WebSocket对象,根据设备realPlayHandler建立session
     */
    public static ConcurrentHashMap<Long, Session> sessions = new ConcurrentHashMap<>();
    /**
     * 存放客户端的对象
     *//*
    public static CopyOnWriteArrayList<Session> sessionList=new CopyOnWriteArrayList<>();*/

    /**
     * 有websocket client连接
     *
     * @param realPlayHandler 预览句柄
     * @param session
     */
    @OnOpen
    public void OnOpen(@PathParam("realPlayHandler") long realPlayHandler, Session session) {

        if (sessions.containsKey(realPlayHandler)) {
            sessions.put(realPlayHandler, session);
        } else {
            sessions.put(realPlayHandler, session);
            addOnlineCount();
        }

        log.info("websocket connect.session: " + session);
    }

    /**
     * 连接关闭调用的方法
     *
     * @param realPlayHandler 预览句柄
     * @param session         websocket连接对象
     */
    @OnClose
    public void onClose(@PathParam("realPlayHandler") Long realPlayHandler, Session session) {
        if (sessions.containsKey(realPlayHandler)) {
            sessions.remove(realPlayHandler);
            subOnlineCount();
        }
    }

    /**
     * 发生错误
     *
     * @param throwable e
     */
    @OnError
    public void onError(Throwable throwable) {
        throwable.printStackTrace();
    }

    /**
     * 收到客户端发来消息
     *
     * @param message 消息对象
     */
    @OnMessage
    public void onMessage(ByteBuffer message) {
        log.info("服务端收到客户端发来的消息: {}", message);
    }

    /**
     * 收到客户端发来消息
     *
     * @param message 字符串类型消息
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("服务端收到客户端发来的消息: {}", message);
    }

    /**
     * 发送消息
     *
     * @param message 字符串类型的消息
     */
    public void sendAll(String message) {
        for (Map.Entry<Long, Session> session : sessions.entrySet()) {
            session.getValue().getAsyncRemote().sendText(message);
        }
    }

    /**
     * 发送binary消息
     *
     * @param buffer
     */
    public void sendMessage(ByteBuffer buffer) {
        for (Map.Entry<Long, Session> session : sessions.entrySet()) {
            session.getValue().getAsyncRemote().sendBinary(buffer);
        }
    }

    /**
     * 发送binary消息给指定客户端
     *
     * @param realPlayHandler 预览句柄
     * @param buffer          码流数据
     */
    public void sendMessageToOne(long realPlayHandler, ByteBuffer buffer) {
        //登录句柄无效
        if (realPlayHandler == 0) {
            log.error("loginHandler is invalid.please check.", this);
            return;
        }
        Session session = sessions.get(realPlayHandler);
        if (session != null) {
            synchronized (session) {
                try {
                    session.getBasicRemote().sendBinary(buffer);
                    byte[] bytes=new byte[buffer.limit()];
                    buffer.get(bytes);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } else {
            //log.error("session is null.please check.", this);
        }
    }

    public void sendMessageToAll(ByteBuffer buffer) {

        for (Session session : sessions.values()) {
            synchronized (session) {
                try {
                    /**
                     * tomcat的原因,使用session.getAsyncRemote()会报Writing FULL WAITING error
                     * 需要使用session.getBasicRemote()
                     */
                    session.getBasicRemote().sendBinary(buffer);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 主动关闭websocket连接
     *
     * @param realPlayHandler 预览句柄
     */
    public void closeSession(long realPlayHandler) {
        try {
            Session session = sessions.get(realPlayHandler);
            if (session != null) {
                session.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取当前连接数
     *
     * @return
     */
    public int getOnlineCount() {
        return onlineCount.get();
    }

    /**
     * 增加当前连接数
     *
     * @return
     */
    public int addOnlineCount() {
        return onlineCount.getAndIncrement();
    }

    /**
     * 减少当前连接数
     *
     * @return
     */
    public int subOnlineCount() {
        return onlineCount.getAndDecrement();
    }
}

遇见的坑:
前端在播放的时候一开始始终不出画面.流数据已经拉过来了.后来才发现是因为hasAudio参数
vue+flv.js+SpringBoot+websocket实现视频监控与回放
这里如果设置成了true.则你的电脑必须插入耳机.不然会报错;

总结:
之前使用纯前端实现视频监控和回放时.浏览器时只支持IE.使用后端推流的方式实现视频监控和回放时.浏览器支持谷歌火狐Edge等.但是又不支持IE了.很有意思.
flv的官方文档解释的是:
vue+flv.js+SpringBoot+websocket实现视频监控与回放
由于IO限制,flv.js可以支持HTTP上的FLV直播流Chrome 43+,FireFox 42+,Edge 15.15048+和Safari 10.1+现在。

最后:
由于是后端不停的拉流.所以流量和服务器压力比较大.可能同时打开多个监控.会出现卡顿的情况.需要注意.文章来源地址https://www.toymoban.com/news/detail-432895.html

到了这里,关于vue+flv.js+SpringBoot+websocket实现视频监控与回放的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • vue3+nodejs(websocket)实现监控拉rtsp流,使用flv.js+ffmpeg包(主要建立websocket是为了转码传流)

    vue3+nodejs(websocket)实现监控拉rtsp流,使用flv.js+ffmpeg包(主要建立websocket是为了转码传流)

    关于拉取监控摄像头的流,我个人去查了很多资料,也是因为之前没有接触过这一模块,加上目前公司也没有后端去写接口,所以我直接用node去写websocket,与前端建立起通信,能够进行后续转码、传流,能够实现实时播放监控画面。 这里的rtsp流是要事先知道的,监控的这个

    2024年02月20日
    浏览(16)
  • uni-app 和H5页面视频播放flv格式视频监控

    本文章向大家介绍uniApp 实现微信小程序和app视频播放flv格式视频监控,主要包括uniApp 实现微信小程序和app视频播放flv格式视频监控使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。 video 支持 App平台: 支持本地视频(

    2023年04月08日
    浏览(15)
  • linux+nginx-http-flv-module+ffmpeg实现搭建简易流媒体服务器将rtsp流转flv格式在web端和微信小程序实时播放监控视频

    linux+nginx-http-flv-module+ffmpeg实现搭建简易流媒体服务器将rtsp流转flv格式在web端和微信小程序实时播放监控视频

    一.介绍背景 公司项目开发需求:将海康摄像头的rtsp流在web端及微信小程序端进行播放。之前我写过一篇关于web端使用webtrc+videojs播放rtsp流的文章,确实能够解决web端播放rtsp流的需求,但是这次多加了一个微信小程序....所以要考虑小程序的播放问题。本着探索实践的精神在

    2024年02月08日
    浏览(14)
  • 安防监控视频汇聚EasyCVR平台的FLV视频流在VLC中无法播放的原因排查

    安防监控视频汇聚EasyCVR平台的FLV视频流在VLC中无法播放的原因排查

    众所周知,TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入,包括主流标准协议国标GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。在视频流的处理与分发上,视频监控汇聚平台EasyCVR的性能也同样表现得很优秀,平台可对外分

    2024年02月13日
    浏览(9)
  • 安防监控视频汇聚平台EasyCVR分发的FLV视频流在VLC中无法播放是什么原因?

    安防监控视频汇聚平台EasyCVR分发的FLV视频流在VLC中无法播放是什么原因?

    众所周知,TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入,包括主流标准协议国标GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。在视频流的处理与分发上,视频监控汇聚平台EasyCVR的性能也同样表现得很优秀,平台可对外分

    2024年02月13日
    浏览(15)
  • 视频汇聚平台EasyCVR安防监控视频汇聚平台的FLV视频流在VLC中无法播放的问题解决方案

    视频汇聚平台EasyCVR安防监控视频汇聚平台的FLV视频流在VLC中无法播放的问题解决方案

    众所周知,TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入,包括主流标准协议国标GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。在视频流的处理与分发上,视频监控汇聚平台EasyCVR的性能也同样表现得很优秀,平台可对外分

    2024年02月12日
    浏览(10)
  • vue实现监控视频直播

    vue实现监控视频直播

    vue实现监控视频直播 要想使用videojs我们势必是需要安装videojs的, 而且在生产环境中我们也需要依赖它, 所以如下 我们打开Vue工程中的主入口 main.js 进行引入 创建监控视频九宫格 创建视频容器 虽然是遍历视频容器组件,但是监控视频只播放第一个,所以这里创建视频容器时

    2024年02月11日
    浏览(9)
  • vue3中使用TcPlayer实现视频监控-代码

    1、引入js文件 在index.html头部引入引入播放器的cdn  2、主要代码 这里主要是监控画面的布局通过点击按钮使用grid布局进行切换,tcplayer.js的初始化就不展示了  3、视频监控-效果图

    2024年02月09日
    浏览(5)
  • VUE+webrtc-streamer实现实时视频播放(监控设备-rtsp)

    VUE+webrtc-streamer实现实时视频播放(监控设备-rtsp)

    首先说明目前我只完成了本地测试,因还没确定技术选型所以暂无项目应用,先做一下储备,后续项目应用的话这篇文章会持续更新。 监控设备播放效果如下:基于公司环境测试了大华和海康的监控设备(H264编码)可以正确播放 1、下载webrtc-streamer,本机测试我下载的最新

    2024年02月01日
    浏览(10)
  • VUE+webrtc-streamer 实现实时视频播放(监控设备-rtsp)

    VUE+webrtc-streamer 实现实时视频播放(监控设备-rtsp)

    下图则启动成功,此时在浏览器访问127.0.0.1:8000可以看到本机监控画面 地址:https://github.com/mpromonet/webrtc-streamer/releases 注意:第三步在本机启动服务所以 new WebRtcStreamer(id,serverUrl)中URL传入127.0.0.1端口可 文章参考链接:https://blog.csdn.net/liona_koukou/article/details/126605137

    2024年02月02日
    浏览(15)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包