dev: enhance thread

This commit is contained in:
wwhai
2025-01-13 21:20:25 +08:00
parent f28e8d17a9
commit 189ae4a77e
8 changed files with 142 additions and 87 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 KiB

+21
View File
@@ -0,0 +1,21 @@
// Copyright (C) 2025 wwhai
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef BACKGROUND_H
#define BACKGROUND_H
#include "frame_queue.h"
/// @brief 背景任务
void *background_task_thread(void *arg);
#endif
+6 -57
View File
@@ -1,46 +1,15 @@
### 仓库介绍
从 RTSP 流拉取视频并渲染到图形界面,同时对每一帧进行图像处理
### 功能特性
1. **RTSP 拉流**:通过提供 RTSP URL,从流媒体服务器拉取视频流。
2. **多线程处理**
- 线程 A:负责实时播放原始视频帧。
- 线程 B:负责对视频帧进行图像识别处理。
3. **动态叠加检测框**:在播放器界面上实时绘制检测出的矩形框及标记文本:
- 矩形框为红色,标记文本为绿色。
4. **硬件加速支持**SDL2 硬件加速提升渲染性能。
### 项目结构
```
.
├── include/ # 头文件目录
│ ├── frame_queue.h # 帧队列相关接口
│ ├── video_renderer.h # 视频渲染接口
│ ├── video_processor.h # 视频处理接口
│ ├── rtsp_client.h # RTSP 拉流接口
├── src/ # 源代码目录
│ ├── frame_queue.c # 帧队列实现
│ ├── video_renderer.c # 视频渲染实现
│ ├── video_processor.c # 视频处理实现
│ ├── rtsp_client.c # RTSP 拉流实现
│ ├── main.c # 主程序入口
├── obj/ # 中间文件存放目录
├── Makefile # 构建脚本
├── README.md # 项目说明文档
```
从 RTSP 流拉取视频并渲染到图形界面,同时对每一帧进行YOLOV8推理,最后融合到视频输出
![P1](image/readme/1736772995737.png)
### 环境要求
- **开发语言**C
- **依赖库**
- libavformat
- libavcodec
- libavutil
- libswscale
- SDL2
- ffmpeg/n7.1
- sdl2/2.30.11
- opencv/4.11.0
- **系统环境**:支持 Linux、Windows 等平台
### 编译与运行
@@ -55,8 +24,6 @@ make
#### 清理文件
删除生成的中间文件和可执行文件:
```bash
make clean
```
@@ -64,24 +31,6 @@ make clean
#### 运行程序
编译成功后,运行以下命令启动程序:
```bash
./rtsp_monitor <RTSP_URL>
./generic-rtsp-yolov8-render.exe rtsp://192.168.10.8:554/av0_0
```
示例:
```bash
./rtsp_monitor rtsp://192.168.1.10:554/live
```
### 核心模块说明
1. **RTSP 拉流模块 (`rtsp_client.c`)**
负责通过 RTSP URL 拉取视频流,解析为帧并推送到帧队列。
2. **视频渲染模块 (`video_renderer.c`)**
负责从队列中读取原始视频帧,并通过 SDL2 硬件加速渲染到图形界面。
3. **视频处理模块 (`video_processor.c`)**
负责对帧进行识别处理(具体识别逻辑由用户实现),并生成矩形框及标记信息。
Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 KiB

