UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
ximea.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2019-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#endif // HAVE_CONFIG_H
41#include "config_unix.h"
42#include "config_win32.h"
43
44#if defined(__APPLE__)
45#include <m3api/xiApi.h>
46#else
47#include <xiApi.h>
48#endif
49
50#include "debug.h"
51#include "compat/dlfunc.h"
52#include "lib_common.h"
53#include "utils/color_out.h"
54#include "utils/macros.h"
55#include "tv.h" // US_IN_SEC
56#include "video.h"
57#include "video_capture.h"
58
59#define DEFAULT_TIMEOUT_MS 100
60#define EXPOSURE_DEFAULT_US 33333L
61#define MAGIC to_fourcc('X', 'I', 'M', 'E')
62#define MOD_NAME "[XIMEA] "
63#define MICROSEC_IN_SEC 1000000.0
64
66 XI_RETURN __cdecl (*xiGetNumberDevices)(OUT PDWORD pNumberDevices);
67 XI_RETURN __cdecl (*xiGetDeviceInfoString)(IN DWORD DevId, const char* prm, char* value, DWORD value_size);
68 XI_RETURN __cdecl (*xiOpenDevice)(IN DWORD DevId, OUT PHANDLE hDevice);
69 XI_RETURN __cdecl (*xiSetParamInt)(IN HANDLE hDevice, const char* prm, const int val);
70 XI_RETURN __cdecl (*xiStartAcquisition)(IN HANDLE hDevice);
71 XI_RETURN __cdecl (*xiGetImage)(IN HANDLE hDevice, IN DWORD timeout, OUT LPXI_IMG img);
72 XI_RETURN __cdecl (*xiStopAcquisition)(IN HANDLE hDevice);
73 XI_RETURN __cdecl (*xiCloseDevice)(IN HANDLE hDevice);
75};
76
78 uint32_t magic;
81
82 HANDLE xiH;
84};
85
86#define GET_SYMBOL(sym) do {\
87 f->sym = (void *) dlsym(f->handle, #sym);\
88 if (f->sym == NULL) {\
89 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to find symbol %s: %s\n", #sym, dlerror());\
90 return false;\
91 }\
92} while(0)
94#ifdef XIMEA_RUNTIME_LINKING
95 f->handle = dlopen(XIMEA_LIBRARY_NAME, RTLD_NOW);
96 if (!f->handle) {
97 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Unable to open library %s): %s!\n",
98 XIMEA_LIBRARY_NAME, dlerror());
99 char path[strlen(XIMEA_LIBRARY_PATH) + 1 + strlen(XIMEA_LIBRARY_NAME) + 1];
100 strcpy(path, XIMEA_LIBRARY_PATH);
101 strcat(path, "/");
102 strcat(path, XIMEA_LIBRARY_NAME);
103 f->handle = dlopen(path, RTLD_NOW);
104 }
105 if (!f->handle) {
106 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to open library (name: %s, path: %s): %s!\n",
107 XIMEA_LIBRARY_NAME, XIMEA_LIBRARY_PATH, dlerror());
108 return false;
109 }
118#else
127#endif
128
129 return true;
130}
131
133{
134 if (f->handle == NULL) {
135 return;
136 }
137#ifdef XIMEA_RUNTIME_LINKING
138 dlclose(f->handle);
139#endif
140}
141
142
144 color_printf("XIMEA usage:\n");
145 color_printf(TERM_BOLD TERM_FG_RED "\t-t ximea" TERM_FG_RESET "[:device=<d>][:exposure=<time_us>|:fps=<fps>]\n" TERM_RESET);
146 color_printf("where\n");
147 color_printf("\t" TBOLD("exposure") " - exposure time in microseconds (default: %ld)\n", EXPOSURE_DEFAULT_US);
148 color_printf("\t" TBOLD("fps") " - frames per second (decimal, exclusive with exposure parameter)\n");
149 printf("\n");
150 printf("Available devices:\n");
151
152 struct ximea_functions funcs;
153 if (!vidcap_ximea_fill_symbols(&funcs)) {
154 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot load XIMEA library!\n");
155 return;
156 }
157
158 DWORD count;
159 XI_RETURN ret = funcs.xiGetNumberDevices(&count);
160 if (ret != XI_OK) {
161 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to get device count!\n");
162 return;
163 }
164
165 if (count == 0) {
166 log_msg(LOG_LEVEL_ERROR, MOD_NAME "No devices found!\n");
167 return;
168 }
169
170 for (DWORD i = 0; i < count; ++i) {
171 char name[256];
172 color_printf(TERM_BOLD "\t%d) " TERM_RESET, (int) i);
173 if (funcs.xiGetDeviceInfoString(i, XI_PRM_DEVICE_NAME, name, sizeof name) == XI_OK) {
174 color_printf("%s", name);
175 }
176 color_printf("\n");
177 }
179}
180
181static int vidcap_ximea_parse_params(struct state_vidcap_ximea *s, char *cfg) {
182 char *save_ptr = NULL;
183 char *tmp = cfg;
184 char *tok = NULL;
185 while ((tok = strtok_r(tmp, ":", &save_ptr)) != NULL) {
186 if (!strcmp(tok, "help")) {
188 return VIDCAP_INIT_NOERR;
189 }
190 if (strstr(tok, "device=")) {
191 char *endptr = NULL;
192 s->device_id = strtol(tok + strlen("device="), &endptr, 0);
193 if (*endptr != '\0') {
194 return VIDCAP_INIT_FAIL;
195 }
196 } else if (strstr(tok, "exposure=")) {
197 char *endptr = NULL;
198 s->exposure_time_us = strtol(tok + strlen("exposure="), &endptr, 0);
199 if (*endptr != '\0' || s->exposure_time_us < 0) {
200 return VIDCAP_INIT_FAIL;
201 }
202 } else if (strstr(tok, "fps=") != NULL) {
203 char *endptr = NULL;
204 s->exposure_time_us = US_IN_SEC / strtod(strchr(tok, '=') + 1, &endptr);
205 if (*endptr != '\0' || s->exposure_time_us < 0) {
206 return VIDCAP_INIT_FAIL;
207 }
208 } else {
209 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unknown option: %s\n", tok);
210 return VIDCAP_INIT_FAIL;
211 }
212 tmp = NULL;
213 }
214
215 return 0;
216}
217
218#define CHECK(cmd) do { \
219 XI_RETURN ret = cmd; \
220 if (ret != XI_OK) { \
221 log_msg(LOG_LEVEL_ERROR, MOD_NAME "%s: %d\n", #cmd, (int) ret); \
222 goto error; \
223 } \
224} while(0)
225static int vidcap_ximea_init(struct vidcap_params *params, void **state)
226{
229 }
230 struct state_vidcap_ximea *s = calloc(1, sizeof(struct state_vidcap_ximea));
231 s->magic = MAGIC;
234 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot load XIMEA library!\n");
235 goto error;
236 }
237 char *cfg = strdup(vidcap_params_get_fmt(params));
238 int ret = vidcap_ximea_parse_params(s, cfg);
239 free(cfg);
240 if (ret != 0) {
241 free(s);
242 return ret;
243 }
244
245 CHECK(s->funcs.xiOpenDevice(s->device_id, &s->xiH));
246 CHECK(s->funcs.xiSetParamInt(s->xiH, XI_PRM_EXPOSURE, s->exposure_time_us));
247 CHECK(s->funcs.xiSetParamInt(s->xiH, XI_PRM_IMAGE_DATA_FORMAT, XI_RGB24));
248 CHECK(s->funcs.xiSetParamInt(s->xiH, XI_PRM_AUTO_WB, XI_ON));
250
251 *state = s;
252 return VIDCAP_INIT_OK;
253
254error:
255 free(s);
256 return VIDCAP_INIT_FAIL;
257}
258
259static void vidcap_ximea_done(void *state)
260{
261 struct state_vidcap_ximea *s = (struct state_vidcap_ximea *) state;
262 assert(s->magic == MAGIC);
264 s->funcs.xiCloseDevice(s->xiH);
266 free(s);
267}
268
269static struct video_frame *vidcap_ximea_grab(void *state, struct audio_frame **audio)
270{
271 struct state_vidcap_ximea *s = (struct state_vidcap_ximea *) state;
272 assert(s->magic == MAGIC);
273 int timeout_ms = DEFAULT_TIMEOUT_MS;
274
275 XI_IMG img;
276 memset(&img, 0, sizeof img);
277 img.size = sizeof img;
278
279 *audio = NULL;
280 XI_RETURN ret = s->funcs.xiGetImage(s->xiH, timeout_ms, &img);
281 if (ret != XI_OK) {
282 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot capture frame: %d", (int) ret);
283 return NULL;
284 }
285
286 struct video_desc d;
287 d.width = img.width;
288 d.height = img.height;
289 d.color_spec = BGR;
290 d.tile_count = 1;
292 d.fps = MICROSEC_IN_SEC / (double) s->exposure_time_us;
293
294 struct video_frame *out = vf_alloc_desc(d);
295 out->tiles[0].data = img.bp;
296
297 return out;
298}
299
300static void vidcap_ximea_probe(struct device_info **available_cards, int *count, void (**deleter)(void *))
301{
302 *deleter = free;
303 *count = 0;
304
305 struct ximea_functions funcs;
306 if (!vidcap_ximea_fill_symbols(&funcs)) {
307 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot load XIMEA library!\n");
308 return;
309 }
310
311 DWORD card_count;
312 XI_RETURN ret = funcs.xiGetNumberDevices(&card_count);
313 if (ret != XI_OK) {
314 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to get device count!\n");
316 return;
317 }
318
319 struct device_info *cards = calloc(card_count, sizeof(struct device_info));
320 for (DWORD i = 0; i < card_count; ++i) {
321 snprintf(cards[i].dev, sizeof cards[i].dev, ":device=%d", (int) i);
322 char name[256];
323 if (funcs.xiGetDeviceInfoString(i, XI_PRM_DEVICE_NAME, name, sizeof name) == XI_OK) {
324 strncpy(cards[i].name, name, sizeof cards[i].name);
325 }
326 }
328
329 *available_cards = cards;
330 *count = card_count;
331}
332
340
342
void ** state
int void * val
struct audio_frame ** audio
int void(** deleter)(void *))
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
int color_printf(const char *format,...)
#define TERM_BOLD
Definition color_out.h:48
#define TERM_FG_RED
Definition color_out.h:50
#define TERM_RESET
Definition color_out.h:47
#define TERM_FG_RESET
Definition color_out.h:59
#define TBOLD(x)
Definition color_out.h:70
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define LIB_HANDLE
Definition dlfunc.h:51
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
#define VIDCAP_INIT_AUDIO_NOT_SUPPORTED
card does not support audio
int count
Definition hd-rum.c:44
@ LIBRARY_CLASS_VIDEO_CAPTURE
Definition lib_common.h:78
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
char dev[1024]
Definition types.h:383
uint32_t magic
Definition ximea.c:78
struct ximea_functions funcs
Definition ximea.c:83
long exposure_time_us
Definition ximea.c:80
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int tile_count
Definition types.h:185
codec_t color_spec
Definition types.h:182
enum interlacing_t interlacing
Definition types.h:184
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
XI_RETURN __cdecl(* xiSetParamInt)(IN HANDLE hDevice, const char *prm, const int val)
Definition ximea.c:69
XI_RETURN __cdecl(* xiGetImage)(IN HANDLE hDevice, IN DWORD timeout, OUT LPXI_IMG img)
Definition ximea.c:71
XI_RETURN __cdecl(* xiGetNumberDevices)(OUT PDWORD pNumberDevices)
Definition ximea.c:66
LIB_HANDLE handle
Definition ximea.c:74
XI_RETURN __cdecl(* xiOpenDevice)(IN DWORD DevId, OUT PHANDLE hDevice)
Definition ximea.c:68
XI_RETURN __cdecl(* xiGetDeviceInfoString)(IN DWORD DevId, const char *prm, char *value, DWORD value_size)
Definition ximea.c:67
XI_RETURN __cdecl(* xiCloseDevice)(IN HANDLE hDevice)
Definition ximea.c:73
XI_RETURN __cdecl(* xiStopAcquisition)(IN HANDLE hDevice)
Definition ximea.c:72
XI_RETURN __cdecl(* xiStartAcquisition)(IN HANDLE hDevice)
Definition ximea.c:70
#define US_IN_SEC
Definition tv.h:89
@ PROGRESSIVE
progressive frame
Definition types.h:164
@ BGR
8-bit BGR
Definition types.h:88
This is an umbrella header for video functions.
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
unsigned int vidcap_params_get_flags(const struct vidcap_params *params)
#define VIDCAP_FLAG_AUDIO_ANY
#define CHECK(cmd)
Definition ximea.c:218
static void vidcap_ximea_done(void *state)
Definition ximea.c:259
static const struct video_capture_info vidcap_ximea_info
Definition ximea.c:333
static int vidcap_ximea_parse_params(struct state_vidcap_ximea *s, char *cfg)
Definition ximea.c:181
static void vidcap_ximea_close_lib(struct ximea_functions *f)
Definition ximea.c:132
static void vidcap_ximea_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
Definition ximea.c:300
#define GET_SYMBOL(sym)
Definition ximea.c:86
static void vidcap_ximea_show_help()
Definition ximea.c:143
static bool vidcap_ximea_fill_symbols(struct ximea_functions *f)
Definition ximea.c:93
static struct video_frame * vidcap_ximea_grab(void *state, struct audio_frame **audio)
Definition ximea.c:269
#define MAGIC
Definition ximea.c:61
#define EXPOSURE_DEFAULT_US
Definition ximea.c:60
#define MOD_NAME
Definition ximea.c:62
static int vidcap_ximea_init(struct vidcap_params *params, void **state)
Definition ximea.c:225
#define MICROSEC_IN_SEC
Definition ximea.c:63
#define DEFAULT_TIMEOUT_MS
Definition ximea.c:59