乐于分享
好东西不私藏

保姆级教程:车流量统计AI系统,从0到1落地实战(上)

保姆级教程:车流量统计AI系统,从0到1落地实战(上)

以下章来源于微信公众号:爱罗AI说

作者:Aleo

链接:https://mp.weixin.qq.com/s/JE4jFdRm0NZDlNRIhPvpBA

本文仅用于学术分享,如有侵权,请联系台作删文处理

导读
本文完整覆盖从多路视频采集到车辆检测的全流程,详细讲解如何项目的原理,算法的部署,以及搭建服务端推拉流系统,以及算法功能开发的完整过程,是入门与实战结合的完整教程。本文是实战教程的上半部分,后面还会更新实战下半部分。

01

开篇

在日常工作以及互联网上,经常有人问,该如何做好一个AI项目?为啥我的模型效果一直不行?AI看似那么牛,为啥时常犯傻?我该在项目上怎么用好AI这个工具?工作这么多年,听到过新毕业的小白同学在问;也有资深的产品以及技术大咖在问;甚至很多不了解AI但想引入AI到他们业务流中的“老板们”也想知道。

作为一个在这个行业里摸爬滚打了八、九年的老油条,有一些自己的看法,想分享给大家,也想从大家那里获取到一些不一样的理解。以前零零碎碎写过一些文章,也获得过一些同学的认可,回过头来看,却是有些太琐碎,也不系统,不太利于自己的沉淀,也不利于别人系统的了解AI的落地过程。大家工作过的都知道,在实战中学习和积累的是最快的,所以寻思着,是不是可以向大家展示一下,作为一个技术同学,实际落地一个AI项目是什么样的一个过程,这样大家了解的更系统,我自己也可以通过咀嚼下过往,理解的更深刻。

基于上面的考虑,寻思着可以出个这样的系列,作为实战项目,希望能涵盖AI落地的基础路径,理解成本也没那么高,就选择了一个常规的安防场景-车流量统计。下面让我们以一个算法工程师的角度,看如何研发一套可用的车流量统计算法系统。

需求分析

车流量即单位时间内通过某路段或交通点的车辆数目。在交通规划中,车流量的统计数据是设计道路、制定交通政策和评估交通项目效果的重要依据。通过对车流量的监测和分析,有关部门可以了解道路的拥堵状况,从而采取相应的措施。本项目的输入为道路上监控摄像头的视频流数据,输出为统计时间段内的车流量均值。

车流量的定义

任何一个算法项目,需求定义清楚是可以进行后面研发的基础,所以,首先要确认什么叫“车流量”,这部分的定义不是上面的那种名词解释,而是面向计算机可以理解的了的定义。定义好问题,也是算法工程师非常重要的技能。

这个项目关注场景为安放在道路中间的摄像头,视野可涵盖正向(即迎着摄像头方向)和反向(即顺着摄像头方向)行驶的车辆,需分别统计客户指定时间段内,正向和反向主路上车流量均值。在关注行驶方向上定义与道路垂直的横截线,越过该线的车辆即计数,并用于统计车流量。

性能指标要求

车流量统计值与真实偏差5%以内;处理效率为单服务器同时处理10路数据。

数据输入输出

系统对接前端管理平台,输入数据为前端摄像头采集的RTSP流,分辨率为1080P,帧率25FPS;统计开始信号;统计结束信号;触发车流量统计的横截线;均由管理平台下发。系统输出统计时间段内的平均车流量,反馈管理平台。

系统部署平台

ubuntu20.04服务器,搭配3080显卡,16核 单核3.6G Hz CPU处理器。

方案选型

算法方案选型

本项目适用目标跟踪后提取轨迹线,利用轨迹线与道路横截线的相对关系统计车流量。在

  1. 视觉识别算法方面,该场景关注的车辆属于常规的目标,使用YOLOv11做目标检测,ByteTrack做目标跟踪,即可满足需求。

  2. 业务算法方面,车辆跟踪后以车尾边的中心点形成轨迹线,判断轨迹线是否跨越横截线,跨越哪条横截线,即对应行驶方向的车流数量加一。

系统方案选型

  1. 算法工程化方面,采用 TensorRT 量化推理实现模型的前向推理加速。多路数据同时接入,多batch推理后再拆分跟踪的方式实现资源的最大化利用,提升算法运行效率。

  2. 视频流数据接入方面,采用ffmpeg库执行多路数据的实时解码。

  3. 指令数据接入方面,使用gRPC做数据通讯交互。

系统部署方案选型

选用docker作为系统部署的载体,系统部署后,以一个常驻的服务形式存在,开机自启,启动后即可实时响应管理平台的指令调度。

在工作之余抽时间总结这些东西,时间有限,但也会尽最大努力保证质量,按照一般先粗后细的模式,先会尽快将项目的全部链路打通,使全链路功能闭环,可以正常运行,然后再逐个技术点拆解,并分享一些算法优化的经验。

02

车流量统计项目-YOLO模型的部署

考虑实际落地中部署推理资源消耗,速度与模型精度之间的均衡,同时,车辆属于比较通用场景的目标检测,我们将YOLOV11作为最终的模型选择,目前官方开源的模型基于coco数据集训练,里面已经包含了车辆相关类别,经测试,在监控视角下效果基本可用,所以,在落地过程中,我们先用这个模型来打通部署链路,后面再逐步优化模型性能。

在Nvidia的硬件下做部署,部署框架优先选择官方框架TensorRT,下面重点讲述如何用TensorRT部署YOLOV11模型。主要分为:训练框架下模型的推理验证,中间件模型onnx的转换,中间件到TensorRT模型转换,以及C++部署四个部分,每一步都会穿插着精度对齐,最后会做推理性能测试。

