You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask MJPEG流加载耗时过长问题排查求助

问题:网页摄像头图像加载延迟严重

参考Miguel的博客实现了一个在网页显示摄像头捕获图像的类(包含图像处理步骤),近期出现网站加载异常:index.html中的静态内容能直接显示,但图像需要30秒到1分钟才开始展示。

编写了简化版代码排查,但仍未找到问题根源,相关代码如下:

Python 代码

import flask
import cv2
import multiprocessing as mp
import numpy as np
import time
import select
import sys

## class display
#
# - show images on html site
# - saves images for logging
class display:

    ##constructor
    # - starts separate process for the website
    def __init__(self):
        self.__controleQueue = mp.Queue()
        self.__imageQueue = mp.Queue()
        self.__imageSemaphore = mp.Semaphore(30)
        self.imgdelete = 0

        print("start controle process")
        self.__controleProcess = mp.Process(target=self.__startApp)
        self.__controleProcess.start()
        print("class initialized")


    ## destructor
    def exit(self):
        print("stop process")
        self.__controleQueue.put("exit")
        while True:
            print("exit loop main")
            if self.__controleQueue.get() == "exit done":
                print("process stopped")
                break
            else:
                time.sleep(1)
        return True

    ## website app
    # - called by constructor as separate process
    # - starts website as separate process
    # - waits for message to stop website
    def __startApp(self):
        print("create app")
        self.app = self.__createApp()
        
        ip = "192.168.62.144"
        print("ip for display website: ", ip)

        print("start working process/app")
        self.__workingProcess = mp.Process(target=self.app.run, kwargs={"host":ip, "port":"80", "debug":False, "threaded":True, "use_reloader":False})
        self.__workingProcess.deamon = True
        self.__workingProcess.start()
        print("working process/app started, waiting for commands")

        while True:
            if self.__controleQueue.get() == "exit":
                print("stop working process")
                if self.__workingProcess.is_alive():
                    self.__workingProcess.terminate()
                self.__workingProcess.join(2)
                while self.__workingProcess.is_alive():
                    time.sleep(0.01)
                print("working process stopped")
                self.__controleQueue.put("exit done")
                break
            else:
                time.sleep(0.1)

        return

    ## new image
    # - send new image to display
    #
    # @param image image to show on website
    def newImage(self, image):
        # self.logger.debug("new image")
        # if len(image.shape) > 2:
        #   image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
        # self.__imageQueue.put(image)
        if self.__imageSemaphore.acquire(block=False):
        #   # print("semaphore yes")
            if len(image.shape) > 2:
                image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
            self.__imageQueue.put(image)
        else:
            print("								semaphore full")
            self.imgdelete += 1
            print("					image delete: ", self.imgdelete)

    ## generater function to prepare new image
    # - receives image from queue
    # - resizes image to defined maximum size
    # - returns encoded image
    def __generate(self):
        print("__generate call")
        fpsCounter = 0
        fpsTime=time.time()
        sleeptime = 0.0001
        while True:
            print("loop 	"+str(fpsCounter)+"	"+str(time.time()-fpsTime))
            fpsCounter += 1
            if fpsCounter == 3200:
                sleeptime=1
            # if fpsCounter == 1010:
            #   sleeptime=0.0001
            # if fpsCounter == 1000:
            #   sleeptime=1
            time.sleep(sleeptime)
            output = self.__imageQueue.get()
            self.__imageSemaphore.release()
            # output = np.ones((500,500,3), dtype = np.uint8)*255
            cv2.putText(output, "test display "+str(fpsCounter), (0, output.shape[0]-10), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,0,0), 2)

            # self.logger.debug("yes new image")
            flag, frame = cv2.imencode(".jpg", output)
            if not flag:
                print("shit")
                continue

            yield(b'--frame\r\nContent-Type: image/jpeg\r\n\r\n' + frame.tobytes() + b'\r\n')

    ## start website app
    # - create app
    # - setup routes
    def __createApp(self):
        # app = flask.Flask("cam1", template_folder='/home/root/camino/templates')
        app = flask.Flask("test display", static_folder='tests')

        # render html page
        @app.route("/")
        def index():
            return flask.render_template("2index.html")
            # return "Congratulations, it's a web app!"

        @app.route("/video_feed")
        def videoFeed():
            # print("videoFeed: ", self.__getCurrentMemoryUsage(), " MB")
            return flask.Response(self.__generate(), mimetype = "multipart/x-mixed-replace; boundary=frame")
        return app

if __name__ == "__main__":

    disp = display()

    sizex = 1500
    sizey = 100
    image = np.zeros((sizey,sizex,3),dtype=np.uint8)
    row = 0
    col = 0
    step = 10
    change = True
    while True:
        if change:
            image[row:row+step, col:col+step,:] = 255
            col += step
            if col >= sizex:
                col = 0
                row += step
                if row >= sizey:
                    row = 0
                    image = np.zeros((sizey,sizex,3),dtype=np.uint8)
                    print("new image done")

        disp.newImage(image)
        time.sleep(0.02)

        i,o,e = select.select( [sys.stdin], [], [], 0.001)
        # print(row, col, "terminal read", i)
        if i:
            key = sys.stdin.readline().strip()
            print(key)
            if key == 'q':
                break
            if key == 'c':
                change = not change

    disp.exit()
    print("end")

HTML 代码(2index.html)

<html>
  <head>
    <link rel="icon" href="data:,">
    <title>test display</title>
  </head>
  <body>
    <h1>Welcome to FlaskApp!</h1>
    <h2>2index.html</h2>
    <img src="{{ url_for('videoFeed') }}">
  </body>
</html>

已做排查

  • 测试排除了队列、信号量、进程间通信的问题
  • 本地测试发现浏览器大约在第3180张图像时开始显示,该数值与图像大小相关
  • 测试Miguel的原始示例,将图像间隔从1秒改为10ms后,首次图像显示需3-4分钟
  • Firefox请求日志显示服务器立即收到GET /video_feed请求,但需等待数分钟才返回首帧

服务运行在计算模块上,通过本地网络访问,此前系统运行正常,恳请提供排查思路。

内容的提问来源于stack exchange,提问作者chillking

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:12:07