Anonymous
Проблема с зависанием веб-просмотра RTSP
Сообщение
Anonymous » 09 дек 2024, 13:34
Я пытаюсь визуализировать изображение с камеры RTSP в шаблоне Flask.
Когда я запускаю его локально в Windows, видео работает отлично. Однако проблема возникает, когда я пытаюсь запустить его на своем сервере Linux.
На сервере видео воспроизводится примерно 10 секунд, а затем зависает. После этого мне нужно отключиться и снова подключиться к потоку, чтобы он снова заработал.
Кто-нибудь сталкивался с этой проблемой и сумел ее решить?
PS: Linux: 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:
# self.frame_count += 1
# self.total_time += (end_time - start_time)
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'] = self.check_network_latency(self.hostname)
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
# network_latency = self.check_network_latency(self.hostname)
logging.info(f"Average Processing Time: {avg_processing_time}, Network Latency: {network_latency}")
self.adjust_stream_settings(avg_processing_time, network_latency)
# self.adjust_stream_settings(avg_processing_time, 0 )
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
Журналы гуикона
Код: Выделить всё
(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=https://obrasaovivo.sinfra.mt.gov.br/contorno/index.m3u8
Dec 09 04:30:32 srv527791 gunicorn[493]: Traceback (most recent call last):
Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/gunicorn/workers/sync.py", line 134, in handle
Dec 09 04:30:32 srv527791 gunicorn[493]: self.handle_request(listener, req, client, addr)
Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/gunicorn/workers/sync.py", line 182, in handle_request
Dec 09 04:30:32 srv527791 gunicorn[493]: for item in respiter:
Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/werkzeug/wsgi.py", line 256, in __next__
Dec 09 04:30:32 srv527791 gunicorn[493]: return self._next()
Dec 09 04:30:32 srv527791 gunicorn[493]: ^^^^^^^^^^^^
Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/werkzeug/wrappers/response.py", line 32, in _iter_encoded
Dec 09 04:30:32 srv527791 gunicorn[493]: for item in iterable:
Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/flask/helpers.py", line 113, in generator
Dec 09 04:30:32 srv527791 gunicorn[493]: yield from gen
Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/stream_routes.py", line 24, in generate
Dec 09 04:30:32 srv527791 gunicorn[493]: frame = camera.get_frame()
Dec 09 04:30:32 srv527791 gunicorn[493]: ^^^^^^^^^^^^^^^^^^
Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/threaded_camera.py", line 110, in get_frame
Dec 09 04:30:32 srv527791 gunicorn[493]: _, buffer = cv2.imencode('.webp', self.frame)
Dec 09 04:30:32 srv527791 gunicorn[493]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/gunicorn/workers/base.py", line 204, in handle_abort
Dec 09 04:30:32 srv527791 gunicorn[493]: sys.exit(1)
Dec 09 04:30:32 srv527791 gunicorn[493]: SystemExit: 1
Dec 09 04:30:32 srv527791 gunicorn[493]: [2024-12-09 04:30:32 +0000] [493] [INFO] Worker exiting (pid: 493)
Dec 09 04:30:32 srv527791 gunicorn[586]: [2024-12-09 04:30:32 +0000] [586] [INFO] Booting worker with pid: 586
Я пытаюсь предотвратить зависание видео при возникновении этого исключения, но мне это не удается.
Подробнее здесь:
https://stackoverflow.com/questions/792 ... view-issue
1733740466
Anonymous
Я пытаюсь визуализировать изображение с камеры RTSP в шаблоне Flask. Когда я запускаю его локально в Windows, видео работает отлично. Однако проблема возникает, когда я пытаюсь запустить его на своем сервере Linux. На сервере видео воспроизводится примерно 10 секунд, а затем зависает. После этого мне нужно отключиться и снова подключиться к потоку, чтобы он снова заработал. Кто-нибудь сталкивался с этой проблемой и сумел ее решить? PS: Linux: Debian 12. [b]threaded_camera.py[/b] [code]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: # self.frame_count += 1 # self.total_time += (end_time - start_time) 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'] = self.check_network_latency(self.hostname) 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 # network_latency = self.check_network_latency(self.hostname) logging.info(f"Average Processing Time: {avg_processing_time}, Network Latency: {network_latency}") self.adjust_stream_settings(avg_processing_time, network_latency) # self.adjust_stream_settings(avg_processing_time, 0 ) 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 [/code] [b]stream_routes.py[/b] [code]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 [/code] [b]Журналы гуикона[/b] [code](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=https://obrasaovivo.sinfra.mt.gov.br/contorno/index.m3u8 Dec 09 04:30:32 srv527791 gunicorn[493]: Traceback (most recent call last): Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/gunicorn/workers/sync.py", line 134, in handle Dec 09 04:30:32 srv527791 gunicorn[493]: self.handle_request(listener, req, client, addr) Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/gunicorn/workers/sync.py", line 182, in handle_request Dec 09 04:30:32 srv527791 gunicorn[493]: for item in respiter: Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/werkzeug/wsgi.py", line 256, in __next__ Dec 09 04:30:32 srv527791 gunicorn[493]: return self._next() Dec 09 04:30:32 srv527791 gunicorn[493]: ^^^^^^^^^^^^ Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/werkzeug/wrappers/response.py", line 32, in _iter_encoded Dec 09 04:30:32 srv527791 gunicorn[493]: for item in iterable: Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/flask/helpers.py", line 113, in generator Dec 09 04:30:32 srv527791 gunicorn[493]: yield from gen Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/stream_routes.py", line 24, in generate Dec 09 04:30:32 srv527791 gunicorn[493]: frame = camera.get_frame() Dec 09 04:30:32 srv527791 gunicorn[493]: ^^^^^^^^^^^^^^^^^^ Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/threaded_camera.py", line 110, in get_frame Dec 09 04:30:32 srv527791 gunicorn[493]: _, buffer = cv2.imencode('.webp', self.frame) Dec 09 04:30:32 srv527791 gunicorn[493]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Dec 09 04:30:32 srv527791 gunicorn[493]: File "/var/www/ivia_dev/ivia_dev/lib/python3.11/site-packages/gunicorn/workers/base.py", line 204, in handle_abort Dec 09 04:30:32 srv527791 gunicorn[493]: sys.exit(1) Dec 09 04:30:32 srv527791 gunicorn[493]: SystemExit: 1 Dec 09 04:30:32 srv527791 gunicorn[493]: [2024-12-09 04:30:32 +0000] [493] [INFO] Worker exiting (pid: 493) Dec 09 04:30:32 srv527791 gunicorn[586]: [2024-12-09 04:30:32 +0000] [586] [INFO] Booting worker with pid: 586 [/code] Я пытаюсь предотвратить зависание видео при возникновении этого исключения, но мне это не удается. Подробнее здесь: [url]https://stackoverflow.com/questions/79264642/freezing-rtsp-web-view-issue[/url]