1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
| #include "library.h"
#include "libuvc/libuvc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <direct.h> // 包含_mkdir函数
#define mkdir(dir, mode) _mkdir(dir) // 在Windows下重定义mkdir
#else
#include <sys/stat.h>
#include <sys/types.h>
#endif
/* 定义数据结构 */
#define FRAME_OFFSET 4640
#define FRAME_WIDTH 640
#define FRAME_HEIGHT 512
#define PIXEL_SIZE 2
#define FRAME_SIZE (FRAME_WIDTH * FRAME_HEIGHT * PIXEL_SIZE)
#define TOTAL_SIZE (FRAME_OFFSET + FRAME_SIZE * 2) // 温度数据 + 图像数据
/* 修改保存数据相关定义 */
#define SAVE_DIR "./thermal_data"
// 全局变量
static uvc_context_t *ctx = NULL;
static uvc_device_t *dev = NULL;
static uvc_device_handle_t *devh = NULL;
static uvc_stream_ctrl_t ctrl;
static int is_streaming = 0;
// 用户回调函数指针
static FrameCallback user_callback = NULL;
static void* user_data = NULL;
// 内部回调函数,将被传递给libuvc
static void internal_callback(uvc_frame_t *frame, void *ptr) {
printf("已接收帧:序列=%u, 数据字节=%lu\n", frame->sequence, frame->data_bytes);
// 如果用户提供了回调函数,调用它
if (user_callback) {
user_callback((unsigned char*)frame->data, (int)frame->data_bytes, frame->sequence);
}
}
// 保存帧数据到文件
UVC_THERMAL_API void save_frame_to_file(const char *prefix, void *data, int size, unsigned int frame_seq) {
char filename[256];
char timestamp[32];
time_t now;
struct tm *timeinfo;
// 创建保存目录
#ifdef _WIN32
mkdir(SAVE_DIR, 0);
#else
struct stat st = {0};
if (stat(SAVE_DIR, &st) == -1) {
mkdir(SAVE_DIR, 0755);
}
#endif
// 获取当前时间作为文件名
time(&now);
timeinfo = localtime(&now);
strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", timeinfo);
// 构建文件名
#ifdef _WIN32
snprintf(filename, sizeof(filename), "%s\\frame_%s_%s_%u.raw",
SAVE_DIR, prefix, timestamp, frame_seq);
#else
snprintf(filename, sizeof(filename), "%s/frame_%s_%s_%u.raw",
SAVE_DIR, prefix, timestamp, frame_seq);
#endif
// 保存数据
FILE *fp = fopen(filename, "wb");
if (!fp) {
printf("错误:无法打开文件 %s 进行写入\n", filename);
return;
}
size_t written = fwrite(data, 1, size, fp);
fclose(fp);
if (written == size) {
printf("已保存数据到 %s (%d 字节)\n", filename, size);
} else {
printf("错误:未能将所有数据写入 %s (%zu / %d 字节已写入)\n",
filename, written, size);
}
}
// 初始化相机
UVC_THERMAL_API int init_camera(void) {
uvc_error_t res;
// 如果已经初始化,先关闭
if (ctx != NULL) {
close_camera();
}
// 初始化UVC服务上下文
res = uvc_init(&ctx, NULL);
if (res < 0) {
uvc_perror(res, "uvc_init");
return res;
}
printf("UVC已初始化\n");
// 定位第一个连接的UVC设备
res = uvc_find_device(ctx, &dev, 0, 0, NULL);
if (res < 0) {
uvc_perror(res, "uvc_find_device");
uvc_exit(ctx);
ctx = NULL;
return res;
}
printf("已找到设备\n");
// 尝试打开设备
res = uvc_open(dev, &devh);
if (res < 0) {
uvc_perror(res, "uvc_open");
uvc_unref_device(dev);
dev = NULL;
uvc_exit(ctx);
ctx = NULL;
return res;
}
printf("设备已打开\n");
// 打印设备信息
uvc_print_diag(devh, stderr);
return 0;
}
// 启动相机流
UVC_THERMAL_API int start_camera(FrameCallback callback, void* user_ptr) {
uvc_error_t res;
if (devh == NULL) {
printf("错误:相机未初始化,请先调用init_camera()\n");
return -1;
}
if (is_streaming) {
printf("相机已经在流式传输中\n");
return 0;
}
// 保存用户回调和数据
user_callback = callback;
user_data = user_ptr;
// 获取格式描述符
const uvc_format_desc_t *format_desc = uvc_get_format_descs(devh);
enum uvc_frame_format frame_format = UVC_FRAME_FORMAT_YUYV;
int width = 640;
int height = 512;
int fps = 30;
// 查找对应的frame descriptor
const uvc_frame_desc_t *frame_desc = format_desc->frame_descs;
int found_frame = 0;
// 尝试找到640x1033的帧描述符
while (frame_desc) {
if (frame_desc->wWidth == 640 && frame_desc->wHeight == 1033) {
fps = 10000000 / frame_desc->dwDefaultFrameInterval;
width = 640;
height = 1033;
found_frame = 1;
printf("\n找到帧格式:%dx%d %dfps\n", width, height, fps);
break;
}
frame_desc = frame_desc->next;
}
if (!found_frame) {
// 如果没找到,使用默认的640x512
printf("\n使用默认帧格式:640x512\n");
width = 640;
height = 512;
}
// 设置流控制参数
memset(&ctrl, 0, sizeof(ctrl));
// 尝试协商流参数
res = uvc_get_stream_ctrl_format_size(
devh, &ctrl,
frame_format,
width, height, fps
);
// 打印结果
uvc_print_stream_ctrl(&ctrl, stderr);
if (res < 0) {
uvc_perror(res, "get_mode");
// 尝试使用另一种方式设置流控制
printf("\n尝试替代方法设置流控制...\n");
// 手动设置流控制参数
memset(&ctrl, 0, sizeof(ctrl));
ctrl.bmHint = 1;
ctrl.bFormatIndex = 1; // YUYV格式索引
ctrl.bFrameIndex = found_frame ? 4 : 2; // 使用找到的帧索引或默认值
ctrl.dwFrameInterval = 333333; // 30 fps
ctrl.wDelay = 0;
ctrl.dwMaxVideoFrameSize = found_frame ? 1315360 : 655360; // 根据帧大小设置
ctrl.dwMaxPayloadTransferSize = 14336;
ctrl.bInterfaceNumber = 1;
// 打印手动设置的流控制参数
printf("\n手动设置的流控制参数:\n");
uvc_print_stream_ctrl(&ctrl, stderr);
}
printf("\n尝试格式:自定义 %dx%d 30fps (总大小:%d 字节)\n",
FRAME_WIDTH, FRAME_HEIGHT, TOTAL_SIZE);
// 开始视频流
res = uvc_start_streaming(devh, &ctrl, internal_callback, user_data, 0);
if (res < 0) {
uvc_perror(res, "start_streaming");
return res;
}
printf("正在流式传输...\n");
is_streaming = 1;
// 启用自动曝光
printf("启用自动曝光...\n");
const uint8_t UVC_AUTO_EXPOSURE_MODE_AUTO = 2;
res = uvc_set_ae_mode(devh, UVC_AUTO_EXPOSURE_MODE_AUTO);
if (res == UVC_SUCCESS) {
printf(" ... 已启用自动曝光\n");
} else if (res == UVC_ERROR_PIPE) {
// 尝试光圈优先模式
printf(" ... 不支持完整AE,尝试光圈优先模式\n");
const uint8_t UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY = 8;
res = uvc_set_ae_mode(devh, UVC_AUTO_EXPOSURE_MODE_APERTURE_PRIORITY);
if (res < 0) {
uvc_perror(res, " ... uvc_set_ae_mode无法启用光圈优先模式");
} else {
printf(" ... 已启用光圈优先自动曝光模式\n");
}
} else {
uvc_perror(res, " ... uvc_set_ae_mode无法启用自动曝光模式");
}
return 0;
}
// 停止相机流
UVC_THERMAL_API void stop_camera(void) {
if (devh != NULL && is_streaming) {
uvc_stop_streaming(devh);
is_streaming = 0;
printf("流式传输已停止\n");
}
}
// 关闭相机
UVC_THERMAL_API void close_camera(void) {
// 如果正在流式传输,先停止
if (is_streaming) {
stop_camera();
}
// 关闭设备句柄
if (devh != NULL) {
uvc_close(devh);
devh = NULL;
printf("设备已关闭\n");
}
// 释放设备描述符
if (dev != NULL) {
uvc_unref_device(dev);
dev = NULL;
}
// 关闭UVC上下文
if (ctx != NULL) {
uvc_exit(ctx);
ctx = NULL;
printf("UVC已退出\n");
}
}
// 原始的hello函数
UVC_THERMAL_API void hello(void) {
printf("Hello, World!\n");
}
|