训练框架下模型的推理验证

一般思路下讲一个模型的应用会按模型理论知识,训练优化,推理部署这样的思路来。我们以项目推进角度整个流程就反过来了,先讲部署,项目上急着用呢,模型的部署要关注三个部分,即模型的前处理,模型的推理以及模型的后处理部分,一般会在推理框架上先做验证,确认没问题后再向后面的部署框架迁移。这三部分我们最关注的是前处理和后处理,推理更多的是框架自己搞定。

模型前处理

YOLOV11的默认模型前处理过程为不变形resize,默认尺寸为640*640,即:

  1. 计算缩放比例,原始图像宽大于高,则将宽设置为640,高度自适应到相应的值;原始图像宽小于高,则高设置为640,宽度自适应到相应的值;

  2. 使用opencv自带的resize函数将图片按上述计算缩放尺寸进行缩放,方式选择“双线性差值”。然后再构建一个640*640的底图,像素值均初始化为(114,114,114),并将已缩放好的原始图片居中贴在底图上。

  3. 将opencv的BGR格式图片数据转换为RGB,并进行归一化操作,并转换为维度格式为[N,C,H,W]四维向量输入模型,本期推理默认batch为1。

具体代码参考:

defpreprocess_warpAffine(image, dst_width=640, dst_height=640):"""图像预处理函数,用于将输入图像变形并缩放到指定大小。该函数首先计算缩放因子,以确保图像按比例缩放到目标尺寸,然后计算偏移量,以确保缩放后的图像居中。使用计算得到的缩放和偏移参数对图像进行仿射变换,并对变换后的图像进行颜色反转、归一化和维度转换,以适应深度学习模型的输入要求。参数:image: 输入的图像,通常是一个二维或三维的numpy数组。dst_width: 目标图像的宽度,默认值为640像素。dst_height: 目标图像的高度,默认值为640像素。返回:img_pre: 预处理后的图像,是一个四维的PyTorch张量。IM: 逆仿射变换矩阵,用于将变形后的图像恢复到原始状态。"""    # 计算缩放因子,确保图像按比例缩放到目标尺寸scale = min((dst_width / image.shape[1], dst_height / image.shape[0]))    # 计算偏移量,确保缩放后的图像居中ox = int((dst_width  - scale * image.shape[1]) / 2)oy = int((dst_height - scale * image.shape[0]) / 2)    # 构建仿射变换矩阵M = np.array([[scale,0, ox],[0,scale, oy]],dtype=np.float32)    # 对图像进行仿射变换,设置目标尺寸、插值方法和边界填充方式img_pre = cv2.warpAffine(image, M, (dst_width, dst_height), flags=cv2.INTER_LINEAR,borderMode=cv2.BORDER_CONSTANT, borderValue=(114114114))    # 计算逆仿射变换矩阵,用于将变形后的图像恢复到原始状态IM = cv2.invertAffineTransform(M)    # 对变换后的图像进行颜色反转和归一化处理img_pre = (img_pre[...,::-1] / 255.0).astype(np.float32)    # 调整图像数据的维度顺序,适应深度学习模型的输入要求img_pre = img_pre.transpose(201)[None]    # 将处理后的图像数据转换为PyTorch张量img_pre = torch.from_numpy(img_pre)    # 设置PyTorch打印选项,提高输出精度torch.set_printoptions(precision=6)    # 返回预处理后的图像和逆仿射变换矩阵returnimg_pre, IM

模型后处理

模型经推理后,输出格式为[1, 84, 8400],其中84表示[cx,cy,w,h,class×80],即检测框的中心点坐标,宽,高,以及coco数据集80个类别的置信度;8400表示三个不同尺度的输出,即20×20+40×40+80×80。

  1. 按设置的置信度阈值过滤8400个候选框,默认0.25;

  2. 对于过滤后的候选框执行NMS,iou阈值默认0.45,剩下的即为输出检测框。

具体代码参考:

# 计算两个边界框的交并比(Intersection over Union,IoU)defiou(box1, box2):# 内部函数,用于计算边界框的面积defarea_box(box):return (box[2] - box[0]) * (box[3] - box[1])# 计算交集的左上角和右下角坐标    left   = max(box1[0], box2[0])    top    = max(box1[1], box2[1])    right  = min(box1[2], box2[2])    bottom = min(box1[3], box2[3])# 计算交集的面积    cross  = max((right-left), 0) * max((bottom-top), 0)# 计算并集的面积    union  = area_box(box1) + area_box(box2) - cross# 如果交集或并集的面积为0,则返回0if cross == 0or union == 0:return0# 返回交并比return cross / union# 非极大值抑制(Non-Maximum Suppression,NMS)算法,用于去除多余的边界框defNMS(boxes, iou_thres):# 初始化一个标记列表,用于标记是否移除某个边界框    remove_flags = [False] * len(boxes)# 初始化一个列表,用于保存保留下来的边界框    keep_boxes = []# 遍历所有边界框for i, ibox in enumerate(boxes):# 如果当前边界框已被标记为移除,则跳过if remove_flags[i]:continue# 将当前边界框添加到保留列表中        keep_boxes.append(ibox)# 继续遍历其余的边界框for j in range(i + 1, len(boxes)):# 如果当前边界框已被标记为移除,则跳过if remove_flags[j]:continue# 获取下一个边界框            jbox = boxes[j]# 如果两个边界框的类别标签不同,则跳过if(ibox[5] != jbox[5]):continue# 如果两个边界框的交并比大于设定的阈值,则标记第二个边界框为移除if iou(ibox, jbox) > iou_thres:                remove_flags[j] = True# 返回保留下来的边界框列表return keep_boxes# 后处理函数,用于处理模型的预测结果defpostprocess(pred, IM=[], conf_thres=0.25, iou_thres=0.45):# 初始化一个列表,用于保存处理后的边界框    boxes = []# 遍历模型的预测结果for item in pred[0]:# 提取边界框的中心坐标、宽度、高度        cx, cy, w, h = item[:4]# 提取类别标签和置信度        label = item[4:].argmax()        confidence = item[4 + label]# 如果置信度低于设定的阈值,则跳过if confidence < conf_thres:continue# 计算边界框的左上角和右下角坐标        left    = cx - w * 0.5        top     = cy - h * 0.5        right   = cx + w * 0.5        bottom  = cy + h * 0.5# 将边界框的坐标、置信度和类别标签添加到列表中        boxes.append([left, top, right, bottom, confidence, label])# 将边界框列表转换为NumPy数组    boxes = np.array(boxes)# 如果没有边界框,则返回空列表if0 == len(boxes):return []# 应用图像的缩放比例和偏移量,将边界框坐标转换回原始图像的坐标系    lr = boxes[:,[02]]    tb = boxes[:,[13]]    boxes[:,[0,2]] = IM[0][0] * lr + IM[0][2]    boxes[:,[1,3]] = IM[1][1] * tb + IM[1][2]# 根据置信度降序排序边界框    boxes = sorted(boxes.tolist(), key=lambda x:x[4], reverse=True)# 应用非极大值抑制,去除多余的边界框return NMS(boxes, iou_thres)

