优化拉流丢包的问题,增加宽松策略

This commit is contained in:
martin 2026-01-29 05:59:39 +08:00
parent 9a09c1e1cf
commit 89181007c2
2 changed files with 278 additions and 33 deletions

View File

@ -1392,6 +1392,228 @@ import asyncio
from typing import Optional from typing import Optional
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
# 使用cv2 拉流避免了ffmpeg 拉流的rtmp延时3s的问题 # 使用cv2 拉流避免了ffmpeg 拉流的rtmp延时3s的问题
# async def read_rtmp_frames(
# loop,
# read_rtmp_frames_executor: ThreadPoolExecutor,
# video_url: str,
# device: Optional[MQTTDevice] = None,
# topic_camera_osd: Optional[str] = None,
# method_camera_osd: Optional[str] = None,
# topic_osd_info: Optional[str] = None,
# method_osd_info: Optional[str] = None,
# cancel_flag: Optional[asyncio.Event] = None,
# frame_queue: asyncio.Queue = None,
# timestamp_frame_queue=None
# ):
# """
# 基于 OpenCV+FFmpeg 读取 RTMP 流帧(优化版:高性能读取,处理损坏帧)
# ✅ 核心修改替换原FFmpeg子进程读流为 cv2.VideoCapture 读流,保留所有原有业务逻辑
# ✅ 核心优化:自适应分辨率、低延迟无残影、断流自动重连、超时保护
# """
# max_retries = 20
# retry_delay = 2
# pic_count = 0
# attempt = 0
# time_start = time.time_ns()
# frame_count = 0
#
# if cancel_flag is None:
# cancel_flag = asyncio.Event()
# if timestamp_frame_queue is None:
# timestamp_frame_queue = []
#
# print(f"开始读取RTMP流: {video_url}")
#
# while not cancel_flag.is_set() and attempt < max_retries:
# attempt += 1
# if cancel_flag.is_set():
# logger.info("收到停止信号,终止 RTMP 读取")
# break
#
# cap = None
# width, height = None, None
# stream_fps = None
#
# try:
# logger.info(f"尝试连接 RTMP 流 (尝试 {attempt}/{max_retries}): {video_url}")
#
# # ✅ 核心替换使用cv2.VideoCapture + CAP_FFMPEG 打开RTMP流最优参数配置
# # 切换到线程池执行opencv操作避免阻塞协程
# cap = await loop.run_in_executor(
# read_rtmp_frames_executor,
# lambda: cv2.VideoCapture(video_url, cv2.CAP_FFMPEG)
# )
# # 设置核心参数 - 重中之重,缺一不可
# await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 60000))
# await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_READ_TIMEOUT_MSEC, 50000))
# await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)) # 无缓存,低延迟无残影
# await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_FPS, 25))
#
# # 校验流是否成功打开
# is_opened = await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.isOpened())
# if not is_opened:
# logger.warning(f"尝试 {attempt} 次打开RTMP流失败准备重试")
# await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.release() if cap else None)
# await asyncio.sleep(retry_delay)
# continue
#
# # ✅ 自适应获取流的【真实分辨率】,无需手动探测,精准无误差
# width = await loop.run_in_executor(read_rtmp_frames_executor, lambda: int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
# height = await loop.run_in_executor(read_rtmp_frames_executor, lambda: int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
# stream_fps = await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.get(cv2.CAP_PROP_FPS))
#
# # 兜底分辨率,防止异常
# if width is None or height is None or width == 0 or height == 0:
# logger.warning("使用默认分辨率 1920x1080")
# width, height = 1920, 1080
#
# logger.info(f"视频分辨率: {width}x{height}, 流帧率: {stream_fps:.1f} FPS")
# logger.info(f"成功启动 OpenCV+FFmpeg 连接 RTMP 流: {video_url}")
#
# # 初始化帧读取状态 (保留原逻辑不变)
# frame_sequence = 0
# last_timestamp = time_start
# consecutive_corrupted_frames = 0 # 连续损坏帧计数
# max_consecutive_corrupted = 10 # 最大连续损坏帧数
#
# while not cancel_flag.is_set():
# try:
# # ✅ 核心替换使用cv2.read()读取帧,线程池执行避免阻塞协程
# ret, frame = await loop.run_in_executor(
# read_rtmp_frames_executor,
# lambda: cap.read()
# )
#
# current_time_ns = time.time_ns()
# frame_sequence += 1
# frame_count += 1
#
# # 处理帧数据(保留原逻辑完全不变,兼容原有的损坏帧处理)
# img = None
# is_corrupted = False
# print(f"读取 read_rtmp_frames 判断")
# try:
# if ret and frame is not None and frame.shape == (height, width, 3):
# # 完整有效帧处理
# img = frame.copy()
# consecutive_corrupted_frames = 0 # 重置连续损坏计数
# else:
# # 损坏帧/空帧处理
# logger.warning(f"帧数据损坏/空帧, 序列: {frame_sequence}")
# is_corrupted = True
# consecutive_corrupted_frames += 1
#
# # 创建替代帧,保留原逻辑
# if consecutive_corrupted_frames <= max_consecutive_corrupted:
# img = np.zeros((height, width, 3), dtype=np.uint8)
# else:
# img = np.zeros((height, width, 3), dtype=np.uint8)
# logger.error(f"连续损坏帧过多 ({consecutive_corrupted_frames}),创建空白帧")
#
# except Exception as frame_error:
# logger.error(f"帧数据处理错误: {frame_error}")
# # 创建空白帧作为后备,保留原逻辑
# img = np.zeros((height, width, 3), dtype=np.uint8)
# is_corrupted = True
# consecutive_corrupted_frames += 1
# print(f"读取 read_rtmp_frames 判断1")
#
# # 获取OSD信息 - 保留原逻辑完全不变
# osd_info = None
# if device and topic_osd_info and method_osd_info:
# try:
# osd_msg = device.get_latest_message(topic=topic_osd_info, method=method_osd_info)
# if osd_msg and hasattr(osd_msg, 'data'):
# osd_info = Air_Attitude(
# gimbal_pitch=osd_msg.data.gimbal_pitch,
# gimbal_roll=osd_msg.data.gimbal_roll,
# gimbal_yaw=osd_msg.data.gimbal_yaw,
# height=osd_msg.data.height,
# latitude=osd_msg.data.latitude,
# longitude=osd_msg.data.longitude
# )
# except Exception as osd_error:
# logger.warning(f"获取OSD信息失败: {osd_error}")
# print(f"读取 read_rtmp_frames 判断2")
#
# # 放入帧队列 - 保留原逻辑完全不变
# if img is not None and not frame_queue.full():
# # 确保时间戳递增
# if current_time_ns <= last_timestamp:
# current_time_ns = last_timestamp + 1
# last_timestamp = current_time_ns
#
# # 统计信息 - 保留原逻辑完全不变
# pic_count += 1
# if current_time_ns - time_start > 1000000000: # 1秒
# elapsed_seconds = (current_time_ns - time_start) / 1e9
# fps = pic_count / elapsed_seconds if elapsed_seconds > 0 else 0
# corrupted_rate = (consecutive_corrupted_frames / pic_count * 100) if pic_count > 0 else 0
# print(
# f"readFrames 序列:{frame_sequence} 帧数:{pic_count} FPS:{fps:.2f} 损坏率:{corrupted_rate:.1f}%")
# pic_count = 0
# time_start = current_time_ns
#
# print(f"读取 read_rtmp_frames 实时流")
#
# # 准备帧数据 - 保留原逻辑完全不变
# frame_data = {
# "sequence": frame_sequence,
# "frame": img,
# "osd_info": osd_info,
# "timestamp": current_time_ns,
# "is_corrupted": is_corrupted
# }
# time_ns = time.time_ns()
# if img is not None and osd_info is not None:
# await frame_queue.put((img, osd_info, time_ns))
# timestamp_frame_queue.append({
# "timestamp": time_ns,
# "frame": img
# })
#
# if frame_sequence % 100 == 0: # 每100帧输出一次日志
# logger.debug(f"已处理帧 序列:{frame_sequence} 累计:{frame_count}")
#
# elif frame_queue.full():
# logger.warning("帧队列已满,跳过此帧")
# await asyncio.sleep(0.001) # 短暂等待
#
# # 连续帧损坏触发重连前置判断 - 保留原逻辑
# if consecutive_corrupted_frames > max_consecutive_corrupted:
# logger.warning(f"连续{consecutive_corrupted_frames}帧损坏,触发流重连逻辑")
# break
#
# except Exception as e:
# logger.error(f"读取帧数据时出错: {e}", exc_info=True)
# break
#
# except Exception as e:
# logger.error(f"RTMP 流错误 (尝试 {attempt}/{max_retries}): {e}")
# if attempt < max_retries:
# await asyncio.sleep(retry_delay)
# else:
# raise RuntimeError(f"无法连接 RTMP 流 (尝试 {max_retries} 次后失败): {video_url}")
# finally:
# # ✅ 释放opencv的VideoCapture资源替代原FFmpeg进程的关闭逻辑
# if cap is not None:
# await loop.run_in_executor(
# read_rtmp_frames_executor,
# lambda: cap.release()
# )
# logger.info("OpenCV VideoCapture 资源已释放")
#
# if frame_count > 0:
# total_time = (time.time_ns() - time_start) / 1e9
# avg_fps = frame_count / total_time if total_time > 0 else 0
# print(f"RTMP流读取完成总帧数: {frame_count}, 总时间: {total_time:.2f}秒, 平均FPS: {avg_fps:.2f}")
# else:
# print("RTMP流读取失败未获取到任何帧")
#
# logger.info(f"RTMP 流已结束或被取消,累计处理帧数: {frame_count}")
async def read_rtmp_frames( async def read_rtmp_frames(
loop, loop,
read_rtmp_frames_executor: ThreadPoolExecutor, read_rtmp_frames_executor: ThreadPoolExecutor,
@ -1406,12 +1628,13 @@ async def read_rtmp_frames(
timestamp_frame_queue=None timestamp_frame_queue=None
): ):
""" """
基于 OpenCV+FFmpeg 读取 RTMP 流帧优化版高性能读取处理损坏帧 基于 OpenCV+FFmpeg 读取 RTMP 流帧兼容优化版解决URL格式错误+超时问题
核心修改替换原FFmpeg子进程读流为 cv2.VideoCapture 读流保留所有原有业务逻辑 核心优化改用FFmpeg环境变量传递容错参数提升兼容性
核心优化自适应分辨率低延迟无残影断流自动重连超时保护 修复问题解决流超时URL识别失败的问题提升连接成功率
保留功能低延迟丢包容错断流自动重连自适应分辨率
""" """
max_retries = 20 max_retries = 20
retry_delay = 2 retry_delay = 3 # 小幅增加重试间隔,避免频繁重试给服务端压力
pic_count = 0 pic_count = 0
attempt = 0 attempt = 0
time_start = time.time_ns() time_start = time.time_ns()
@ -1421,8 +1644,21 @@ async def read_rtmp_frames(
cancel_flag = asyncio.Event() cancel_flag = asyncio.Event()
if timestamp_frame_queue is None: if timestamp_frame_queue is None:
timestamp_frame_queue = [] timestamp_frame_queue = []
if frame_queue is None:
frame_queue = asyncio.Queue(maxsize=30) # 给队列设置默认容量,防止内存溢出
print(f"开始读取RTMP流: {video_url}") # ✅ 关键改动1通过FFmpeg环境变量传递容错参数兼容所有OpenCV/FFmpeg环境
# 这是比URL拼接更稳定的方式OpenCV底层FFmpeg会自动读取这些环境变量
os.environ['FFMPEG_FLAGS'] = (
"nobuffer,fastseek,discardcorrupt," # 核心容错:无缓冲、快速寻址、丢弃损坏帧
"low_delay," # 低延迟模式
"ignore_err," # 忽略解码错误
"max_delay=0" # 最大延迟设为0
)
# 额外设置FFmpeg的日志级别减少无关输出可选
os.environ['FFREPORT'] = "level=warning"
print(f"开始读取RTMP流兼容模式已配置FFmpeg容错参数: {video_url}")
while not cancel_flag.is_set() and attempt < max_retries: while not cancel_flag.is_set() and attempt < max_retries:
attempt += 1 attempt += 1
@ -1437,27 +1673,36 @@ async def read_rtmp_frames(
try: try:
logger.info(f"尝试连接 RTMP 流 (尝试 {attempt}/{max_retries}): {video_url}") logger.info(f"尝试连接 RTMP 流 (尝试 {attempt}/{max_retries}): {video_url}")
# ✅ 核心替换使用cv2.VideoCapture + CAP_FFMPEG 打开RTMP流最优参数配置 # ✅ 关键改动2恢复原始RTMP URL避免格式识别错误
# 切换到线程池执行opencv操作避免阻塞协程
cap = await loop.run_in_executor( cap = await loop.run_in_executor(
read_rtmp_frames_executor, read_rtmp_frames_executor,
lambda: cv2.VideoCapture(video_url, cv2.CAP_FFMPEG) lambda: cv2.VideoCapture(video_url, cv2.CAP_FFMPEG)
) )
# 设置核心参数 - 重中之重,缺一不可
await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 60000)) # ✅ 关键改动3优化超时和缓存参数解决30秒超时问题
await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_READ_TIMEOUT_MSEC, 50000)) # 调整超时时间OpenCV的超时参数在部分环境下需要略大于实际预期推荐设为30秒
await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)) # 无缓存,低延迟无残影 await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 30000)) # 连接超时30秒适配底层逻辑
await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_READ_TIMEOUT_MSEC, 10000)) # 读取帧超时10秒避免长时间阻塞
await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_BUFFERSIZE, 2)) # 缓存从1调整为2平衡低延迟和稳定性丢包场景下更稳定
await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_FPS, 25)) await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_FPS, 25))
# 关闭自动处理功能,减少额外开销
await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_AUTO_WB, 0))
await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0))
# ✅ 关键改动4流连接预热提升首次打开成功率
await asyncio.sleep(0.5) # 短暂等待让FFmpeg完成底层初始化
# 校验流是否成功打开 # 校验流是否成功打开
is_opened = await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.isOpened()) is_opened = await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.isOpened())
if not is_opened: if not is_opened:
logger.warning(f"尝试 {attempt}打开RTMP流失败准备重试") logger.warning(f"尝试 {attempt}打开RTMP流失败准备重试")
# 释放资源前先短暂等待,避免资源泄露
await asyncio.sleep(0.1)
await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.release() if cap else None) await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.release() if cap else None)
await asyncio.sleep(retry_delay) await asyncio.sleep(retry_delay)
continue continue
# 自适应获取流的【真实分辨率】,无需手动探测,精准无误差 # 自适应获取流的【真实分辨率】
width = await loop.run_in_executor(read_rtmp_frames_executor, lambda: int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))) width = await loop.run_in_executor(read_rtmp_frames_executor, lambda: int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
height = await loop.run_in_executor(read_rtmp_frames_executor, lambda: int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))) height = await loop.run_in_executor(read_rtmp_frames_executor, lambda: int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
stream_fps = await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.get(cv2.CAP_PROP_FPS)) stream_fps = await loop.run_in_executor(read_rtmp_frames_executor, lambda: cap.get(cv2.CAP_PROP_FPS))
@ -1470,15 +1715,15 @@ async def read_rtmp_frames(
logger.info(f"视频分辨率: {width}x{height}, 流帧率: {stream_fps:.1f} FPS") logger.info(f"视频分辨率: {width}x{height}, 流帧率: {stream_fps:.1f} FPS")
logger.info(f"成功启动 OpenCV+FFmpeg 连接 RTMP 流: {video_url}") logger.info(f"成功启动 OpenCV+FFmpeg 连接 RTMP 流: {video_url}")
# 初始化帧读取状态 (保留原逻辑不变) # 初始化帧读取状态
frame_sequence = 0 frame_sequence = 0
last_timestamp = time_start last_timestamp = time_start
consecutive_corrupted_frames = 0 # 连续损坏帧计数 consecutive_corrupted_frames = 0 # 连续损坏帧计数
max_consecutive_corrupted = 10 # 最大连续损坏帧数 max_consecutive_corrupted = 20 # 适配丢包场景的最大连续损坏帧数
while not cancel_flag.is_set(): while not cancel_flag.is_set():
try: try:
# ✅ 核心替换使用cv2.read()读取帧,线程池执行避免阻塞协程 # 读取帧,线程池执行避免阻塞协程
ret, frame = await loop.run_in_executor( ret, frame = await loop.run_in_executor(
read_rtmp_frames_executor, read_rtmp_frames_executor,
lambda: cap.read() lambda: cap.read()
@ -1488,22 +1733,26 @@ async def read_rtmp_frames(
frame_sequence += 1 frame_sequence += 1
frame_count += 1 frame_count += 1
# 处理帧数据(保留原逻辑完全不变,兼容原有的损坏帧处理 # 处理帧数据(微调损坏帧判断,提升容错
img = None img = None
is_corrupted = False is_corrupted = False
print(f"读取 read_rtmp_frames 判断")
try: try:
if ret and frame is not None and frame.shape == (height, width, 3): # 放宽帧形状判断,允许偶尔的分辨率小幅波动(丢包导致)
# 完整有效帧处理 valid_frame_shape = (height, width, 3)
if ret and frame is not None and frame.shape in [valid_frame_shape, (height, width)]:
# 完整有效帧处理统一转为3通道BGR格式
img = frame.copy() img = frame.copy()
if len(img.shape) == 2: # 灰度帧转为3通道
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
consecutive_corrupted_frames = 0 # 重置连续损坏计数 consecutive_corrupted_frames = 0 # 重置连续损坏计数
else: else:
# 损坏帧/空帧处理 # 损坏帧/空帧处理
if consecutive_corrupted_frames % 5 == 0: # 每5帧打印一次日志减少冗余输出
logger.warning(f"帧数据损坏/空帧, 序列: {frame_sequence}") logger.warning(f"帧数据损坏/空帧, 序列: {frame_sequence}")
is_corrupted = True is_corrupted = True
consecutive_corrupted_frames += 1 consecutive_corrupted_frames += 1
# 创建替代帧,保留原逻辑 # 创建替代帧
if consecutive_corrupted_frames <= max_consecutive_corrupted: if consecutive_corrupted_frames <= max_consecutive_corrupted:
img = np.zeros((height, width, 3), dtype=np.uint8) img = np.zeros((height, width, 3), dtype=np.uint8)
else: else:
@ -1512,11 +1761,9 @@ async def read_rtmp_frames(
except Exception as frame_error: except Exception as frame_error:
logger.error(f"帧数据处理错误: {frame_error}") logger.error(f"帧数据处理错误: {frame_error}")
# 创建空白帧作为后备,保留原逻辑
img = np.zeros((height, width, 3), dtype=np.uint8) img = np.zeros((height, width, 3), dtype=np.uint8)
is_corrupted = True is_corrupted = True
consecutive_corrupted_frames += 1 consecutive_corrupted_frames += 1
print(f"读取 read_rtmp_frames 判断1")
# 获取OSD信息 - 保留原逻辑完全不变 # 获取OSD信息 - 保留原逻辑完全不变
osd_info = None osd_info = None
@ -1534,7 +1781,6 @@ async def read_rtmp_frames(
) )
except Exception as osd_error: except Exception as osd_error:
logger.warning(f"获取OSD信息失败: {osd_error}") logger.warning(f"获取OSD信息失败: {osd_error}")
print(f"读取 read_rtmp_frames 判断2")
# 放入帧队列 - 保留原逻辑完全不变 # 放入帧队列 - 保留原逻辑完全不变
if img is not None and not frame_queue.full(): if img is not None and not frame_queue.full():
@ -1543,7 +1789,7 @@ async def read_rtmp_frames(
current_time_ns = last_timestamp + 1 current_time_ns = last_timestamp + 1
last_timestamp = current_time_ns last_timestamp = current_time_ns
# 统计信息 - 保留原逻辑完全不变 # 统计信息
pic_count += 1 pic_count += 1
if current_time_ns - time_start > 1000000000: # 1秒 if current_time_ns - time_start > 1000000000: # 1秒
elapsed_seconds = (current_time_ns - time_start) / 1e9 elapsed_seconds = (current_time_ns - time_start) / 1e9
@ -1554,9 +1800,7 @@ async def read_rtmp_frames(
pic_count = 0 pic_count = 0
time_start = current_time_ns time_start = current_time_ns
print(f"读取 read_rtmp_frames 实时流") # 准备帧数据
# 准备帧数据 - 保留原逻辑完全不变
frame_data = { frame_data = {
"sequence": frame_sequence, "sequence": frame_sequence,
"frame": img, "frame": img,
@ -1579,7 +1823,7 @@ async def read_rtmp_frames(
logger.warning("帧队列已满,跳过此帧") logger.warning("帧队列已满,跳过此帧")
await asyncio.sleep(0.001) # 短暂等待 await asyncio.sleep(0.001) # 短暂等待
# 连续帧损坏触发重连前置判断 - 保留原逻辑 # 连续帧损坏触发重连前置判断
if consecutive_corrupted_frames > max_consecutive_corrupted: if consecutive_corrupted_frames > max_consecutive_corrupted:
logger.warning(f"连续{consecutive_corrupted_frames}帧损坏,触发流重连逻辑") logger.warning(f"连续{consecutive_corrupted_frames}帧损坏,触发流重连逻辑")
break break
@ -1595,11 +1839,11 @@ async def read_rtmp_frames(
else: else:
raise RuntimeError(f"无法连接 RTMP 流 (尝试 {max_retries} 次后失败): {video_url}") raise RuntimeError(f"无法连接 RTMP 流 (尝试 {max_retries} 次后失败): {video_url}")
finally: finally:
# ✅ 释放opencv的VideoCapture资源替代原FFmpeg进程的关闭逻辑 # 安全释放opencv的VideoCapture资源
if cap is not None: if cap is not None:
await loop.run_in_executor( await loop.run_in_executor(
read_rtmp_frames_executor, read_rtmp_frames_executor,
lambda: cap.release() lambda: cap.release() if cap.isOpened() else None
) )
logger.info("OpenCV VideoCapture 资源已释放") logger.info("OpenCV VideoCapture 资源已释放")
@ -1613,6 +1857,7 @@ async def read_rtmp_frames(
logger.info(f"RTMP 流已结束或被取消,累计处理帧数: {frame_count}") logger.info(f"RTMP 流已结束或被取消,累计处理帧数: {frame_count}")
# async def process_frames(detector: MultiYOLODetector): # async def process_frames(detector: MultiYOLODetector):
# async def process_frames(detector: MultiYOLODetector_TrackId, cancel_flag: asyncio.Event, # async def process_frames(detector: MultiYOLODetector_TrackId, cancel_flag: asyncio.Event,
# frame_queue: asyncio.Queue, processed_queue: asyncio.Queue): # frame_queue: asyncio.Queue, processed_queue: asyncio.Queue):

View File

@ -667,8 +667,8 @@ async def run_back_Multi_Detect_async(request, request_json, stop_event: asyncio
'engine_path': config.engine_path, 'engine_path': config.engine_path,
'so_path': config.so_path, 'so_path': config.so_path,
# # 测试代码 # # 测试代码
# 'engine_path': r"D:\project\AI-PYTHON\tensorrtx-master\yolo11\build\Release\build.engine", # 'engine_path': r"D:\project\AI-PYTHON\Ai_tottle\engine\renche\renche.engine",
# 'so_path': r"D:\project\AI-PYTHON\tensorrtx-master\yolo11\build\Release\myplugins.dll", # 'so_path': r"D:\project\AI-PYTHON\Ai_tottle\engine\renche\myplugins.dll",
# 工地安全帽 # 工地安全帽
# 'engine_path': r"D:\project\AI-PYTHON\Ai_tottle\engine\gdaq_hat_0926\gdaq_hat_0926.engine", # 'engine_path': r"D:\project\AI-PYTHON\Ai_tottle\engine\gdaq_hat_0926\gdaq_hat_0926.engine",
# 'so_path': r"D:\project\AI-PYTHON\Ai_tottle\engine\gdaq_hat_0926\myplugins.dll", # 'so_path': r"D:\project\AI-PYTHON\Ai_tottle\engine\gdaq_hat_0926\myplugins.dll",