将网络服务器上 Raspberry Pi 的视频流式传输给多个用户

Streaming video from Raspberry Pi on web server to multiple users

我有一个 Raspberry Pi,使用 Wifi,它是 运行 一个人数统计模型,并将使用 ZeroMQ 套接字将处理后的图像发送到我的服务器。在这台服务器上,我使用 Flask 构建了一个 Web 服务器。我收到一个错误,该错误在第一位访问该网站的人的流式传输期间未显示,但第一次访问后的下一次访问将失败:

zmq.error.ZMQError: Address in use

我应该怎么做才能让更多人访问我的服务器并可以看到视频流?

服务器代码:

from flask         import Flask, render_template, Response, request, jsonify
from flask_restful import Api
import numpy as np
from api.configs   import configs
from flask_cors    import CORS
import zmq
import cv2
import base64

app = Flask(__name__, static_url_path='/static')
api_restful = Api(app)
cors = CORS(app)

def gen():
    context = zmq.Context()
    footage_socket = context.socket(zmq.SUB)
    footage_socket.bind('tcp://*:{}'.format(configs.SOCKET_PORT))
    footage_socket.setsockopt_string(zmq.SUBSCRIBE, np.unicode(''))
    while True:
        frame = footage_socket.recv()
        npimg = np.fromstring(frame, dtype=np.uint8)
        source = cv2.imdecode(npimg, 1)
        s = cv2.imencode('.jpg', source)[1].tobytes()
        yield ( b'--frame\r\n'
                b'Content-Type: image/jpeg\r\n\r\n' + s + b'\r\n' )

@app.route('/video_feed')
def video_feed():
    return Response( gen(),
                     mimetype = 'multipart/x-mixed-replace; boundary=frame'
                     )

if __name__ == '__main__':
    app.run( debug    = True,
             threaded = True,
             port     = configs.PORT,
             host     = configs.HOST
             )

Raspberry Pi客户端代码:

import socket
import logging as log
import numpy as np
from api.configs import configs
import zmq
import numpy as np
import cv2

context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.connect( 'tcp://{}:{}'.format( configs.SERVER_HOST,
                                              configs.SOCKET_PORT )
                         )
time.sleep(1)
cap = cv2.VideoCapture(0)
while True:
    while cap.isOpened():
        ret, frame = cap.read()
        frame = cv2.resize(frame, (640, 480))
        encoded, buffer_ = cv2.imencode('.jpg', frame)
        footage_socket.send(buffer_)

我省略了一些代码以便于查看

欢迎来到零之禅的艺术。


如果你从未使用过 ZeroMQ,
你可能会喜欢先看看
,然后再深入了解更多细节



我不能谈论隐藏在 Flask() 网络服务视频 re-wrapping/delivery 中的细节,但 ZeroMQ 部分似乎是问题所在,所以:

a )
似乎每个网络观察者都会生成另一个 @app.route( '/video_feed' )-decorated Flask()-实例(检查模式——如果只是基于线程,或基于进程——重要的是,中央 Context() 实例是否可以获取共享并有效地重复使用)。

b )
代码,驻留在那里钩住 Response()-feeder,尝试 .bind() 为每个后续 Flask()-服务的用户,由于明显的原因会发生冲突,因为资源( address:port )已被 POSACK 专供先到先得的访问者使用(任何下一个必须崩溃并如上文所述崩溃)。


专业解决方案(或者只是一个快速但--修复?) :

对于确实小规模的用例,反转.bind()/.connect()[=99=就足够了]-s,即 PUB.bind()(因为它不会自我复制,并且可以服务任何少量的进一步 SUB(s),通过 .connect()-s 到达)

但是,这个快速修复有点脏。这绝不应该在生产级系统中完成。工作量范围很快就会超过合理水平。正确的架构应该以不同的方式工作。

  • 实例化一个中央 zmq.Context( configs.nIOthreads ) 实例(可以共享以在调用的方法中重复使用),因此出于性能原因也具有 nIOthreads 的中央缩放。

  • 实例化一个中央 SUB 实例,它与 Rpi 通信并收集视频帧,以避免 image/serving

    的任何重复
  • 配置双方使用.setsockopt( zmq.CONFLATE )主要是为了避免视频的任何重播 "History of No Value"

  • 实例化所有 @app-修饰的 Flask()-工具以重新使用中央 Context()-实例并在内部连接到中央视频重-PUB-lisher,使用另一个 PUB/SUB 原型的 SUB 端,这里使用高效的内存映射 inproc://,再次使用 .setsockopt( zmq.CONFLATE )

  • 配置所有资源以具有更高的稳健性 - 至少使用显式 .setsockopt( zmq.LINGER, 0 )

  • 检测并处理所有错误状态,因为 ZeroMQ API 很好地记录了所有错误状态,足以帮助您专业安全地诊断 + 管理任何故障状态