Проблема с зависанием веб-просмотра RTSPPython

Программы на Python
Ответить
Anonymous
 Проблема с зависанием веб-просмотра RTSP

Сообщение Anonymous »

Я пытаюсь визуализировать изображение с камеры 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
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»