Node.js + Python 用WebSocket实现视频流推送

举报
jackwangcumt 发表于 2021/12/06 21:10:30 2021/12/06
【摘要】 视频流可以通过OpenCV从摄像头进行获取,视频流可以抽取出每一个帧,一般来说,如果一个视频的fps为24,那么则意味着1秒中播放24帧,而每一帧就是一副图像,因此,我们可以从视频流中抽取出图像帧,并对图像进行处理,比如大小跳转,绘制边框等,然后可以将其通过base64转换成字符串,通过WebSocket发送到服务器,而服务器则通过WebSocket协议推送到浏览器,并基于img标签进行显示。

1 Node.js概述


        根据百度百科的定义,Node.js是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型, 让JavaScript 成为一种不但可以运行在浏览器端,还可以运行在服务端的编程语言,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。Node.js是单线程的,它通过事件循环(event loop)来实现并发操作,因此,编程时尽可能多使用非阻塞操作。

2 WebSocket


       根据百度百科的定义,WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。因此,目前对于Web端消息推送应用,很多都采用WebSocket,它能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

3 视频流推送实现


      视频流可以通过OpenCV从摄像头进行获取,视频流可以抽取出每一个帧,一般来说,如果一个视频的fps为24,那么则意味着1秒中播放24帧,而每一帧就是一副图像,因此,我们可以从视频流中抽取出图像帧,并对图像进行处理,比如大小跳转,绘制边框等,然后可以将其通过base64转换成字符串,通过WebSocket发送到服务器,而服务器则通过WebSocket协议推送到浏览器,并基于img标签进行显示。

      本示例核心组件如下所示:

     (1)Node.js 构建的server.js 是 WebSocket Server端;
     (2)Python 构建的client.py 则获取视频流并推送base64编码后的图像到Server端;
     (3)Python Flask构建的web.py,返回index.html页面,接收WebSocket协议推送过来的图像,并显示。

     项目文件结构如下图所示:

8.jpg

     下面给出server.js 核心代码,具体如下所示:

const PORT = 8168

// npm install ws
let WebSocketServer = require('ws').Server;

let wss = new WebSocketServer({
     port: PORT 
    });

wss.on('connection', function (ws) {
    console.log('Client Connected.');
    ws.on('message', function (msg) {
        wss.clients.forEach(function each(client) {
            //console.log(client)
            client.send(msg);
        });
    });
});

//node server.js

     其中的WebSocket协议需要通过 npm install ws 进行安装。而wss.on方法可以监听客户端的连接,并通过循环客户端列表来发送数据,即client.send(msg)。下面再给出Python 构建的client.py核心代码,具体如下所示:

import asyncio
import websockets
import base64
from cv2 import cv2
import numpy as np
import configparser
#pip install websockets
capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
if not capture.isOpened():
    print('no video')
    quit()
ret, frame = capture.read()
encode_param=[int(cv2.IMWRITE_JPEG_QUALITY),80]

#Send image to node server
async def send_msg(websocket):
    global ret,frame
    while ret:
        result, imgencode = cv2.imencode('.jpg', frame, encode_param)
        data = np.array(imgencode)
        #img = data.tostring()
        img = data.tobytes()
        img = base64.b64encode(img).decode()
        await websocket.send("data:image/jpeg;base64," + img)
        ret, frame = capture.read()

async def main():
    cf = configparser.ConfigParser()
    cf.read("config.ini") 
    WS_HOST = cf.get("websocket", "host")
    PORT = cf.get("websocket", "port")
    WS_URL = 'ws://'+WS_HOST+':'+str(PORT)
    print(WS_URL)
    async with websockets.connect(WS_URL) as websocket:
        await send_msg(websocket)

asyncio.get_event_loop().run_until_complete(main())

# python client.py

     这里需要构建Python环境,且需要安装多种库,比如 pip install websockets 安装WebSocket协议库,而import cv2则需要执行pip install opencv-python 命令来安装opencv python库。同理,pip install numpy也是需要执行的。import configparser 可以解析 ini文件,其中有一些配置,如下所示:

[websocket]
host = 192.168.0.107
port = 8168

      最后,给出Python Flask构建的web.py,返回index.html页面,核心代码如下所示:

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.debug = True
    app.run(host="0.0.0.0",port=5000)

     其中需要执行pip install Flask来导入flask库,默认的路由返回templates目录下的index.html,其代码如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>View Video</title>
</head>
<body>
<div>
    <img id="video" src="" />
</div>
<script src="/static/js/jquery-3.3.1.min.js" ></script>
<script>
        let wss_url = "ws://192.168.0.107:8168/";
        let ws = new WebSocket(wss_url);
        ws.onopen = function(evt) {
            console.log(">>> ws.onopen.");
            //ws.send("Test");
        };

        ws.onmessage = function(evt) {  
            //get PromiseResult from Promise
            var base64 = evt.data.text().then(
                res => {
                   //console.log(res);
                   $("#video").attr("src",res);
                }
            )
        };

        ws.onclose = function(evt) {
            console.log(">>> ws.onclose.");
        };


</script>
</body>
</html>

    至此,我们依次执行如下命令,则可以启动该示例:

node server.js
python client.py
python web.py

    用浏览器打开网址 http://192.168.0.107:5000  则显示的界面如下所示:

7.jpg


    

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。