+29
View File
@@ -0,0 +1,29 @@
// Copyright (C) 2025 wwhai
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "background.h"
#include "thread_args.h"
void *background_task_thread(void *arg)
{
ThreadArgs *args = (ThreadArgs *)arg;
pthread_mutex_lock(&(args->ctx->mtx));
while (!args->ctx->is_cancelled)
{
pthread_cond_wait(&(args->ctx->cond), &(args->ctx->mtx));
}
pthread_mutex_unlock(&(args->ctx->mtx));
return NULL;
}
+51 -23
View File
@@ -23,27 +23,42 @@
#include "detection_thread.h"
#include <signal.h>
#include <unistd.h>
#include "background.h"
#include "context.h"
/// @brief
Context *background_thread_ctx;
Context *rtsp_thread_ctx;
Context *video_renderer_thread_ctx;
Context *detection_thread_ctx;
/// @brief
/// @param sig
void handle_signal(int sig)
{
CancelContext(rtsp_thread_ctx);
CancelContext(video_renderer_thread_ctx);
CancelContext(detection_thread_ctx);
CancelContext(background_thread_ctx);
fprintf(stderr, "Received signal: %d\n", sig);
exit(0);
}
int main(int argc, char *argv[])
{
if (signal(SIGTERM, handle_signal) == SIG_ERR)
if (argc < 2)
{
fprintf(stderr, "Usage: %s <RTSP_URL>\n", argv[0]);
return 1;
}
if (signal(SIGINT, handle_signal) == SIG_ERR)
{
perror("Failed to set signal handler for SIGTERM");
return 1;
}
if (argc < 2)
{
fprintf(stderr, "Usage: %s <RTSP_URL>\n", argv[0]);
return EXIT_FAILURE;
}
const char *rtsp_url = argv[1];
background_thread_ctx = CreateContext();
rtsp_thread_ctx = CreateContext();
video_renderer_thread_ctx = CreateContext();
detection_thread_ctx = CreateContext();
// Initialize frame queues
FrameQueue video_queue, detection_queue, box_queue;
@@ -52,43 +67,56 @@ int main(int argc, char *argv[])
frame_queue_init(&box_queue, 60);
// Create threads
pthread_t rtsp_thread, renderer_thread, detection_thread;
Context *rtsp_thread_ctx = CreateContext();
pthread_t background_thread, rtsp_thread, renderer_thread, detection_thread;
//
ThreadArgs background_thread_args = {.ctx = background_thread_ctx};
ThreadArgs rtsp_thread_args = {rtsp_url, &video_queue, &detection_queue, &box_queue, rtsp_thread_ctx};
ThreadArgs video_renderer_thread_args = {rtsp_url, &video_queue, &detection_queue, &box_queue, video_renderer_thread_ctx};
ThreadArgs detection_thread_args = {rtsp_url, &video_queue, &detection_queue, &box_queue, detection_thread_ctx};
//
if (pthread_create(&background_thread, NULL, background_task_thread, (void *)&background_thread_args) != 0)
{
perror("Failed to create RTSP thread");
goto END;
}
//
if (pthread_create(&rtsp_thread, NULL, rtsp_handler_thread, (void *)&rtsp_thread_args) != 0)
{
perror("Failed to create RTSP thread");
return EXIT_FAILURE;
goto END;
}
Context *video_renderer_thread_ctx = CreateContext();
ThreadArgs video_renderer_thread_args = {rtsp_url, &video_queue, &detection_queue, &box_queue, video_renderer_thread_ctx};
//
if (pthread_create(&renderer_thread, NULL, video_renderer_thread, (void *)&video_renderer_thread_args) != 0)
{
perror("Failed to create video renderer thread");
return EXIT_FAILURE;
goto END;
}
Context *detection_thread_ctx = CreateContext();
ThreadArgs detection_thread_args = {rtsp_url, &video_queue, &detection_queue, &box_queue, detection_thread_ctx};
//
if (pthread_create(&detection_thread, NULL, frame_detection_thread, (void *)&detection_thread_args) != 0)
{
perror("Failed to create detection thread");
return EXIT_FAILURE;
goto END;
}
// Wait for threads to finish
pthread_join(rtsp_thread, NULL);
pthread_join(renderer_thread, NULL);
pthread_join(detection_thread, NULL);
// Cleanup
printf("waiting for threads to finish\n");
pthread_detach(rtsp_thread);
pthread_detach(renderer_thread);
pthread_detach(detection_thread);
pthread_join(background_thread, NULL);
END:
// Cancel contexts
CancelContext(background_thread_ctx);
CancelContext(rtsp_thread_ctx);
CancelContext(video_renderer_thread_ctx);
CancelContext(detection_thread_ctx);
//
// Destroy threads
pthread_cond_destroy(&rtsp_thread_ctx->cond);
pthread_mutex_destroy(&video_renderer_thread_ctx->mtx);
pthread_mutex_destroy(&detection_thread_ctx->mtx);
//
// Free frame queues
frame_queue_destroy(&video_queue);
frame_queue_destroy(&box_queue);
frame_queue_destroy(&detection_queue);
+35 -7
View File
@@ -128,18 +128,46 @@ void rescale_box(float x, float y, float w, float h, float width_scale, float he
*h_original = h * height_scale;
}
#include <opencv2/opencv.hpp>
void letterbox(const cv::Mat *src, cv::Mat *dst, int new_width, int new_height, cv::Scalar color)
{
float scale = std::min((float)new_width / src->cols, (float)new_height / src->rows);
int unpad_w = scale * src->cols;
int unpad_h = scale * src->rows;
float scale;
int unpad_w, unpad_h;
// 宽度大于高度
if (new_width > new_height)
{
scale = (float)new_height / src->rows;
unpad_w = scale * src->cols;
unpad_h = new_height;
}
// 高度大于等于宽度
else
{
scale = (float)new_width / src->cols;
unpad_w = new_width;
unpad_h = scale * src->rows;
}
cv::resize(*src, *dst, cv::Size(unpad_w, unpad_h));
int pad_w = new_width - unpad_w;
int pad_h = new_height - unpad_h;
int top = pad_h / 2;
int bottom = pad_h - top;
int left = pad_w / 2;
int right = pad_w - left;
int top, bottom, left, right;
// 宽度大于高度的填充机制
if (new_width > new_height)
{
top = 0;
bottom = 0;
left = pad_w / 2;
right = pad_w - left;
}
// 高度大于等于宽度的填充机制
else
{
left = 0;
right = 0;
top = pad_h / 2;
bottom = pad_h - top;
}
cv::copyMakeBorder(*dst, *dst, top, bottom, left, right, cv::BORDER_CONSTANT, color);
}