数据集下的推理验证

上述做完后,需要做一下测试集上的推理验证,我们选择coco数据集自带的5000张测试集,验证下来结果基本没有问题。

同时,按“图片名称,类别索引,置信度,检测框”格式,保存识别结果,用于后面模型转换精度保持的精度分析。

中间件模型onnx的转换

本次使用pytorch转TensorRT的常规方式,通过转换中间件onnx进行转换,这步还比较简单,不BB,直接见代码:

model = YOLO(model='yolo11x.pt')  # load a pretrained model (recommended for training)model.export(format="onnx", opset=16, imgsz=640, simplify=True, save_dir='.')

onnx的前后处理也pytorch一样,只是推理调用部分有所区别,这部分不放代码了,后面大家自己在代码仓库中看即可。

中间件到TensorRT模型转换

中间件模型转换完以后,正式到转TensorRT模型的阶段了,我们这次采用的是调用TensorRT的python接口的方式转换(方式有很多,选合适的即可),为快速跑通链路,我们选择了FP16精度做部署,后面细化调优的时候,会跟大家讲讲INT8,这里面可能遇到的坑比较多,到时候细聊。同样不BB,直接上代码:

defbuild_engine(onnx_model_path, engine_path, precision_set="fp16"):"""    从ONNX模型生成TensorRT引擎。    参数:    onnx_model_path: str,ONNX模型的路径。    engine_path: str,生成的TensorRT引擎的保存路径。    precision_set: str,精度设置,默认为"fp16"。    返回:    无    """# 创建TensorRT日志记录器    TRT_LOGGER = trt.Logger(trt.Logger.WARNING)# 创建TensorRT构建器    builder = trt.Builder(TRT_LOGGER)# 创建网络定义,并设置显式批处理标志    network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))# 创建ONNX解析器    parser = trt.OnnxParser(network, TRT_LOGGER)# 打开ONNX模型文件并解析with open(onnx_model_path, "rb"as model:ifnot parser.parse(model.read()):# 如果解析失败,打印错误信息for error in range(parser.num_errors):                print(parser.get_error(error))# 创建构建配置    config = builder.create_builder_config()# 设置最大工作空间大小为1GB    config.max_workspace_size = 1 << 30# 1GB# 如果精度设置为fp16,启用FP16模式if precision_set == "fp16":        config.set_flag(trt.BuilderFlag.FP16)# 如果引擎文件不存在,构建并保存引擎ifnot os.path.exists(engine_path):        engine = builder.build_engine(network, config)with open(engine_path, "wb"as f:# 序列化引擎并写入文件            f.write(engine.serialize())

模型转换完,也要做个这部分的单元测试,TensorRT模型的前后处理和pytorch模型不太一样,不过差异不大,主要是:

  1. 预处理阶段转成[N,C,H,W]四维向量后,需要拉成一维传给模型;

  2. 模型推理完成以后,也是以一维的方式输出,同样要转换为[1, 84, 8400]格式后,接前面的后处理算法。

C++部署

最终算法将以C++的方式部署在客户机器上,所以需要将TensorRT模型python部分的推理代码移植成c++,同时顺带做了些推理优化,主要是将前处理和后处理的代码用cuda重写了,以提升推理性能。

前处理的cuda移植

  1. 双线性差值的不变形resize,做灰边填充部分的代码如下:

