mirror of
https://github.com/wwhai/generic-rtsp-yolov8-render.git
synced 2026-04-22 23:27:21 +08:00
dev: enhance thread
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 1007 KiB |
@@ -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
|
||||
@@ -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推理,最后融合到视频输出。
|
||||

|
||||
|
||||
### 环境要求
|
||||
|
||||
- **开发语言**: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 |
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user