Потоковое видео с Raspberry Pi на веб-сервере для нескольких пользователей - PullRequest
1 голос
/ 22 апреля 2020

У меня есть Raspberry Pi, использующий Wifi, он работает с моделью подсчета людей и отправит обработанное изображение на мой сервер с помощью сокета ZeroMQ. На этом сервере я строю веб-сервер, используя Flask. Я получил ошибку, которая не отображается во время потоковой передачи для первого человека, который получил доступ к веб-сайту, но следующий доступ после первого не удастся:

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_)

Я пропустил некоторый код, чтобы его было легче увидеть

1 Ответ

0 голосов
/ 22 апреля 2020

Добро пожаловать в Art of Zen-of-Zero.


В случае, если вы никогда не работали с ZeroMQ,
здесь можно получить первый взгляд на " ZeroMQ Принципы менее чем за Пять секунд"
, прежде чем углубляться в дальнейшие детали



I не могу говорить о деталях, скрытых внутри веб-пересылки / доставки Flask() видео, но часть ZeroMQ кажется проблемой, поэтому:

a)
Кажется, что каждый веб-наблюдатель порождает другой @app.route( '/video_feed' ) -декорированный Flask() -экземпляр (проверьте режим - если только на основе потоков, или на основе процессов - это важно, может ли центральный экземпляр Context() использоваться или не использоваться совместно и эффективно использоваться повторно.

b)
код, находящийся внутри, подключенный Response() - feeder, пытается .bind() для каждого последующего Flask() обслуживаемого пользователя, который по очевидным причинам сталкивается, так как ресурс (address:port) был POSACK 'исключен Полезное использование для посетителя, пришедшего первым, первым обслуженным (любой следующий должен взломать sh и разбиться, как указано выше).


ПРОФЕССИОНАЛЬНОЕ РЕШЕНИЕ (или просто быстрое но - грязный -фикс? ) :

Для действительно небольших масштабов использования достаточно будет поменять .bind() / .connect() -s т. е. PUB будет .bind() (так как он не воспроизводит себя и может обслуживать любые небольшие подсчеты в дальнейшем SUB (s), поступающих через .connect() -s)

Однако, это быстрое -фикс немного грязный Это никогда не должно быть сделано в системе производственного класса. Объем рабочей нагрузки скоро вырастет выше разумного уровня. Надлежащая архитектура должна работать по-другому.

  • создает центральный экземпляр zmq.Context( configs.nIOthreads ) (может использоваться совместно для повторного использования в вызываемых методах), имея, таким образом, также центральное масштабирование nIOthreads по соображениям производительности.

  • создает единичный, центральный SUB -экземпляр, который связывается с Rpi и собирает видеокадры, чтобы избежать повторного дублирования изображения / обслуживающий

  • настроить обе стороны на использование .setsockopt( zmq.CONFLATE ), чтобы принципиально избежать повторной трансляции видео "История без значения"

  • создает экземпляры всех @app декорированных Flask() инструментов для повторного использования центрального Context() экземпляра и для внутреннего подключения к центральному видео- re- PUB -lisher, используя сторону SUB другого архетипа PUB/SUB, здесь, используя эффективную карту памяти inproc://, снова используя .setsockopt( zmq.CONFLATE )

  • настроить все ресурсы, чтобы иметь более высокую надежность - по крайней мере, с явным * 1 107 *

  • обнаруживает и обрабатывает все состояния ошибок, поскольку API ZeroMQ документирует их все достаточно хорошо, чтобы помочь вам профессионально и безопасно диагностировать + управлять любым сбойным состоянием

...