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

Debian 12服务器上Flask渲染RTSP摄像头流10秒后冻结的问题求助

Debian 12服务器上Flask渲染RTSP摄像头流10秒后冻结的问题求助

我现在在尝试用Flask模板渲染RTSP摄像头流,在Windows本地运行时视频完全正常,但部署到Debian 12的Linux服务器上就出问题了——视频大概运行10秒就会冻结,之后必须断开重连才能恢复。有没有人遇到过类似问题并解决了?

PS:服务器系统是Debian 12。


threaded_camera.py

import cv2
import time
import logging
import os
from threading import Thread, Event
from urllib.parse import urlparse


logging.basicConfig(level=logging.INFO)

class ThreadedCamera(object):
    def __init__(self, src):
        self.capture = cv2.VideoCapture(src, cv2.CAP_FFMPEG)
        if not self.capture.isOpened():
            print(f"Failed to open stream: {src}")
        self.src = src
        self.hostname = self.extract_hostname(src)
        self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 1)
        self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        self.capture.set(cv2.CAP_PROP_FPS, 30)
        self.min_fps = 10
        self.FPS = 1/30
        self.FPS_MS = int(self.FPS * 1000)
        self.status, self.frame = self.capture.read()
        self.frame_count = 0
        self.total_time = 0
        self.origin_info = {
            "width": 0,
            "height": 0,
            "fps": 0,
            "codec": '',
            "latency": False,
            "processing_time": 0
        }
        self.transcoded_info = {
            "width": 640,
            "height": 480,
            "fps": 30,
            "codec": 'mjpg',
            "latency": False,
            "processing_time": 0
        }

        # Start frame retrieval thread
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()

    def extract_hostname(self, url):
        parsed_url = urlparse(url)
        return parsed_url.hostname

    def update(self):
        while True:
            if self.capture.isOpened():
                start_time = time.time()
                self.status, self.frame = self.capture.read()
                network_latency = self.check_network_latency(self.hostname)
                end_time = time.time()

                logging.info(f"Status: {self.status}, Start Time: {start_time}, End Time: {end_time}, Duration: {end_time - start_time}")

                self.frame_count += 1
                self.total_time += (end_time - start_time)


                if self.status:
                    logging.info(f"Frame Count: {self.frame_count}, Total Time: {self.total_time}")

                    # Update origin info
                    self.origin_info['width'] = self.capture.get(cv2.CAP_PROP_FRAME_WIDTH)
                    self.origin_info['height'] = self.capture.get(cv2.CAP_PROP_FRAME_HEIGHT)
                    self.origin_info['fps'] = self.capture.get(cv2.CAP_PROP_FPS)
                    codec = int(self.capture.get(cv2.CAP_PROP_FOURCC))
                    self.origin_info['codec'] = ''.join([chr((codec >> 8 * i) & 0xFF) for i in range(4)])
                    self.origin_info['latency'] = network_latency

                if self.frame_count % int(self.capture.get(cv2.CAP_PROP_FPS)) == 0:
                    logging.info(f"Frame Count Reached FPS Interval: {self.frame_count}")
                    avg_processing_time = self.total_time / self.frame_count if self.frame_count > 0 else 0

                    logging.info(f"Average Processing Time: {avg_processing_time}, Network Latency: {network_latency}")

                    self.adjust_stream_settings(avg_processing_time, network_latency)
                    self.transcoded_info['latency'] = network_latency
                    self.transcoded_info['processing_time'] = round(avg_processing_time * 1000, 4)

                    logging.info(f"Transcoded Info Updated: {self.transcoded_info}")

                    # Reset frame count and total time for next calculation period
                    self.frame_count = 0
                    self.total_time = 0

                time.sleep(self.FPS)

    def check_network_latency(self, hostname):
        # response = os.system(f"ping -c 1 {hostname}")
        # return response == 0
        return 1

    def get_frame(self):
        if self.frame is not None:
            self.frame = self.resize_frame(self.frame, width=640)
            _, buffer = cv2.imencode('.webp', self.frame)
            self.transcoded_info['width'] = self.frame.shape[1]
            self.transcoded_info['height'] = self.frame.shape[0]
            return buffer.tobytes()
        return None

    def stop(self):
        self.stopped.set()
        self.thread.join()
        self.capture.release()

    def resize_frame(self, frame, width):
        height = int(width * (frame.shape[0] / frame.shape[1]))
        return cv2.resize(frame, (width, height))


    def adjust_stream_settings(self, avg_processing_time, network_latency):
        current_fps = self.capture.get(cv2.CAP_PROP_FPS)
        if avg_processing_time > 0.1 or not network_latency:
            new_fps = max(self.min_fps, current_fps - 5)
        else:
            new_fps = current_fps + 5  # Incremento mais suave

        self.capture.set(cv2.CAP_PROP_FPS, new_fps)
        # Ensure minimum FPS
        if self.capture.get(cv2.CAP_PROP_FPS) < self.min_fps:
            self.capture.set(cv2.CAP_PROP_FPS, self.min_fps)
        self.FPS = 1 / self.capture.get(cv2.CAP_PROP_FPS)
        self.FPS_MS = int(self.FPS * 1000)

    def get_origin_stream_info(self):
        logging.info(f"get_origin_stream_info: {self.transcoded_info}")
        return self.origin_info

    def get_transcoded_stream_info(self):
        logging.info(f"get_transcoded_stream_info: {self.transcoded_info}")
        return self.transcoded_info

