mirror of
https://github.com/wwhai/generic-rtsp-yolov8-render.git
synced 2026-04-22 15:17:26 +08:00
dev: reformat logger
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
// 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 LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
// 日志等级枚举
|
||||
typedef enum
|
||||
{
|
||||
LOG_TRACE,
|
||||
LOG_DEBUG,
|
||||
LOG_INFO,
|
||||
LOG_WARN,
|
||||
LOG_ERROR,
|
||||
LOG_FATAL
|
||||
} LogLevel;
|
||||
|
||||
// 设置日志等级
|
||||
void set_log_level(LogLevel level);
|
||||
|
||||
// 日志记录函数,增加文件和行号参数
|
||||
void log_message(LogLevel level, const char *file, int line, const char *format, ...);
|
||||
|
||||
// 定义宏,方便调用时自动传入文件和行号
|
||||
#define log_trace(format, ...) log_message(LOG_TRACE, __FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
#define log_debug(format, ...) log_message(LOG_DEBUG, __FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
#define log_info(format, ...) log_message(LOG_INFO, __FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
#define log_warn(format, ...) log_message(LOG_WARN, __FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
#define log_error(format, ...) log_message(LOG_ERROR, __FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
#define log_fatal(format, ...) log_message(LOG_FATAL, __FILE__, __LINE__, format, ##__VA_ARGS__)
|
||||
|
||||
#endif // LOGGER_H
|
||||
@@ -55,87 +55,3 @@ make clean
|
||||
```sh
|
||||
docker run --rm -it -p 1935:1935 -p 1985:1985 -p 8080:8080 ossrs/srs:5
|
||||
```
|
||||
|
||||
## YoloV8类型
|
||||
```
|
||||
0 => person
|
||||
1 => bicycle
|
||||
2 => car
|
||||
3 => motorcycle
|
||||
4 => airplane
|
||||
5 => bus
|
||||
6 => train
|
||||
7 => truck
|
||||
8 => boat
|
||||
9 => traffic light
|
||||
10 => fire hydrant
|
||||
11 => stop sign
|
||||
12 => parking meter
|
||||
13 => bench
|
||||
14 => bird
|
||||
15 => cat
|
||||
16 => dog
|
||||
17 => horse
|
||||
18 => sheep
|
||||
19 => cow
|
||||
20 => elephant
|
||||
21 => bear
|
||||
22 => zebra
|
||||
23 => giraffe
|
||||
24 => backpack
|
||||
25 => umbrella
|
||||
26 => handbag
|
||||
27 => tie
|
||||
28 => suitcase
|
||||
29 => frisbee
|
||||
30 => skis
|
||||
31 => snowboard
|
||||
32 => sports ball
|
||||
33 => kite
|
||||
34 => baseball bat
|
||||
35 => baseball glove
|
||||
36 => skateboard
|
||||
37 => surfboard
|
||||
38 => tennis racket
|
||||
39 => bottle
|
||||
40 => wine glass
|
||||
41 => cup
|
||||
42 => fork
|
||||
43 => knife
|
||||
44 => spoon
|
||||
45 => bowl
|
||||
46 => banana
|
||||
47 => apple
|
||||
48 => sandwich
|
||||
49 => orange
|
||||
50 => broccoli
|
||||
51 => carrot
|
||||
52 => hot dog
|
||||
53 => pizza
|
||||
54 => donut
|
||||
55 => cake
|
||||
56 => chair
|
||||
57 => couch
|
||||
58 => potted plant
|
||||
59 => bed
|
||||
60 => dining table
|
||||
61 => toilet
|
||||
62 => tv
|
||||
63 => laptop
|
||||
64 => mouse
|
||||
65 => remote
|
||||
66 => keyboard
|
||||
67 => cell phone
|
||||
68 => microwave
|
||||
69 => oven
|
||||
70 => toaster
|
||||
71 => sink
|
||||
72 => refrigerator
|
||||
73 => book
|
||||
74 => clock
|
||||
75 => vase
|
||||
76 => scissors
|
||||
77 => teddy bear
|
||||
78 => hair drier
|
||||
79 => toothbrush
|
||||
```
|
||||
+4
-4
@@ -14,7 +14,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "coco_class.h"
|
||||
|
||||
#include "logger.h"
|
||||
// 定义存储名称的数组
|
||||
const char *coco_names[80];
|
||||
void init_coco_names()
|
||||
@@ -102,12 +102,12 @@ void init_coco_names()
|
||||
}
|
||||
void print_coco_names()
|
||||
{
|
||||
fprintf(stdout, "===========coco names============ \n");
|
||||
log_info( "===========coco names============ ");
|
||||
for (int i = 0; i < 80; i++)
|
||||
{
|
||||
printf(" %d => %s\n", i, coco_names[i]);
|
||||
log_info( " %d => %s", i, coco_names[i]);
|
||||
}
|
||||
fprintf(stdout, "================================= \n");
|
||||
log_info( "================================= ");
|
||||
}
|
||||
|
||||
const char *get_coco_name(int id)
|
||||
|
||||
+1
-3
@@ -18,6 +18,7 @@
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include "context.h"
|
||||
#include "logger.h"
|
||||
|
||||
// 创建 Context 结构体
|
||||
Context *CreateContext()
|
||||
@@ -25,18 +26,15 @@ Context *CreateContext()
|
||||
Context *ctx = (Context *)malloc(sizeof(Context));
|
||||
if (ctx == NULL)
|
||||
{
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
}
|
||||
if (pthread_mutex_init(&ctx->mtx, NULL) != 0)
|
||||
{
|
||||
perror("pthread_mutex_init");
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
if (pthread_cond_init(&ctx->cond, NULL) != 0)
|
||||
{
|
||||
perror("pthread_cond_init");
|
||||
pthread_mutex_destroy(&ctx->mtx);
|
||||
free(ctx);
|
||||
return NULL;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "opencv_utils.h"
|
||||
#include "warning_timer.h"
|
||||
#include "timestamp_utils.h"
|
||||
|
||||
#include "logger.h"
|
||||
void *frame_detection_thread(void *arg)
|
||||
{
|
||||
const ThreadArgs *args = (ThreadArgs *)arg;
|
||||
@@ -30,7 +30,7 @@ void *frame_detection_thread(void *arg)
|
||||
cv::dnn::Net net;
|
||||
if (Init_CV_ONNX_DNN_Yolov8(modelPath, &net) != 0)
|
||||
{
|
||||
printf("Error: Failed to initialize the YOLOv8 ONNX DNN model.\n");
|
||||
log_info( "Error: Failed to initialize the YOLOv8 ONNX DNN model.");
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+3
-1
@@ -13,6 +13,8 @@
|
||||
// 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 "frame_queue.h"
|
||||
#include "logger.h"
|
||||
|
||||
void free_queue_node(QueueItem *item)
|
||||
{
|
||||
if (item != NULL)
|
||||
@@ -64,7 +66,7 @@ int enqueue(FrameQueue *q, QueueItem item)
|
||||
QueueNode *newNode = (QueueNode *)malloc(sizeof(QueueNode));
|
||||
if (newNode == NULL)
|
||||
{
|
||||
perror("malloc failed");
|
||||
log_error( "malloc failed");
|
||||
pthread_mutex_unlock(&q->lock);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
+5
-4
@@ -14,12 +14,13 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "http_api.h"
|
||||
#include "logger.h"
|
||||
|
||||
// 回调函数,用于处理响应数据
|
||||
size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
size_t realsize = size * nmemb;
|
||||
fprintf(stdout, "=== http write callback === %.*s\n", (int)realsize, (char *)contents);
|
||||
log_info( "=== http write callback === %.*s", (int)realsize, (char *)contents);
|
||||
return realsize;
|
||||
}
|
||||
void post_recognized_type(const char *url, int type, const char *device_uuid)
|
||||
@@ -31,7 +32,7 @@ void post_recognized_type(const char *url, int type, const char *device_uuid)
|
||||
tm_info = localtime(&now);
|
||||
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
snprintf(json_body, sizeof(json_body), "{\"type\": %d, \"ts\": \"%s\", \"device_uuid\": \"%s\"}", type, ts, device_uuid);
|
||||
fprintf(stdout, "====== post_recognized_type json_body ======\n");
|
||||
fprintf(stdout, "POST: %s, Body: %s\n", url, json_body);
|
||||
fprintf(stdout, "============================================\n");
|
||||
log_info( "====== post_recognized_type json_body ======");
|
||||
log_info( "POST: %s, Body: %s", url, json_body);
|
||||
log_info( "============================================");
|
||||
}
|
||||
|
||||
+9
-8
@@ -17,6 +17,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include "logger.h"
|
||||
// 保存AVFrame图像到文件,格式为png
|
||||
const char *get_av_error(int errnum)
|
||||
{
|
||||
@@ -69,7 +70,7 @@ void save_frame_as_bmp(AVFrame *frame, const char *filename)
|
||||
{
|
||||
if (!frame || !filename)
|
||||
{
|
||||
fprintf(stderr, "Invalid input parameters\n");
|
||||
log_info( "Invalid input parameters");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -77,18 +78,18 @@ void save_frame_as_bmp(AVFrame *frame, const char *filename)
|
||||
int height = frame->height;
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
fprintf(stderr, "Invalid frame dimensions: %d*%d\n", width, height);
|
||||
log_info( "Invalid frame dimensions: %d*%d", width, height);
|
||||
return;
|
||||
}
|
||||
AVPixelFormat input_format = (AVPixelFormat)frame->format;
|
||||
AVPixelFormat output_format = AV_PIX_FMT_BGR24;
|
||||
AVPixelFormat output_format = AV_PIX_FMT_YUV420P;
|
||||
|
||||
// 检查像素格式是否支持
|
||||
const AVPixFmtDescriptor *input_desc = av_pix_fmt_desc_get(input_format);
|
||||
const AVPixFmtDescriptor *output_desc = av_pix_fmt_desc_get(output_format);
|
||||
if (!input_desc || !output_desc)
|
||||
{
|
||||
fprintf(stderr, "Unsupported pixel format\n");
|
||||
log_info( "Unsupported pixel format");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ void save_frame_as_bmp(AVFrame *frame, const char *filename)
|
||||
FILE *file = fopen(filename, "wb");
|
||||
if (!file)
|
||||
{
|
||||
fprintf(stderr, "Failed to open file: %s\n", filename);
|
||||
log_info( "Failed to open file: %s", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -153,7 +154,7 @@ void save_frame_as_bmp(AVFrame *frame, const char *filename)
|
||||
SWS_BILINEAR, NULL, NULL, NULL);
|
||||
if (!sws_ctx)
|
||||
{
|
||||
fprintf(stderr, "Failed to create SwsContext\n");
|
||||
log_info( "Failed to create SwsContext");
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
@@ -162,7 +163,7 @@ void save_frame_as_bmp(AVFrame *frame, const char *filename)
|
||||
AVFrame *bgr_frame = av_frame_alloc();
|
||||
if (!bgr_frame)
|
||||
{
|
||||
fprintf(stderr, "Failed to allocate AVFrame\n");
|
||||
log_info( "Failed to allocate AVFrame");
|
||||
sws_freeContext(sws_ctx);
|
||||
fclose(file);
|
||||
return;
|
||||
@@ -173,7 +174,7 @@ void save_frame_as_bmp(AVFrame *frame, const char *filename)
|
||||
uint8_t *buffer = (uint8_t *)av_malloc(num_bytes * sizeof(uint8_t));
|
||||
if (!buffer)
|
||||
{
|
||||
fprintf(stderr, "Failed to allocate buffer\n");
|
||||
log_info( "Failed to allocate buffer");
|
||||
av_frame_free(&bgr_frame);
|
||||
sws_freeContext(sws_ctx);
|
||||
fclose(file);
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
// 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 "logger.h"
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// 全局日志等级
|
||||
static LogLevel current_log_level = LOG_INFO;
|
||||
|
||||
// 颜色代码
|
||||
const char *color_codes[] = {
|
||||
"\x1B[90m", // 灰色 (TRACE)
|
||||
"\x1B[36m", // 青色 (DEBUG)
|
||||
"\x1B[32m", // 绿色 (INFO)
|
||||
"\x1B[33m", // 黄色 (WARN)
|
||||
"\x1B[31m", // 红色 (ERROR)
|
||||
"\x1B[41m" // 红底白字 (FATAL)
|
||||
};
|
||||
|
||||
const char *reset_color = "\x1B[0m";
|
||||
|
||||
// 日志等级名称
|
||||
const char *log_level_names[] = {
|
||||
"TRACE",
|
||||
"DEBUG",
|
||||
"INFO",
|
||||
"WARN",
|
||||
"ERROR",
|
||||
"FATAL"};
|
||||
|
||||
// 设置日志等级
|
||||
void set_log_level(LogLevel level)
|
||||
{
|
||||
current_log_level = level;
|
||||
}
|
||||
|
||||
// 获取当前时间字符串
|
||||
char *get_current_time()
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm *timeinfo;
|
||||
char *time_str = (char *)malloc(26);
|
||||
if (time_str == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
time(&rawtime);
|
||||
timeinfo = localtime(&rawtime);
|
||||
if (timeinfo == NULL || strftime(time_str, 26, "%Y-%m-%d %H:%M:%S", timeinfo) == 0)
|
||||
{
|
||||
free(time_str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return time_str;
|
||||
}
|
||||
|
||||
// 日志记录函数,增加文件和行号参数
|
||||
void log_message(LogLevel level, const char *file, int line, const char *format, ...)
|
||||
{
|
||||
if (level < current_log_level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char *time_str = get_current_time();
|
||||
va_list args;
|
||||
|
||||
// 打印时间、文件、行号和日志等级
|
||||
if (time_str != NULL)
|
||||
{
|
||||
fprintf(stdout, "%s%s [%s] %s:%d - ", color_codes[level], time_str, log_level_names[level], file, line);
|
||||
free(time_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stdout, "%sUnknownTime [%s] %s:%d - ", color_codes[level], log_level_names[level], file, line);
|
||||
}
|
||||
|
||||
// 打印日志消息
|
||||
va_start(args, format);
|
||||
vfprintf(stdout, format, args);
|
||||
va_end(args);
|
||||
|
||||
// 重置颜色
|
||||
fprintf(stdout, "%s\n", reset_color);
|
||||
}
|
||||
+14
-19
@@ -28,7 +28,7 @@
|
||||
#include "push_stream_thread.h"
|
||||
#include "warning_timer.h"
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "logger.h"
|
||||
// 全局上下文指针数组
|
||||
Context *contexts[4];
|
||||
|
||||
@@ -42,7 +42,7 @@ void handle_signal(int sig)
|
||||
CancelContext(contexts[i]);
|
||||
}
|
||||
}
|
||||
fprintf(stdout, "Received signal %d, exiting...\n", sig);
|
||||
log_info("Received signal %d, exiting...", sig);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ int create_thread(pthread_t *thread, void *(*start_routine)(void *), void *arg)
|
||||
int ret = pthread_create(thread, NULL, start_routine, arg);
|
||||
if (ret != 0)
|
||||
{
|
||||
perror("Failed to create thread");
|
||||
log_error( "Failed to create thread");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -89,31 +89,32 @@ void destroy_frame_queues(FrameQueue *queues, int num_queues)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
set_log_level(LOG_DEBUG);
|
||||
// 检查命令行参数数量
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <camera_URL> <PUSH_URL>\n", argv[0]);
|
||||
log_info("Usage: %s <camera_URL> <PUSH_URL>", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 设置信号处理函数
|
||||
if (signal(SIGINT, handle_signal) == SIG_ERR)
|
||||
{
|
||||
perror("Failed to set signal handler for SIGINT");
|
||||
log_error( "Failed to set signal handler for SIGINT");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 初始化告警计时器
|
||||
if (warning_timer_init(10000, 10, event_triggered) != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to initialize warning timer\n");
|
||||
log_info("Failed to initialize warning timer");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 初始化cURL库
|
||||
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
|
||||
{
|
||||
fprintf(stderr, "Failed to initialize cURL library\n");
|
||||
log_info("Failed to initialize cURL library");
|
||||
warning_timer_stop();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@@ -127,7 +128,7 @@ int main(int argc, char *argv[])
|
||||
contexts[i] = CreateContext();
|
||||
if (!contexts[i])
|
||||
{
|
||||
fprintf(stderr, "Failed to create context %d\n", i);
|
||||
log_info("Failed to create context %d", i);
|
||||
destroy_contexts();
|
||||
curl_global_cleanup();
|
||||
warning_timer_stop();
|
||||
@@ -162,23 +163,17 @@ int main(int argc, char *argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
fprintf(stdout, "Main thread waiting for threads to finish...\n");
|
||||
log_info("Main thread waiting for threads to finish...");
|
||||
|
||||
// 分离线程
|
||||
for (int i = 1; i < 4; i++)
|
||||
// 等待所有线程结束
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (pthread_detach(threads[i]) != 0)
|
||||
if (pthread_join(threads[i], NULL) != 0)
|
||||
{
|
||||
perror("Failed to detach thread");
|
||||
log_error( "Failed to join thread");
|
||||
}
|
||||
}
|
||||
|
||||
// 等待后台线程结束
|
||||
if (pthread_join(threads[0], NULL) != 0)
|
||||
{
|
||||
perror("Failed to join background thread");
|
||||
}
|
||||
|
||||
// 清理资源
|
||||
destroy_contexts();
|
||||
destroy_frame_queues(queues, num_queues);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "opencv_dnn_module.h"
|
||||
#include "opencv_utils.h"
|
||||
#include <iostream>
|
||||
#include "logger.h"
|
||||
#include "coco_class.h"
|
||||
// 初始化YOLOv8模型
|
||||
int Init_CV_ONNX_DNN_Yolov8(const char *model_path, cv::dnn::Net *net)
|
||||
@@ -47,7 +48,7 @@ int Infer_CV_ONNX_DNN_Yolov8(cv::dnn::Net *net, cv::Mat frame, std::vector<Box>
|
||||
{
|
||||
if (!net)
|
||||
{
|
||||
fprintf(stdout, "Error: Net pointer is null.\n");
|
||||
log_info( "Error: Net pointer is null.");
|
||||
return -1;
|
||||
}
|
||||
// 准备输入; YOLOV8图片尺寸需要压缩为640*640
|
||||
@@ -65,7 +66,7 @@ int Infer_CV_ONNX_DNN_Yolov8(cv::dnn::Net *net, cv::Mat frame, std::vector<Box>
|
||||
// 检查推理结果
|
||||
if (outs.empty())
|
||||
{
|
||||
fprintf(stdout, "Error: No output from the network.\n");
|
||||
log_info( "Error: No output from the network.");
|
||||
return -1;
|
||||
}
|
||||
std::vector<DnnResult> results = postprocess(frame, outs, 0.25, 0.5);
|
||||
@@ -92,17 +93,6 @@ int Infer_CV_ONNX_DNN_Yolov8(cv::dnn::Net *net, cv::Mat frame, std::vector<Box>
|
||||
// 释放模型资源
|
||||
int Release_CV_ONNX_DNN_Yolov8(cv::dnn::Net *net)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (net)
|
||||
{
|
||||
net->~Net();
|
||||
}
|
||||
return 0; // 成功
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
fprintf(stdout, "Exception during model release: %s\n", e.what());
|
||||
return -1;
|
||||
}
|
||||
log_info( "Releasing YOLOv8 model...");
|
||||
return 0;
|
||||
}
|
||||
|
||||
+3
-2
@@ -14,6 +14,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "opencv_utils.h"
|
||||
#include "logger.h"
|
||||
// 将 AVFrame 转换为 OpenCV 的 cv::Mat
|
||||
|
||||
cv::Mat AVFrameToCVMat(AVFrame *frame)
|
||||
@@ -47,7 +48,7 @@ cv::Mat AVFrameToCVMat(AVFrame *frame)
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stdout, "Unsupported pixel format: %s\n", av_get_pix_fmt_name(pix_fmt));
|
||||
log_info( "Unsupported pixel format: %s", av_get_pix_fmt_name(pix_fmt));
|
||||
return cv::Mat();
|
||||
}
|
||||
|
||||
@@ -108,7 +109,7 @@ std::vector<DnnResult> postprocess(cv::Mat &frame, const std::vector<cv::Mat> &o
|
||||
for (int idx : indices)
|
||||
{
|
||||
cv::Rect box = boxes[idx];
|
||||
// printf("box: %d, %d, %d, %d, %f, %d\n", box.x, box.y, box.width, box.height, confidences[idx], classIds[idx]);
|
||||
// log_message(LOG_INFO"box: %d, %d, %d, %d, %f, %d", box.x, box.y, box.width, box.height, confidences[idx], classIds[idx]);
|
||||
boxes_result.push_back({box.x, box.y, box.width, box.height, confidences[idx], classIds[idx]});
|
||||
}
|
||||
return boxes_result;
|
||||
|
||||
@@ -28,6 +28,7 @@ extern "C"
|
||||
#include "libav_utils.h"
|
||||
#include "push_stream_thread.h"
|
||||
#include "video_record_thread.h"
|
||||
#include "logger.h"
|
||||
// 克隆帧并加入队列
|
||||
void clone_and_enqueue(AVFrame *src_frame, FrameQueue *queue)
|
||||
{
|
||||
@@ -49,7 +50,7 @@ void handle_error(const char *message, int ret, AVFormatContext **fmt_ctx, AVPac
|
||||
{
|
||||
message = "Unknown error";
|
||||
}
|
||||
fprintf(stderr, "%s (%s).\n", message, get_av_error(ret));
|
||||
log_info( "%s (%s).", message, get_av_error(ret));
|
||||
if (codec_ctx != nullptr && *codec_ctx != nullptr)
|
||||
{
|
||||
avcodec_free_context(codec_ctx);
|
||||
@@ -107,9 +108,9 @@ void *pull_stream_handler_thread(void *arg)
|
||||
handle_error("Error: No video stream found", AVERROR_STREAM_NOT_FOUND, &fmt_ctx, &origin_packet, &codec_ctx);
|
||||
}
|
||||
// Print stream information
|
||||
fprintf(stdout, "=========input_stream_url======== \n");
|
||||
log_info( "=========input_stream_url======== ");
|
||||
av_dump_format(fmt_ctx, 0, args->input_stream_url, 0);
|
||||
fprintf(stdout, "================================= \n");
|
||||
log_info( "================================= ");
|
||||
// Allocate AVPacket for reading frames
|
||||
origin_packet = av_packet_alloc();
|
||||
if (!origin_packet)
|
||||
@@ -146,14 +147,14 @@ void *pull_stream_handler_thread(void *arg)
|
||||
AVStream *stream = fmt_ctx->streams[i];
|
||||
char pix_fmt_str[16];
|
||||
av_get_pix_fmt_string(pix_fmt_str, sizeof(pix_fmt_str), (AVPixelFormat)stream->codecpar->format);
|
||||
fprintf(stdout, "======= Stream Info =====\n");
|
||||
printf("Stream %d:\n", i);
|
||||
printf(" pixel_format: %s\n", pix_fmt_str);
|
||||
printf(" codec_type: %s\n", av_get_media_type_string(stream->codecpar->codec_type));
|
||||
printf(" codec_name: %s\n", avcodec_get_name(stream->codecpar->codec_id));
|
||||
fprintf(stdout, "=========================\n");
|
||||
log_info( "======= Stream Info =====");
|
||||
log_info( "Stream %d:", i);
|
||||
log_info( " pixel_format: %s", pix_fmt_str);
|
||||
log_info( " codec_type: %s", av_get_media_type_string(stream->codecpar->codec_type));
|
||||
log_info( " codec_name: %s", avcodec_get_name(stream->codecpar->codec_id));
|
||||
log_info( "=========================");
|
||||
}
|
||||
fprintf(stdout, "Stream handler thread started. Pull stream: %s\n", args->input_stream_url);
|
||||
log_info( "Stream handler thread started. Pull stream: %s", args->input_stream_url);
|
||||
Context *record_mp4_thread_ctx = CreateContext();
|
||||
Context *push_stream_thread_ctx = CreateContext();
|
||||
if (!record_mp4_thread_ctx || !push_stream_thread_ctx)
|
||||
@@ -221,14 +222,14 @@ void *pull_stream_handler_thread(void *arg)
|
||||
ret = avcodec_send_packet(codec_ctx, origin_packet);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Error: Failed to send packet to decoder (%s).\n", get_av_error(ret));
|
||||
log_info( "Error: Failed to send packet to decoder (%s).", get_av_error(ret));
|
||||
av_packet_unref(origin_packet);
|
||||
continue;
|
||||
}
|
||||
AVFrame *origin_frame = av_frame_alloc();
|
||||
if (!origin_frame)
|
||||
{
|
||||
fprintf(stdout, "Error: Failed to allocate frame(%s).\n", get_av_error(AVERROR(ENOMEM)));
|
||||
log_info( "Error: Failed to allocate frame(%s).", get_av_error(AVERROR(ENOMEM)));
|
||||
av_packet_unref(origin_packet);
|
||||
continue;
|
||||
}
|
||||
@@ -236,14 +237,14 @@ void *pull_stream_handler_thread(void *arg)
|
||||
ret = avcodec_receive_frame(codec_ctx, origin_frame);
|
||||
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
|
||||
{
|
||||
printf("No frame received from decoder.\n");
|
||||
log_info( "No frame received from decoder.");
|
||||
av_frame_free(&origin_frame);
|
||||
av_packet_unref(origin_packet);
|
||||
continue;
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Error: Failed to receive frame from decoder (%s).\n", get_av_error(ret));
|
||||
log_info( "Error: Failed to receive frame from decoder (%s).", get_av_error(ret));
|
||||
av_frame_free(&origin_frame);
|
||||
av_packet_unref(origin_packet);
|
||||
continue;
|
||||
@@ -260,7 +261,7 @@ void *pull_stream_handler_thread(void *arg)
|
||||
}
|
||||
av_packet_unref(origin_packet);
|
||||
}
|
||||
fprintf(stdout, "Stream handler thread stopped\n");
|
||||
log_info( "push_stream_thread ended.");
|
||||
// 取消上下文并销毁互斥锁
|
||||
if (push_stream_thread_ctx)
|
||||
{
|
||||
|
||||
+20
-19
@@ -15,36 +15,37 @@
|
||||
|
||||
#include "push_stream_thread.h"
|
||||
#include "libav_utils.h"
|
||||
#include "logger.h"
|
||||
// 初始化 RTMP 流上下文
|
||||
int init_rtmp_stream(RtmpStreamContext *ctx, const char *output_url, int width, int height, int fps)
|
||||
{
|
||||
if (!ctx || !output_url)
|
||||
{
|
||||
fprintf(stdout, "Invalid input parameters for init_rtmp_stream\n");
|
||||
log_info( "Invalid input parameters for init_rtmp_stream");
|
||||
return -1;
|
||||
}
|
||||
// 输出参数
|
||||
fprintf(stdout, "init_rtmp_stream === output_url=%s,width=%d,height=%d,fps=%d\n",
|
||||
output_url, width, height, fps);
|
||||
log_info( "init_rtmp_stream === output_url=%s,width=%d,height=%d,fps=%d",
|
||||
output_url, width, height, fps);
|
||||
// 创建输出上下文
|
||||
int ret = avformat_alloc_output_context2(&ctx->output_ctx, NULL, "flv", output_url);
|
||||
if (ret < 0 || !ctx->output_ctx)
|
||||
{
|
||||
fprintf(stdout, "Failed to create output context: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to create output context: %s", get_av_error(ret));
|
||||
return -1;
|
||||
}
|
||||
// 查找编码器
|
||||
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
|
||||
if (!codec)
|
||||
{
|
||||
fprintf(stdout, "H.264 encoder not found\n");
|
||||
log_info( "H.264 encoder not found");
|
||||
goto cleanup_output_context;
|
||||
}
|
||||
// 创建编码器上下文
|
||||
ctx->codec_ctx = avcodec_alloc_context3(codec);
|
||||
if (!ctx->codec_ctx)
|
||||
{
|
||||
fprintf(stdout, "Failed to allocate codec context\n");
|
||||
log_info( "Failed to allocate codec context");
|
||||
goto cleanup_output_context;
|
||||
}
|
||||
// 从输入流复制编解码器参数到编码器上下文
|
||||
@@ -53,7 +54,7 @@ int init_rtmp_stream(RtmpStreamContext *ctx, const char *output_url, int width,
|
||||
ret = avcodec_parameters_to_context(ctx->codec_ctx, ctx->input_stream->codecpar);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to copy codec parameters from input stream: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to copy codec parameters from input stream: %s", get_av_error(ret));
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
}
|
||||
@@ -70,21 +71,21 @@ int init_rtmp_stream(RtmpStreamContext *ctx, const char *output_url, int width,
|
||||
ctx->video_stream = avformat_new_stream(ctx->output_ctx, NULL);
|
||||
if (!ctx->video_stream)
|
||||
{
|
||||
fprintf(stdout, "Failed to create video stream\n");
|
||||
log_info( "Failed to create video stream");
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
// 关联编码器参数到输出流
|
||||
ret = avcodec_parameters_from_context(ctx->video_stream->codecpar, ctx->codec_ctx);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to copy codec parameters to output stream: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to copy codec parameters to output stream: %s", get_av_error(ret));
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
// 打开编码器
|
||||
ret = avcodec_open2(ctx->codec_ctx, codec, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to open codec: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to open codec: %s", get_av_error(ret));
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
// 打开网络输出
|
||||
@@ -93,7 +94,7 @@ int init_rtmp_stream(RtmpStreamContext *ctx, const char *output_url, int width,
|
||||
ret = avio_open(&ctx->output_ctx->pb, output_url, AVIO_FLAG_WRITE);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to open output URL: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to open output URL: %s", get_av_error(ret));
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
}
|
||||
@@ -101,7 +102,7 @@ int init_rtmp_stream(RtmpStreamContext *ctx, const char *output_url, int width,
|
||||
ret = avformat_write_header(ctx->output_ctx, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to write header: %d, %s\n", ret, get_av_error(ret));
|
||||
log_info( "Failed to write header: %d, %s", ret, get_av_error(ret));
|
||||
goto cleanup_io;
|
||||
}
|
||||
return 0;
|
||||
@@ -121,7 +122,7 @@ void push_stream(RtmpStreamContext *ctx, AVFrame *frame)
|
||||
{
|
||||
if (!ctx || !frame)
|
||||
{
|
||||
fprintf(stdout, "Invalid input parameters: RtmpStreamContext or AVFrame is NULL\n");
|
||||
log_info( "Invalid input parameters: RtmpStreamContext or AVFrame is NULL");
|
||||
return;
|
||||
}
|
||||
int ret = 0;
|
||||
@@ -129,13 +130,13 @@ void push_stream(RtmpStreamContext *ctx, AVFrame *frame)
|
||||
ret = avcodec_send_frame(ctx->codec_ctx, frame);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Error sending frame: %s\n", get_av_error(ret));
|
||||
log_info( "Error sending frame: %s", get_av_error(ret));
|
||||
return;
|
||||
}
|
||||
AVPacket *pkt = av_packet_alloc();
|
||||
if (!pkt)
|
||||
{
|
||||
fprintf(stdout, "Error allocating AVPacket\n");
|
||||
log_info( "Error allocating AVPacket");
|
||||
return;
|
||||
}
|
||||
// 接收编码后的数据包
|
||||
@@ -149,7 +150,7 @@ void push_stream(RtmpStreamContext *ctx, AVFrame *frame)
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Error encoding frame: %s\n", get_av_error(ret));
|
||||
log_info( "Error encoding frame: %s", get_av_error(ret));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ void push_stream(RtmpStreamContext *ctx, AVFrame *frame)
|
||||
ret = av_interleaved_write_frame(ctx->output_ctx, pkt);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Error writing packet: %d, %s\n", ret, get_av_error(ret));
|
||||
log_info( "Error writing packet: %d, %s", ret, get_av_error(ret));
|
||||
}
|
||||
// 释放数据包
|
||||
av_packet_unref(pkt);
|
||||
@@ -176,7 +177,7 @@ void *push_rtmp_handler_thread(void *arg)
|
||||
// 初始化输出流
|
||||
if (init_rtmp_stream(&ctx, args->output_stream_url, 1920, 1080, 25) < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to initialize RTMP stream\n");
|
||||
log_info( "Failed to initialize RTMP stream");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -196,7 +197,7 @@ void *push_rtmp_handler_thread(void *arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_info( "push_rtmp_handler_thread exit");
|
||||
av_write_trailer(ctx.output_ctx);
|
||||
avcodec_free_context(&ctx.codec_ctx);
|
||||
avio_closep(&ctx.output_ctx->pb);
|
||||
|
||||
+8
-7
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "sdl_utils.h"
|
||||
#include "frame_queue.h"
|
||||
#include "logger.h"
|
||||
void NV12ToRGB(uint8_t *y_plane, uint8_t *uv_plane, int width,
|
||||
int height, int y_pitch, int uv_pitch, uint8_t *rgb_buffer)
|
||||
{
|
||||
@@ -59,7 +60,7 @@ void SDLDisplayNV12Frame(SDL_Renderer *renderer, SDL_Texture *texture, AVFrame *
|
||||
frame->width <= 0 ||
|
||||
frame->height <= 0)
|
||||
{
|
||||
fprintf(stdout, "frame is null or invalid\n");
|
||||
log_info( "frame is null or invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ void SDLDisplayNV12Frame(SDL_Renderer *renderer, SDL_Texture *texture, AVFrame *
|
||||
uint8_t *rgb_buffer = (uint8_t *)malloc(width * height * 3);
|
||||
if (!rgb_buffer)
|
||||
{
|
||||
fprintf(stdout, "Failed to allocate RGB buffer\n");
|
||||
log_info( "Failed to allocate RGB buffer");
|
||||
return;
|
||||
}
|
||||
// 转换 NV12 为 RGB
|
||||
@@ -80,7 +81,7 @@ void SDLDisplayNV12Frame(SDL_Renderer *renderer, SDL_Texture *texture, AVFrame *
|
||||
// 更新纹理
|
||||
if (SDL_UpdateTexture(texture, NULL, rgb_buffer, width * 3) < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to update texture: %s\n", SDL_GetError());
|
||||
log_info( "Failed to update texture: %s", SDL_GetError());
|
||||
free(rgb_buffer);
|
||||
return;
|
||||
}
|
||||
@@ -103,14 +104,14 @@ void SDLDrawText(SDL_Renderer *renderer, SDL_Texture *texture, TTF_Font *font, c
|
||||
SDL_Surface *textSurface = TTF_RenderText_Solid(font, text, textColor);
|
||||
if (textSurface == NULL)
|
||||
{
|
||||
fprintf(stdout, "Failed to render text: %s\n", TTF_GetError());
|
||||
log_info( "Failed to render text: %s", TTF_GetError());
|
||||
return;
|
||||
}
|
||||
// 创建纹理
|
||||
SDL_Texture *textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
|
||||
if (textTexture == NULL)
|
||||
{
|
||||
fprintf(stdout, "Failed to create texture from surface: %s\n", SDL_GetError());
|
||||
log_info( "Failed to create texture from surface: %s", SDL_GetError());
|
||||
SDL_FreeSurface(textSurface);
|
||||
return;
|
||||
}
|
||||
@@ -128,7 +129,7 @@ void SDLDrawLabel(SDL_Renderer *renderer, TTF_Font *font, const char *text, int
|
||||
SDL_Surface *textSurface = TTF_RenderText_Solid(font, text, textColor);
|
||||
if (!textSurface)
|
||||
{
|
||||
printf("Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError());
|
||||
log_info( "Unable to render text surface! SDL_ttf Error: %s", TTF_GetError());
|
||||
return;
|
||||
}
|
||||
// 创建一个矩形作为背景
|
||||
@@ -143,7 +144,7 @@ void SDLDrawLabel(SDL_Renderer *renderer, TTF_Font *font, const char *text, int
|
||||
|
||||
if (!textTexture)
|
||||
{
|
||||
printf("Unable to create texture from rendered text! SDL_Error: %s\n", SDL_GetError());
|
||||
log_info( "Unable to create texture from rendered text! SDL_Error: %s", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -14,9 +14,10 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "thread_args.h"
|
||||
#include "logger.h"
|
||||
void dump_thread_args(ThreadArgs *args)
|
||||
{
|
||||
fprintf(stdout, "=== dump_thread_args ===\n");
|
||||
fprintf(stdout, "input_stream_url=%s\n", args->input_stream_url);
|
||||
fprintf(stdout, "output_stream_url=%s\n", args->output_stream_url);
|
||||
log_info( "=== dump_thread_args ===");
|
||||
log_info( "input_stream_url=%s", args->input_stream_url);
|
||||
log_info( "output_stream_url=%s", args->output_stream_url);
|
||||
}
|
||||
+24
-23
@@ -15,36 +15,37 @@
|
||||
|
||||
#include "video_record_thread.h"
|
||||
#include "libav_utils.h"
|
||||
#include "logger.h"
|
||||
// 初始化 RTMP 流上下文
|
||||
int init_mp4_stream(Mp4StreamContext *ctx, const char *output_url, int width, int height, int fps)
|
||||
{
|
||||
if (!ctx || !output_url)
|
||||
{
|
||||
fprintf(stdout, "Invalid input parameters for init_rtmp_stream\n");
|
||||
log_info( "Invalid input parameters for init_rtmp_stream");
|
||||
return -1;
|
||||
}
|
||||
// 输出参数
|
||||
fprintf(stdout, "init_rtmp_stream === output_url=%s,width=%d,height=%d,fps=%d\n",
|
||||
output_url, width, height, fps);
|
||||
log_info( "init_rtmp_stream === output_url=%s,width=%d,height=%d,fps=%d",
|
||||
output_url, width, height, fps);
|
||||
// 创建输出上下文
|
||||
int ret = avformat_alloc_output_context2(&ctx->output_ctx, NULL, "flv", output_url);
|
||||
if (ret < 0 || !ctx->output_ctx)
|
||||
{
|
||||
fprintf(stdout, "Failed to create output context: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to create output context: %s", get_av_error(ret));
|
||||
return -1;
|
||||
}
|
||||
// 查找编码器
|
||||
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
|
||||
if (!codec)
|
||||
{
|
||||
fprintf(stdout, "H.264 encoder not found\n");
|
||||
log_info( "H.264 encoder not found");
|
||||
goto cleanup_output_context;
|
||||
}
|
||||
// 创建编码器上下文
|
||||
ctx->codec_ctx = avcodec_alloc_context3(codec);
|
||||
if (!ctx->codec_ctx)
|
||||
{
|
||||
fprintf(stdout, "Failed to allocate codec context\n");
|
||||
log_info( "Failed to allocate codec context");
|
||||
goto cleanup_output_context;
|
||||
}
|
||||
// 从输入流复制编解码器参数到编码器上下文
|
||||
@@ -53,7 +54,7 @@ int init_mp4_stream(Mp4StreamContext *ctx, const char *output_url, int width, in
|
||||
ret = avcodec_parameters_to_context(ctx->codec_ctx, ctx->input_stream->codecpar);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to copy codec parameters from input stream: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to copy codec parameters from input stream: %s", get_av_error(ret));
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
}
|
||||
@@ -70,33 +71,33 @@ int init_mp4_stream(Mp4StreamContext *ctx, const char *output_url, int width, in
|
||||
ctx->video_stream = avformat_new_stream(ctx->output_ctx, NULL);
|
||||
if (!ctx->video_stream)
|
||||
{
|
||||
fprintf(stdout, "Failed to create video stream\n");
|
||||
log_info( "Failed to create video stream");
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
// 关联编码器参数到输出流
|
||||
ret = avcodec_parameters_from_context(ctx->video_stream->codecpar, ctx->codec_ctx);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to copy codec parameters to output stream: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to copy codec parameters to output stream: %s", get_av_error(ret));
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
// 打开编码器
|
||||
ret = avcodec_open2(ctx->codec_ctx, codec, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to open codec: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to open codec: %s", get_av_error(ret));
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
fprintf(stdout, "=== av_dump_format output.mp4 === \n");
|
||||
log_info( "=== av_dump_format output.mp4 === ");
|
||||
av_dump_format(ctx->output_ctx, 0, "output.mp4", 1);
|
||||
fprintf(stdout, "================================= \n");
|
||||
log_info( "================================= ");
|
||||
// 打开网络输出
|
||||
if (!(ctx->output_ctx->oformat->flags & AVFMT_NOFILE))
|
||||
{
|
||||
ret = avio_open(&ctx->output_ctx->pb, output_url, AVIO_FLAG_WRITE);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to open output URL: %s\n", get_av_error(ret));
|
||||
log_info( "Failed to open output URL: %s", get_av_error(ret));
|
||||
goto cleanup_codec_context;
|
||||
}
|
||||
}
|
||||
@@ -104,7 +105,7 @@ int init_mp4_stream(Mp4StreamContext *ctx, const char *output_url, int width, in
|
||||
ret = avformat_write_header(ctx->output_ctx, NULL);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to write header: %d, %s\n", ret, get_av_error(ret));
|
||||
log_info( "Failed to write header: %d, %s", ret, get_av_error(ret));
|
||||
goto cleanup_io;
|
||||
}
|
||||
return 0;
|
||||
@@ -124,7 +125,7 @@ void save_mp4(Mp4StreamContext *ctx, AVFrame *frame)
|
||||
{
|
||||
if (!ctx || !frame)
|
||||
{
|
||||
fprintf(stdout, "Invalid input parameters: RtmpStreamContext or AVFrame is NULL\n");
|
||||
log_info( "Invalid input parameters: RtmpStreamContext or AVFrame is NULL");
|
||||
return;
|
||||
}
|
||||
int ret = 0;
|
||||
@@ -132,13 +133,13 @@ void save_mp4(Mp4StreamContext *ctx, AVFrame *frame)
|
||||
ret = avcodec_send_frame(ctx->codec_ctx, frame);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Error sending frame: %s\n", get_av_error(ret));
|
||||
log_info( "Error sending frame: %s", get_av_error(ret));
|
||||
return;
|
||||
}
|
||||
AVPacket *pkt = av_packet_alloc();
|
||||
if (!pkt)
|
||||
{
|
||||
fprintf(stdout, "Error allocating AVPacket\n");
|
||||
log_info( "Error allocating AVPacket");
|
||||
return;
|
||||
}
|
||||
// 接收编码后的数据包
|
||||
@@ -152,7 +153,7 @@ void save_mp4(Mp4StreamContext *ctx, AVFrame *frame)
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Error encoding frame: %s\n", get_av_error(ret));
|
||||
log_info( "Error encoding frame: %s", get_av_error(ret));
|
||||
break;
|
||||
}
|
||||
av_packet_rescale_ts(pkt, ctx->input_stream->time_base, ctx->video_stream->time_base);
|
||||
@@ -160,7 +161,7 @@ void save_mp4(Mp4StreamContext *ctx, AVFrame *frame)
|
||||
ret = av_interleaved_write_frame(ctx->output_ctx, pkt);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "Error writing packet: %d, %s\n", ret, get_av_error(ret));
|
||||
log_info( "Error writing packet: %d, %s", ret, get_av_error(ret));
|
||||
}
|
||||
// 释放数据包
|
||||
av_packet_unref(pkt);
|
||||
@@ -177,14 +178,14 @@ void *save_mp4_handler_thread(void *arg)
|
||||
ctx.input_stream = args->input_stream;
|
||||
|
||||
// 初始化输出流
|
||||
fprintf(stdout, "Start save mp4 record thread\n");
|
||||
log_info( "Start save mp4 record thread");
|
||||
// 获取当前时间戳作为文件名
|
||||
time_t current_time = time(NULL);
|
||||
char file_name[100];
|
||||
strftime(file_name, sizeof(file_name), "./local_%Y%m%d_%H%M%S.mp4", localtime(¤t_time));
|
||||
if (init_mp4_stream(&ctx, file_name, 1920, 1080, 25) < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to initialize RTMP stream\n");
|
||||
log_info( "Failed to initialize RTMP stream");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -223,7 +224,7 @@ void *save_mp4_handler_thread(void *arg)
|
||||
ctx.input_stream = args->input_stream;
|
||||
if (init_mp4_stream(&ctx, file_name, 1920, 1080, 25) < 0)
|
||||
{
|
||||
fprintf(stdout, "Failed to initialize new MP4 stream\n");
|
||||
log_info( "Failed to initialize new MP4 stream");
|
||||
return NULL;
|
||||
}
|
||||
start_time = time(NULL);
|
||||
@@ -234,7 +235,7 @@ void *save_mp4_handler_thread(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, "Stop save mp4 record thread\n");
|
||||
log_info( "Stop save mp4 record thread");
|
||||
av_write_trailer(ctx.output_ctx);
|
||||
avio_closep(&ctx.output_ctx->pb);
|
||||
avcodec_free_context(&ctx.codec_ctx);
|
||||
|
||||
+65
-38
@@ -1,72 +1,74 @@
|
||||
// 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 "video_renderer.h"
|
||||
#define TARGET_FPS 30 // 目标帧率
|
||||
#include "logger.h"
|
||||
#define TARGET_FPS 25 // 目标帧率
|
||||
#define FRAME_TIME (1000 / TARGET_FPS) // 每帧目标时间 (毫秒)
|
||||
|
||||
void *video_renderer_thread(void *arg)
|
||||
{
|
||||
// 检查输入参数是否为空
|
||||
if (arg == NULL)
|
||||
{
|
||||
log_info( "Error: Input argument is NULL.");
|
||||
return NULL;
|
||||
}
|
||||
ThreadArgs *args = (ThreadArgs *)arg;
|
||||
|
||||
// 初始化 SDL 和 TTF
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
|
||||
{
|
||||
fprintf(stdout, "SDL_Init Error: %s\n", SDL_GetError());
|
||||
log_info( "SDL_Init Error: %s", SDL_GetError());
|
||||
return NULL;
|
||||
}
|
||||
if (TTF_Init() == -1)
|
||||
{
|
||||
fprintf(stdout, "SDL_ttf could not initialize! TTF_Error: %s\n", TTF_GetError());
|
||||
log_info( "SDL_ttf could not initialize! TTF_Error: %s", TTF_GetError());
|
||||
SDL_Quit();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 创建窗口
|
||||
SDL_Window *window = SDL_CreateWindow("VIDEO-PLAYER",
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
1920, 1080, SDL_WINDOW_SHOWN);
|
||||
if (!window)
|
||||
{
|
||||
fprintf(stdout, "SDL_CreateWindow Error: %s\n", SDL_GetError());
|
||||
log_info( "SDL_CreateWindow Error: %s", SDL_GetError());
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 创建渲染器
|
||||
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (!renderer)
|
||||
{
|
||||
fprintf(stdout, "SDL_CreateRenderer Error: %s\n", SDL_GetError());
|
||||
log_info( "SDL_CreateRenderer Error: %s", SDL_GetError());
|
||||
SDL_DestroyWindow(window);
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 创建纹理
|
||||
SDL_Texture *texture = SDL_CreateTexture(renderer,
|
||||
SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING,
|
||||
1920, 1080);
|
||||
if (!texture)
|
||||
{
|
||||
fprintf(stdout, "SDL_CreateTexture Error: %s\n", SDL_GetError());
|
||||
log_info( "SDL_CreateTexture Error: %s", SDL_GetError());
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 加载字体
|
||||
TTF_Font *font = TTF_OpenFont("mono.ttf", 18);
|
||||
if (font == NULL)
|
||||
{
|
||||
fprintf(stdout, "Failed to load font! TTF_Error: %s\n", TTF_GetError());
|
||||
log_info( "Failed to load font! TTF_Error: %s", TTF_GetError());
|
||||
SDL_DestroyTexture(texture);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
TTF_Quit();
|
||||
@@ -81,14 +83,16 @@ void *video_renderer_thread(void *arg)
|
||||
Uint64 lastFrameTime = SDL_GetPerformanceCounter();
|
||||
Uint64 performanceFrequency = SDL_GetPerformanceFrequency();
|
||||
SDL_Event event;
|
||||
// loop
|
||||
|
||||
// 主循环
|
||||
while (1)
|
||||
{
|
||||
Uint32 frameStart = SDL_GetTicks();
|
||||
|
||||
// 检查上下文是否被取消
|
||||
if (args->ctx->is_cancelled)
|
||||
{
|
||||
goto END;
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
@@ -109,12 +113,13 @@ void *video_renderer_thread(void *arg)
|
||||
newFrame->data[2], newFrame->linesize[2]);
|
||||
if (ret < 0)
|
||||
{
|
||||
fprintf(stdout, "SDL_UpdateYUVTexture failed: %s\n", SDL_GetError());
|
||||
log_info( "SDL_UpdateYUVTexture failed: %s", SDL_GetError());
|
||||
}
|
||||
av_frame_free(&newFrame);
|
||||
}
|
||||
}
|
||||
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
||||
|
||||
// 处理检测结果队列
|
||||
QueueItem boxes_item;
|
||||
if (async_dequeue(args->box_queue, &boxes_item))
|
||||
@@ -123,12 +128,17 @@ void *video_renderer_thread(void *arg)
|
||||
{
|
||||
for (int i = 0; i < boxes_item.box_count; ++i)
|
||||
{
|
||||
SDLDrawBox(renderer, font, boxes_item.Boxes[i].label,
|
||||
boxes_item.Boxes[i].x, boxes_item.Boxes[i].y,
|
||||
boxes_item.Boxes[i].w, boxes_item.Boxes[i].h, 1);
|
||||
// 检查边界框数据是否有效
|
||||
if (boxes_item.Boxes != NULL)
|
||||
{
|
||||
SDLDrawBox(renderer, font, boxes_item.Boxes[i].label,
|
||||
boxes_item.Boxes[i].x, boxes_item.Boxes[i].y,
|
||||
boxes_item.Boxes[i].w, boxes_item.Boxes[i].h, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算FPS并显示
|
||||
frameCount++;
|
||||
currentFrameTime = SDL_GetPerformanceCounter();
|
||||
@@ -159,25 +169,42 @@ void *video_renderer_thread(void *arg)
|
||||
{
|
||||
if (event.type == SDL_QUIT)
|
||||
{
|
||||
goto END;
|
||||
break;
|
||||
}
|
||||
else if (event.type == SDL_KEYDOWN)
|
||||
{
|
||||
if (event.key.keysym.sym == SDLK_ESCAPE)
|
||||
{
|
||||
goto END;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event.type == SDL_QUIT || event.key.keysym.sym == SDLK_ESCAPE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
END:
|
||||
SDL_DestroyTexture(texture);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
TTF_CloseFont(font);
|
||||
// 资源释放
|
||||
if (texture)
|
||||
{
|
||||
SDL_DestroyTexture(texture);
|
||||
}
|
||||
if (renderer)
|
||||
{
|
||||
SDL_DestroyRenderer(renderer);
|
||||
}
|
||||
if (window)
|
||||
{
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
if (font)
|
||||
{
|
||||
TTF_CloseFont(font);
|
||||
}
|
||||
TTF_Quit();
|
||||
SDL_Quit();
|
||||
pthread_exit(NULL);
|
||||
|
||||
log_info( "video_renderer_thread exit");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <unistd.h>
|
||||
#include "http_api.h"
|
||||
#include "libav_utils.h"
|
||||
#include "logger.h"
|
||||
// 全局变量
|
||||
static uint32_t interval_ms;
|
||||
static uint32_t threshold;
|
||||
@@ -34,8 +35,8 @@ static AVFrame *last_frame;
|
||||
//
|
||||
void print_warning_info(WarningInfo *info)
|
||||
{
|
||||
fprintf(stdout, "Warning triggered! Count: %u, Interval: %ums, Type: %s, Timestamp: %d\n",
|
||||
info->warning_count, info->interval_ms, info->coco_types, info->latest_warning_timestamp);
|
||||
log_info( "Warning triggered! Count: %u, Interval: %ums, Type: %s, Timestamp: %d",
|
||||
info->warning_count, info->interval_ms, info->coco_types, info->latest_warning_timestamp);
|
||||
}
|
||||
// 计时器线程函数
|
||||
static void *timer_thread_func(void *arg)
|
||||
@@ -84,10 +85,10 @@ int warning_timer_init(uint32_t interval_ms_param, uint32_t threshold_param, voi
|
||||
|
||||
if (pthread_create(&timer_thread, NULL, timer_thread_func, NULL) != 0)
|
||||
{
|
||||
perror("Failed to create timer thread");
|
||||
log_error( "Failed to create timer thread");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stdout, "Warning timer initialized!\n");
|
||||
log_info( "Warning timer initialized!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ void warning_timer_stop()
|
||||
{
|
||||
running = 0;
|
||||
pthread_join(timer_thread, NULL);
|
||||
fprintf(stdout, "Warning timer stopped!\n");
|
||||
log_info( "Warning timer stopped!");
|
||||
}
|
||||
// 触发事件的回调函数
|
||||
void event_triggered(WarningInfo *info)
|
||||
|
||||
Reference in New Issue
Block a user