__global__ voidletterbox(const uchar* srcData, constint srcH, constint srcW, uchar* tgtData, constint tgtH, constint tgtW, constint rszH, constint rszW, constint startY, constint startX){int ix = threadIdx.x + blockDim.x * blockIdx.x;int iy = threadIdx.y + blockDim.y * blockIdx.y;int idx = ix + iy * tgtW;int idx3 = idx * 3;if ( ix > tgtW || iy > tgtH ) return;  // thread out of target range// gray region on target imageif ( iy < startY || iy > (startY + rszH - 1) ) {        tgtData[idx3] = 114;        tgtData[idx3 + 1] = 114;        tgtData[idx3 + 2] = 114;return;    }if ( ix < startX || ix > (startX + rszW - 1) ){        tgtData[idx3] = 114;        tgtData[idx3 + 1] = 114;        tgtData[idx3 + 2] = 114;return;    }float scaleY = (float)rszH / (float)srcH;float scaleX = (float)rszW / (float)srcW;// (ix,iy)为目标图像坐标// (before_x,before_y)原图坐标float beforeX = float(ix - startX + 0.5) / scaleX - 0.5;float beforeY = float(iy - startY + 0.5) / scaleY - 0.5;// 原图像坐标四个相邻点// 获得变换前最近的四个顶点,取整int topY = static_cast<int>(beforeY);int bottomY = topY + 1;int leftX = static_cast<int>(beforeX);int rightX = leftX + 1;//计算变换前坐标的小数部分float u = beforeX - leftX;float v = beforeY - topY;if (topY >= srcH - 1 && leftX >= srcW - 1)  //右下角    {for (int k = 0; k < 3; k++)        {            tgtData[idx3 + k] = (1. - u) * (1. - v) * srcData[(leftX + topY * srcW) * 3 + k];        }    }elseif (topY >= srcH - 1)  // 最后一行    {for (int k = 0; k < 3; k++)        {            tgtData[idx3 + k]            = (1. - u) * (1. - v) * srcData[(leftX + topY * srcW) * 3 + k]            + (u) * (1. - v) * srcData[(rightX + topY * srcW) * 3 + k];        }    }elseif (leftX >= srcW - 1)  // 最后一列    {for (int k = 0; k < 3; k++)        {            tgtData[idx3 + k]            = (1. - u) * (1. - v) * srcData[(leftX + topY * srcW) * 3 + k]            + (1. - u) * (v) * srcData[(leftX + bottomY * srcW) * 3 + k];        }    }else// 非最后一行或最后一列情况    {for (int k = 0; k < 3; k++)        {            tgtData[idx3 + k]            = (1. - u) * (1. - v) * srcData[(leftX + topY * srcW) * 3 + k]            + (u) * (1. - v) * srcData[(rightX + topY * srcW) * 3 + k]            + (1. - u) * (v) * srcData[(leftX + bottomY * srcW) * 3 + k]            + u * v * srcData[(rightX + bottomY * srcW) * 3 + k];        }    }}
  1. 颜色通道转换以及归一化部分的代码如下:

__global__ voidprocess(const uchar* srcData, float* tgtData, constint h, constint w){int ix = threadIdx.x + blockIdx.x * blockDim.x;int iy = threadIdx.y + blockIdx.y * blockDim.y;int idx = ix + iy * w;int idx3 = idx * 3;if (ix < w && iy < h)    {        tgtData[idx] = (float)srcData[idx3 + 2] / 255.0;  // R pixel        tgtData[idx + h * w] = (float)srcData[idx3 + 1] / 255.0;  // G pixel        tgtData[idx + h * w * 2] = (float)srcData[idx3] / 255.0;  // B pixel    }}

后处理的cuda移植

  1. 数据解码的cuda化

// ------------------ decode ( get class and conf ) --------------------__global__ voiddecode_kernel(float* src, float* dst, int numBboxes, int numClasses, float confThresh, int maxObjects, int numBoxElement){int position = blockDim.x * blockIdx.x + threadIdx.x;if (position >= numBboxes) return;float* pitem = src + (4 + numClasses) * position;float* classConf = pitem + 4;float confidence = 0;int label = 0;for (int i = 0; i < numClasses; i++){if (classConf[i] > confidence){            confidence = classConf[i];            label = i;        }    }if (confidence < confThresh) return;int index = (int)atomicAdd(dst, 1);if (index >= maxObjects) return;float cx     = pitem[0];float cy     = pitem[1];float width  = pitem[2];float height = pitem[3];float left   = cx - width * 0.5f;float top    = cy - height * 0.5f;float right  = cx + width * 0.5f;float bottom = cy + height * 0.5f;float* pout_item = dst + 1 + index * numBoxElement;    pout_item[0] = left;    pout_item[1] = top;    pout_item[2] = right;    pout_item[3] = bottom;    pout_item[4] = confidence;    pout_item[5] = label;    pout_item[6] = 1;  // 1 = keep, 0 = ignore}voiddecode(float* src, float* dst, int numBboxes, int numClasses, float confThresh, int maxObjects, int numBoxElement, cudaStream_t stream){    cudaMemsetAsync(dst, 0, sizeof(int), stream);int blockSize = 256;int gridSize = (numBboxes + blockSize - 1) / blockSize;    decode_kernel<<<gridSize, blockSize, 0, stream>>>(src, dst, numBboxes, numClasses, confThresh, maxObjects, numBoxElement);}
  1. nms的cuda化

__device__ floatbox_iou(float aleft, float atop, float aright, float abottom, float bleft, float btop, float bright, float bbottom){float cleft = max(aleft, bleft);float ctop = max(atop, btop);float cright = min(aright, bright);float cbottom = min(abottom, bbottom);float c_area = max(cright - cleft, 0.0f) * max(cbottom - ctop, 0.0f);if (c_area == 0.0f) return0.0f;float a_area = max(0.0f, aright - aleft) * max(0.0f, abottom - atop);float b_area = max(0.0f, bright - bleft) * max(0.0f, bbottom - btop);return c_area / (a_area + b_area - c_area);}__global__ voidnms_kernel(float* data, float kNmsThresh, int maxObjects, int numBoxElement){int position = blockDim.x * blockIdx.x + threadIdx.x;int count = min((int)data[0], maxObjects);if (position >= count) return;// left, top, right, bottom, confidence, class, keepflagfloat* pcurrent = data + 1 + position * numBoxElement;float* pitem;for (int i = 0; i < count; i++){        pitem = data + 1 + i * numBoxElement;if (i == position || pcurrent[5] != pitem[5]) continue;if (pitem[4] >= pcurrent[4]){if (pitem[4] == pcurrent[4] && i < position) continue;float iou = box_iou(                pcurrent[0], pcurrent[1], pcurrent[2], pcurrent[3],                pitem[0], pitem[1], pitem[2], pitem[3]            );if (iou > kNmsThresh){                pcurrent[6] = 0;  // 1 = keep, 0 = ignorereturn;            }        }    }}voidnms(float* data, float kNmsThresh, int maxObjects, int numBoxElement, cudaStream_t stream){int blockSize = maxObjects < 256?maxObjects:256;int gridSize = (maxObjects + blockSize - 1) / blockSize;    nms_kernel<<<gridSize, blockSize, 0, stream>>>(data, kNmsThresh, maxObjects, numBoxElement);}

基于c++的推理部署与cuda加速后,相应的模型和代码将作为最终的产出集成进产品里,使用测试集测试确定:

  1. 单帧推理耗时5.88毫秒,即一秒推理170帧,粗略估计实时处理10路数据问题不大。(还没有发力,指标就达到了,没意思,后面再手动给自己加戏吧,一堆优化策略没上呢)

下一步逐步做完性能保持的测试,确保量化部署后的模型和原始模型没有大的精度损失,即可对外发布了。

量化精度损失评估

以最终目标为导向,进行量化后目标精度损失的评估,即这一批数据中每一张图应该出几个框,统计下量化后这些框还在不在,在的话即认为没有精度损失,否则认为有精度损失,简单高效。具体方法如下:

  1. 逐类别拆分,分析每个类别的量化精度损失;

  2. 定义IOU阈值为0.5,进行量化前后的目标框匹配,最匹配目标IOU大于0.5即认为匹配上,以量化前作为真值,统计匹配上目标的准确率和召回率,进而判断是否有不可接受的精度损失。

精度保持测试:

  1. pytorch模型和c++版本TensorRT模型

有精度损失但差异不大,整体可以用,后续可以着重看损失点在哪里,进行相关优化。

到这为止,YOLOV的量化部署就结束了,达到了基本可用的程度,后续再继续优化,其实整体链路早就打通了,只是在做一致性测试的时候,一直有一些问题,仔细排查后才对齐,而这一块也是做模型部署很重要的一部分工作,后续再着重讲吧。

备注:

c++部署部分的代码参考了 https://github.com/emptysoal/TensorRT-YOLO11,这里面有较多的yolo系列的工程实现,大家也可以访问支持。

03

多路视频推拉流服务

做车流量统计,原始数据的获取必不可少,还需要快速在服务端搭建一套多路视频的推拉流服务,我们的输入是多路(可扩展)的rtsp流,输出是对应解码好的图片数据,该图片数据用于做后续的图像识别。
1. 流媒体服务,选用 mediamtx 作为 RTSP 服务器搭建的工具,简单介绍下这个工具,它是一个开源的高性能媒体服务器搭建工具,可以用来出来音频和视频,支持包括RTSP, RTMP, WebRTC等协议,适合用作直播,视频监控,以及流媒体的中继,同时它也支持跨平台,Windows,Linux, macOS都支持。
2.视频推拉流,选用FFmpeg,这个我就不用介绍了,搞音视频的应该绕不过,在这个项目中,我们主要用它来做模拟编码推流,以及拉流解码工作。
3.解码后的图像数据存放于OpenCV的cv::Mat中,用于后续流转。

工具安装

上面三个工具OpenCV属于常规工具,我就不介绍了,重点介绍 mediamtx 和 FFmpeg 在ubuntu20.04上的安装方式。

mediamtx

  • 下载地址:https://github.com/bluenviron/mediamtx/releases,选择 mediamtx_v1.9.1_linux_amd64.tar.gz,这个版本是验证过可以直接用的。

  • 下载后直接解压即可,里面主要就俩文件,一个 mediamtx 的可执行文件,一个 mediamtx.yml 的配置文件

  • 使用默认配置就行,如需配置相关端口,可在  mediamtx.yml 内修改

  • 相关配置确认后,直接终端执行 “./mediamtx”即可。

FFmpeg

FFmpeg常规安装可以支持apt,我们本次安装走的是源码编译,一个是我个人习惯,源码编译可以解决很多环境兼容性问题,一个是为后面解码性能提升做准备,我们编译支持cuda的版本。

  • 源码下载

    • git clone -b release/7.1 git@github.com:FFmpeg/FFmpeg.git

    • https://github.com/FFmpeg/nv-codec-headers/releases 内下载 nv-codec-headers-12.2.72.0 这个要和nvidia-smi里的cuda version匹配,我选的12.2

    • wget http://ftp.videolan.org/pub/videolan/x265/x265_2.6.tar.gz

  • x264安装

    • sudo apt-get install libx264-dev

  • sdl 安装,ffplay依赖

    • sudo apt-get install libsdl2-dev

  • x265编译

    • 解压安装包

    • cd x265_v2.6/build/linux/

    • sh ./make-Makefiles.bash

    • make

    • make install

    • 默认安装在系统目录下,这个目录要记下来,后面部署时候可能要用。

  • nv-codec-headers 编译

    • cd nv-codec-headers-12.2.72.0/

    • make

    • make install

    • 默认安装在系统目录下,这个目录要记下来,后面部署时候可能要用。

  • ffmpeg源码编译

    • 在工程目录下,./configure --prefix=/usr/local/ffmpeg --enable-shared --disable-static --disable-doc  --enable-gpl --enable-libx265 --enable-libx264 --enable-cuda --enable-cuvid --enable-nvdec --disable-x86asm

    • make

    • make install

    • 默认安装在系统目录/usr/local/ffmpeg下,这个目录要记下来,后面部署时候可能要用

    模拟推流

    一般情况下RTSP流的源头来自于相机,系统侧主要关注拉流解码即可,但在研发和测试阶段没有那么多真实数据可以接入,所以需要搭建一套模型的推流服务,该推流服务支持多路数据的推送。

    核心代码就这一句:

    ffmpeg -re -i "${INPUT_MP4}" -c:v libx264 -preset veryfast -maxrate 3000k -bufsize 6000k -pix_fmt yuv420p -g 50 -c:a aac -b:a 160k -ac 2 -ar 44100 -f rtsp "${RTSP_URL}"
    • 基本参数包括

      • -re 以输入文件的原始帧率读取输入文件。通常用于模拟实时流。

      • -i  "${INPUT_MP4}": 指定输入文件路径

    • 视频编码参数包括:

      • ultrafast‌:编码速度非常快,但输出视频质量不高。

      • superfast‌:编码速度非常快,输出视频质量比ultrafast略微高一些。

      • veryfast‌:编码速度较快,输出视频质量较好。

      • fast‌:编码速度较快,输出视频质量较好。

      • medium‌:编码速度适中,输出视频质量非常好,这是默认值。

      • slow‌:编码速度较慢,但输出视频质量比medium更好。

      • slower‌:编码速度比slow略微慢一些,但输出视频质量更好。

      • veryslow‌:编码速度最慢,但输出视频质量最好‌。

      • -c:v libx264: 使用 H.264 编码器对视频进行编码, H.264 编码器,兼容性好,压缩效率高。适合大多数应用场景。如果需要更高的压缩比和更好的质量,可以考虑使用 H.265/HEVC 编码器(-c:v libx265),但编码速度会更慢,硬件要求也更高。

      • -preset veryfast: 设置编码速度为非常快(veryfast),这会牺牲一些压缩效率以换取更快的编码速度。-preset 参数设置参考:

      • -maxrate 3000k: 设置最大比特率为 3000 kbps(3 Mbps)。确保该值与目标网络带宽匹配,避免过高导致网络拥塞或过低影响视频质量。可以结合 -minrate 和 -bufsize 参数进一步优化比特率控制。

      • -bufsize 6000k: 设置缓冲区大小为 6000 kbps(6 Mbps),用于平滑比特率波动。一般建议 -bufsize 至少是 -maxrate 的两倍,以确保稳定的比特率输出。对于网络条件较差的情况,可以适当增大缓冲区。

      • -pix_fmt yuv420p: 设置像素格式为 YUV 4:2:0,大多数设备和播放器支持的格式。

      • -g 50: 设置 GOP(Group of Pictures)大小为 50,即每隔 50 帧插入一个关键帧(I 帧)。

    • 音频编码参数包括(本项目不太关注):

      • -c:a aac: 使用 AAC 编码器对音频进行编码。

      • -b:a 160k: 设置音频比特率为 160 kbps。

      • -ac 2: 设置音频声道数为 2(立体声)。

      • -ar 44100: 设置音频采样率为 44100 Hz。

    • 输出格式

      • -f rtsp: 指定输出格式为 RTSP 流协议。

      • "${RTSP_URL}": 指定 RTSP 流的 URL。

    支持多路视频推送的shell脚本代码如下:

    #!/bin/bash# 检查参数数量if [ "$#" -ne 2 ]; then    echo "Usage: $0 <input_folder> <rtsp_base_url>"    exit 1fi# ffmpeg路径export PATH=../../3rd/ffmpeg/bin:$PATHexport LD_LIBRARY_PATH=../../3rd/ffmpeg/lib# 输入的文件夹路径INPUT_FOLDER="$1"# 输出的RTSP URL基础RTSP_BASE_URL="$2"# 检查输入文件夹是否存在if [ ! -d "$INPUT_FOLDER" ]; then    echo "Error: Input folder $INPUT_FOLDER not found!"    exit 1fi# 遍历输入文件夹中的所有MP4文件for INPUT_MP4 in "$INPUT_FOLDER"/*.mp4; do    # 检查文件是否存在    if [ ! -f "$INPUT_MP4" ]; then        echo "No MP4 files found in $INPUT_FOLDER!"        continue    fi    # 获取文件名(不带路径)    FILENAME=$(basename "$INPUT_MP4")    # 去掉文件扩展名    BASENAME="${FILENAME%.*}"    # 构建RTSP URL    RTSP_URL="${RTSP_BASE_URL}/${BASENAME}"    # 使用 FFmpeg 将 mp4 流转换为 RTSP 流    function mp4_to_rtsp {        ffmpeg -re -i "${INPUT_MP4}" -c:v libx264 -preset veryfast -maxrate 3000k -bufsize 6000k -pix_fmt yuv420p -g 50 -c:a aac -b:a 160k -ac 2 -ar 44100 -f rtsp "${RTSP_URL}"    }    # 启动推送MP4文件的FFmpeg进程    echo "Starting to push MP4 file $INPUT_MP4 to RTSP server at $RTSP_URL..."    mp4_to_rtsp &done# 等待所有后台任务完成waitecho "All processes have finished."

      实时解码

      实时解码部分的技术方案采用的是C++的线程池技术,用于适应不同路数视频数据的实时接入,解码库依旧使用的是FFmpeg,这个部分使用的是FFmpeg的C++接口函数,不废话,直接上代码:

      线程池解码的类头文件:

      #ifndef RTSPSTREAMPROCESSOR_H#define RTSPSTREAMPROCESSOR_H#include"common.h"class RTSPStreamProcessor {public:    // 构造函数,初始化线程池    RTSPStreamProcessor(int numThreads, BlockingQueue<sSingleFrameInfo>& queue);    // 析构函数,停止线程池并等待所有线程结束    ~RTSPStreamProcessor();    // 将RTSP URL添加到任务队列中    voidenqueue(const std::string& rtspUrl);private:    // 处理单个RTSP流    voidprocessRTSPStream(const std::string& rtspUrl);    // 工作线程池    std::vector<std::thread> workers;    // 任务队列    std::queue<std::function<void()>> tasks;    // 保护任务队列的互斥锁    std::mutex queue_mutex;    // 条件变量,用于线程间的通信    std::condition_variable condition;    // 标志位,指示是否停止处理任务    bool stop;    uint32_t mGlobalFrameNum = 0;    BlockingQueue<sSingleFrameInfo> &frameQueue;};#endif// RTSPSTREAMPROCESSOR_H

      线程池解码的类源文件:

      #include"RTSPStreamProcessor.h"// 构造函数,初始化线程池RTSPStreamProcessor::RTSPStreamProcessor(int numThreads, BlockingQueue<sSingleFrameInfo>& queue) :    stop(false), frameQueue(queue) {    for (int i = 0; i < numThreads; ++i) {        workers.emplace_back([this] {            for (;;) {                std::function<void()> task;                {                    std::unique_lock<std::mutex> lock(this->queue_mutex);                    // 等待任务或停止信号                    this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); });                    if (this->stop && this->tasks.empty()) {                        return;                    }                    // 获取任务并移除                    task = std::move(this->tasks.front());                    this->tasks.pop();                }                // 执行任务                task();            }        });    }}// 析构函数,停止线程池并等待所有线程结束RTSPStreamProcessor::~RTSPStreamProcessor() {    {        std::unique_lock<std::mutex> lock(queue_mutex);        stop = true;    }    // 通知所有线程停止    condition.notify_all();    for (std::thread& worker : workers) {        worker.join();    }}// 将RTSP URL添加到任务队列中voidRTSPStreamProcessor::enqueue(const std::string& rtspUrl){    {        std::unique_lock<std::mutex> lock(queue_mutex);        // 将处理RTSP流的任务添加到队列        tasks.emplace([this, rtspUrl] { processRTSPStream(rtspUrl); });    }    // 通知一个线程有新任务    condition.notify_one();}// 处理单个RTSP流voidRTSPStreamProcessor::processRTSPStream(const std::string& rtspUrl){    AVFormatContext *pFormatCtx = nullptr;    AVCodecContext *pCodecCtx = nullptr;    AVFrame *pFrame = nullptr, *pFrameBGR = nullptr;    AVPacket *packet = nullptr;    uint8_t *outBuffer  = nullptr;    SwsContext *imgConvertCtx = nullptr;    int videoStream;    bool bConnected = false;    while (true){        if (!bConnected) {            avformat_network_init();            AVDictionary *avdic=NULL;            av_dict_set(&avdic, "buffer_size""655360"0);            av_dict_set(&avdic,"rtsp_transport","tcp",0);            av_dict_set(&avdic, "max_delay""500000"0);            av_dict_set(&avdic, "stimeout""5000000"0);            if (avformat_open_input(&pFormatCtx, rtspUrl.c_str(), nullptr, &avdic) != 0)            {                std::cout << "can't open the file." << std::endl;                bConnected = false;                continue;            }            if (avformat_find_stream_info(pFormatCtx, nullptr) < 0)            {                std::cout << "can't find stream infomation" << std::endl;                bConnected = false;                continue ;            }            videoStream = -1;            for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++)            {                if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)                {                    videoStream = i;                }            }            if (videoStream == -1)            {                std::cout << "can't find a video stream" << std::endl;                bConnected = false;                continue ;            }            const AVCodec *pCodec = avcodec_find_decoder(pFormatCtx->streams[videoStream]->codecpar->codec_id);            if (pCodec == NULL)            {                std::cout << "can't find a codec" << std::endl;                bConnected = false;                continue ;            }            pCodecCtx = avcodec_alloc_context3(pCodec);            if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar) < 0) {                std::cerr << "Failed to allocate codec context." << std::endl;            }            pCodecCtx->thread_count = 4;            pCodecCtx->thread_type = FF_THREAD_SLICE;            if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)            {                std::cout << "can't open a codec" << std::endl;                bConnected = false;                continue ;            }            pFrame = av_frame_alloc();            pFrameBGR = av_frame_alloc();            imgConvertCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height,                                           pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,                                           AV_PIX_FMT_BGR24, SWS_BICUBIC, NULLNULLNULL);            int numBytes = av_image_get_buffer_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height, 1);            outBuffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));            av_image_fill_arrays(pFrameBGR->data, pFrameBGR->linesize, outBuffer, AV_PIX_FMT_BGR24,                                 pCodecCtx->width, pCodecCtx->height, 1);            packet = (AVPacket *) malloc(sizeof(AVPacket));            av_new_packet(packet, pCodecCtx->width * pCodecCtx->height);            bConnected = true;        }        uint32_t frameCount = 0;        while (av_read_frame(pFormatCtx, packet) >= 0)        {            if (packet->stream_index == videoStream)            {                int ret = avcodec_send_packet(pCodecCtx, packet);                if (ret < 0) {//                    fprintf(stderr, "Error sending a packet for decoding: %s\n", av_err2str(ret));                    continue;                }else{                    ret = avcodec_receive_frame(pCodecCtx, pFrame);                    if (ret < 0) {//                        fprintf(stderr, "Error during decoding: %s\n", av_err2str(ret));                    }else{                        sws_scale(imgConvertCtx,                                  (uint8_t const * const *) pFrame->data,                                  pFrame->linesize, 0, pCodecCtx->height, pFrameBGR->data,                                  pFrameBGR->linesize);                        cv::Mat img(pFrame->height,                                    pFrame->width,                                    CV_8UC3,                                    pFrameBGR->data[0]);//                        sSingleFrameInfo frameInfo;//                        frameInfo.frame = img.clone();//                        frameInfo.rtspUrl = rtspUrl;//                        frameInfo.localFrameNum = frameCount;                        queue_mutex.lock();                        cv::imshow("rtsp:" + rtspUrl, img);                        if (cv::waitKey(1) == 27break;                        queue_mutex.unlock();                        std::cout << "rtsp:" << rtspUrl << "frameCount: " << frameCount << std::endl;//                        std::vector<std::string> rtspUrls = splitString(rtspUrl, '/');//                        const std::string savePath = "/media/dling/data_1/ubuntu/everyDay/20241230/" + rtspUrls.back() + "/";//                        createDirectories(savePath);//                        std::string saveName = savePath + std::to_string(frameCount) + ".jpg";//                        cv::imwrite(saveName, img);                        frameCount++;                    }                }            }            av_packet_unref(packet);        }        bConnected = false;        av_free(outBuffer);        outBuffer = nullptr;        av_freep(&pFrameBGR);        av_freep(&pFrame);        sws_freeContext(imgConvertCtx);        imgConvertCtx = nullptr;        avcodec_free_context(&pCodecCtx);        pCodecCtx = nullptr;        avformat_close_input(&pFormatCtx);        pFormatCtx = nullptr;        av_packet_free(&packet);        packet = nullptr;        usleep(1000000);    }}

      运行展示

      经由上述流媒体服务搭建,推流服务搭建,以及拉流解码程序后,实时解析10路RTSP流并展示的效果如下:
      作者介绍
      江大白,工业AI实战派CTO,安生智联联合创始人,专注从算法到工业场景的应用落地,和你分享产品&项目中的所见所得。

      深耕企业安全管理+AI领域,通过“技术+商业+内容”的融合视角,深度参与AI产业化落地。

      全网20W+粉丝AI知识博主,人工智能技术文章超1000W+阅读,《30天入门人工智能》课程,全网2000+名学员。

      主导构建的AI知识平台www.jiangdabai.com累计访问已超800万次;

      更新平台:

      思想阵地(深度洞察):知乎、CSDN @江大白

      内容阵地(视频解读):抖音、快手、小红书 @江大白讲AI

      实战阵地(产品纪实):抖音、快手、小红书 @安生江大白 | 记录“1年10个AI产品100个项目应用”的极限挑战

      欢迎加入《AI未来星球》,一起成长
      扫描下方二维码即可加入~
      真诚分享AI落地过程(AI商机->项目签约->算法开发->产品开发->实施运维)中的各方面经验和踩过的坑。
      你可以获得什么?
      1、大白之前花费10W+购买,AI行业各场景私有数据集下载,星球内倾情分享;2、AI行业研发、产品、商业落地问题咨询(目前AI公司创业中),都可获高质量解答, 有效期一年,无限次提问,有问必答。3、定期邀请AI行业各类嘉宾分享,创业/商业等方面的经验!
      帮助你解决遇到的实际问题,升职加薪!

      大家一起加油!

      基本 文件 流程 错误 SQL 调试
      1. 请求信息 : 2026-06-05 13:41:22 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/714778.html
      2. 运行时间 : 0.094251s [ 吞吐率:10.61req/s ] 内存消耗:4,917.23kb 文件加载:145
      3. 缓存信息 : 0 reads,0 writes
      4. 会话信息 : SESSION_ID=3a2bff4016e571330d318ad56a94a548
      1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
      2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
      3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
      4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
      5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
      6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
      7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
      8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
      9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
      10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
      11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
      12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
      13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
      14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
      15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
      16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
      17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
      18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
      19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
      20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
      21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
      22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
      23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
      24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
      25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
      26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
      27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
      28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
      29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
      30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
      31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
      32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
      33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
      34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
      35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
      36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
      37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
      38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
      39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
      40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
      41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
      42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
      43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
      44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
      45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
      46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
      47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
      48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
      49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
      50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
      51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
      52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
      53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
      54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
      55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
      56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
      57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
      58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
      59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
      60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
      61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
      62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
      63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
      64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
      65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
      66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
      67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
      68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
      69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
      70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
      71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
      72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
      73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
      74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
      75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
      76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
      77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
      78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
      79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
      80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
      81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
      82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
      83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
      84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
      85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
      86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
      87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
      88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
      89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
      90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
      91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
      92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
      93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
      94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
      95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
      96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
      97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
      98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
      99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
      100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
      101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
      102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
      103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
      104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
      105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
      106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
      107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
      108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
      109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
      110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
      111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
      112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
      113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
      114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
      115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
      116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
      117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
      118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
      119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
      120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
      121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
      122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
      123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
      124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
      125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
      126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
      127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
      128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
      129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
      130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
      131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
      132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
      133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
      134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
      135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
      136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
      137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
      138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
      139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
      140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
      141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
      142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
      143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
      144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
      145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
      1. CONNECT:[ UseTime:0.000583s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
      2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000736s ]
      3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000357s ]
      4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000277s ]
      5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000563s ]
      6. SELECT * FROM `set` [ RunTime:0.000250s ]
      7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000582s ]
      8. SELECT * FROM `article` WHERE `id` = 714778 LIMIT 1 [ RunTime:0.000915s ]
      9. UPDATE `article` SET `lasttime` = 1780638082 WHERE `id` = 714778 [ RunTime:0.001356s ]
      10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000300s ]
      11. SELECT * FROM `article` WHERE `id` < 714778 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000479s ]
      12. SELECT * FROM `article` WHERE `id` > 714778 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000392s ]
      13. SELECT * FROM `article` WHERE `id` < 714778 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000860s ]
      14. SELECT * FROM `article` WHERE `id` < 714778 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.000731s ]
      15. SELECT * FROM `article` WHERE `id` < 714778 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.001197s ]
      0.096342s