stream_routes.py

from flask import Blueprint, Response, request, stream_with_context, current_app, jsonify, redirect, url_for
from threaded_camera import ThreadedCamera
import logging

bp = Blueprint('stream', __name__)
cameras = {}

@bp.route('/stream')
def stream():
    try:
        rtsp_url = request.args.get('rtsp_url')
        if not rtsp_url:
            return "RTSP URL not set", 400

        if rtsp_url not in cameras:
            cameras[rtsp_url] = ThreadedCamera(rtsp_url)

        camera = cameras[rtsp_url]

        def generate():
            while True:
                try:
                    frame = camera.get_frame()
                    if frame:
                        yield (b'--frame\r\n'
                               b'Content-Type: image/webp\r\n\r\n' + frame + b'\r\n')
                except Exception as e:
                    current_app.logger.error(f"Error generating frame: {e}")
                    break

        return Response(stream_with_context(generate()), mimetype='multipart/x-mixed-replace; boundary=frame')

    except Exception as e:
        current_app.logger.error(f"Error in /stream route: {e}")
        return "Internal Server Error", 500

@bp.route('/stop')
def stop_stream():
    try:
        rtsp_url = request.args.get('rtsp_url')
        if not rtsp_url:
            return "RTSP URL not set", 400

        if rtsp_url in cameras:
            cameras[rtsp_url].stop()
            del cameras[rtsp_url]

        return redirect(url_for('view_stream'))
    except Exception as e:
        current_app.logger.error(f"Error in /stop route: {e}")
        return redirect(url_for('view_stream'))

@bp.route('/stream_info')
def stream_info():
    try:
        rtsp_url = request.args.get('rtsp_url')
        if not rtsp_url:
            return jsonify(error="RTSP URL not set"), 400

        camera = ThreadedCamera(rtsp_url)
        origin_info = camera.get_origin_stream_info()
        trans_info = camera.get_transcoded_stream_info()

        return jsonify(
            origin=origin_info,
            transcoded=trans_info
        )
    except Exception as e:
        logging.error(f"Error in stream_info: {e}")
        return jsonify(error="Internal Server Error"), 500

Gunicorn日志

(ivia_dev) root@srv527791:/var/www/ivia_dev# journalctl -u app.service -f
Dec 09 04:28:11 srv527791 systemd[1]: Started app.service - Gunicorn instance to serve your application.
Dec 09 04:28:11 srv527791 gunicorn[469]: [2024-12-09 04:28:11 +0000] [469] [INFO] Starting gunicorn 23.0.0
Dec 09 04:28:11 srv527791 gunicorn[469]: [2024-12-09 04:28:11 +0000] [469] [INFO] Listening at: unix:/var/www/ivia_dev/app.sock (469)
Dec 09 04:28:11 srv527791 gunicorn[469]: [2024-12-09 04:28:11 +0000] [469] [INFO] Using worker: sync
Dec 09 04:28:11 srv527791 gunicorn[493]: [2024-12-09 04:28:11 +0000] [493] [INFO] Booting worker with pid: 493
Dec 09 04:28:11 srv527791 gunicorn[495]: [2024-12-09 04:28:11 +0000] [495] [INFO] Booting worker with pid: 495
Dec 09 04:28:11 srv527791 gunicorn[500]: [2024-12-09 04:28:11 +0000] [500] [INFO] Booting worker with pid: 500
Dec 09 04:30:32 srv527791 gunicorn[469]: [2024-12-09 04:30:32 +0000] [469] [CRITICAL] WORKER TIMEOUT (pid:493)
Dec 09 04:30:32 srv527791 gunicorn[493]: [2024-12-09 04:30:32 +0000] [493] [ERROR] Error handling request /stream?rtsp_url=htt

备注:内容来源于stack exchange,提问作者Demon0102

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 03:23:25