#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
static void shim_sleep_ms(int ms) { Sleep((DWORD)ms); }
#else
# include <unistd.h>
static void shim_sleep_ms(int ms) { usleep((useconds_t)(ms) * 1000u); }
#endif
#include "tl_camera_sdk.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
typedef struct TsiCtx {
void* handle;
int sensor_type;
int bit_depth;
int bytes_per_pixel;
int sensor_width;
int sensor_height;
int img_width;
int img_height;
uint16_t* image_buf;
size_t image_buf_bytes;
int frame_bytes;
volatile int frame_ready;
int in_sequence;
} TsiCtx;
static void frame_callback(
void* sender,
unsigned short* image_buffer,
int frame_count,
unsigned char* metadata,
int metadata_size,
void* context)
{
TsiCtx* ctx = (TsiCtx*)context;
if (!ctx || !image_buffer) return;
int fb = ctx->img_width * ctx->img_height * ctx->bytes_per_pixel;
if (fb <= 0) return;
if (fb > (int)ctx->image_buf_bytes) {
free(ctx->image_buf);
ctx->image_buf = (uint16_t*)malloc((size_t)fb);
if (!ctx->image_buf) { ctx->image_buf_bytes = 0; return; }
ctx->image_buf_bytes = (size_t)fb;
}
memcpy(ctx->image_buf, image_buffer, (size_t)fb);
ctx->frame_bytes = fb;
ctx->frame_ready = 1;
}
static void refresh_image_dims(TsiCtx* ctx) {
if (!ctx || !ctx->handle) return;
tl_camera_get_image_width(ctx->handle, &ctx->img_width);
tl_camera_get_image_height(ctx->handle, &ctx->img_height);
}
static int wait_for_frame(TsiCtx* ctx, int timeout_ms) {
int elapsed = 0;
while (!ctx->frame_ready) {
if (elapsed >= timeout_ms) return -1;
shim_sleep_ms(1);
elapsed++;
}
ctx->frame_ready = 0;
return 0;
}
int tsi_sdk_open(void) {
#ifdef _WIN32
if (tl_camera_sdk_dll_initialize() != 0) return -1;
#endif
return tl_camera_open_sdk() == 0 ? 0 : -1;
}
void tsi_sdk_close(void) {
tl_camera_close_sdk();
#ifdef _WIN32
tl_camera_sdk_dll_uninitialize();
#endif
}
int tsi_discover_cameras(char* buf, int len) {
if (!buf || len <= 0) return -1;
buf[0] = '\0';
if (tl_camera_discover_available_cameras(buf, len) != 0) return -1;
if (buf[0] == '\0') return 0;
int count = 1;
for (int i = 0; buf[i]; i++) {
if (buf[i] == ' ' && buf[i + 1] != '\0') count++;
}
return count;
}
TsiCtx* tsi_open_camera(const char* camera_id) {
if (!camera_id) return NULL;
void* handle = NULL;
if (tl_camera_open_camera(camera_id, &handle) != 0 || !handle)
return NULL;
TsiCtx* ctx = (TsiCtx*)calloc(1, sizeof(TsiCtx));
if (!ctx) { tl_camera_close_camera(handle); return NULL; }
ctx->handle = handle;
tl_camera_set_operation_mode(handle, TL_CAMERA_OPERATION_MODE_SOFTWARE_TRIGGERED);
tl_camera_get_camera_sensor_type(handle, &ctx->sensor_type);
tl_camera_get_bit_depth(handle, &ctx->bit_depth);
ctx->bytes_per_pixel = 2;
tl_camera_get_image_width(handle, &ctx->sensor_width);
tl_camera_get_image_height(handle, &ctx->sensor_height);
ctx->img_width = ctx->sensor_width;
ctx->img_height = ctx->sensor_height;
tl_camera_set_frame_available_callback(handle, frame_callback, ctx);
return ctx;
}
void tsi_close_camera(TsiCtx* ctx) {
if (!ctx) return;
if (ctx->in_sequence) {
tl_camera_disarm(ctx->handle);
ctx->in_sequence = 0;
}
tl_camera_close_camera(ctx->handle);
free(ctx->image_buf);
free(ctx);
}
int tsi_get_image_width(TsiCtx* ctx) { return ctx ? ctx->img_width : 0; }
int tsi_get_image_height(TsiCtx* ctx) { return ctx ? ctx->img_height : 0; }
int tsi_get_sensor_width(TsiCtx* ctx) { return ctx ? ctx->sensor_width : 0; }
int tsi_get_sensor_height(TsiCtx* ctx) { return ctx ? ctx->sensor_height : 0; }
int tsi_get_bit_depth(TsiCtx* ctx) { return ctx ? ctx->bit_depth : 0; }
int tsi_get_bytes_per_pixel(TsiCtx* ctx) { return ctx ? ctx->bytes_per_pixel : 0; }
int tsi_get_sensor_type(TsiCtx* ctx) { return ctx ? ctx->sensor_type : 0; }
int tsi_get_serial_number(TsiCtx* ctx, char* buf, int len) {
if (!ctx || !buf) return -1;
return tl_camera_get_serial_number(ctx->handle, buf, len) == 0 ? 0 : -1;
}
int tsi_get_firmware_version(TsiCtx* ctx, char* buf, int len) {
if (!ctx || !buf) return -1;
return tl_camera_get_firmware_version(ctx->handle, buf, len) == 0 ? 0 : -1;
}
long long tsi_get_exposure_us(TsiCtx* ctx) {
if (!ctx) return -1;
long long v = 0;
tl_camera_get_exposure_time(ctx->handle, &v);
return v;
}
int tsi_set_exposure_us(TsiCtx* ctx, long long us) {
if (!ctx) return -1;
return tl_camera_set_exposure_time(ctx->handle, us) == 0 ? 0 : -1;
}
int tsi_get_exposure_range_us(TsiCtx* ctx, long long* min_out, long long* max_out) {
if (!ctx) return -1;
return tl_camera_get_exposure_time_range(ctx->handle, min_out, max_out) == 0 ? 0 : -1;
}
int tsi_set_roi(TsiCtx* ctx, int x, int y, int w, int h) {
if (!ctx) return -1;
int x2 = x + w - 1;
int y2 = y + h - 1;
if (tl_camera_set_roi(ctx->handle, x, y, x2, y2) != 0) return -1;
refresh_image_dims(ctx);
return 0;
}
int tsi_clear_roi(TsiCtx* ctx) {
if (!ctx) return -1;
tl_camera_set_roi(ctx->handle, 0, 0,
ctx->sensor_width - 1,
ctx->sensor_height - 1);
refresh_image_dims(ctx);
return 0;
}
int tsi_get_roi(TsiCtx* ctx, int* x, int* y, int* w, int* h) {
if (!ctx) return -1;
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
if (tl_camera_get_roi(ctx->handle, &x1, &y1, &x2, &y2) != 0) return -1;
*x = x1; *y = y1;
*w = x2 - x1 + 1; *h = y2 - y1 + 1;
return 0;
}
int tsi_get_binx(TsiCtx* ctx) {
if (!ctx) return 1;
int v = 1;
tl_camera_get_binx(ctx->handle, &v);
return v;
}
int tsi_get_biny(TsiCtx* ctx) {
if (!ctx) return 1;
int v = 1;
tl_camera_get_biny(ctx->handle, &v);
return v;
}
int tsi_set_binx(TsiCtx* ctx, int val) {
if (!ctx) return -1;
if (tl_camera_set_binx(ctx->handle, val) != 0) return -1;
refresh_image_dims(ctx);
return 0;
}
int tsi_set_biny(TsiCtx* ctx, int val) {
if (!ctx) return -1;
if (tl_camera_set_biny(ctx->handle, val) != 0) return -1;
refresh_image_dims(ctx);
return 0;
}
int tsi_get_binx_range(TsiCtx* ctx, int* min_out, int* max_out) {
if (!ctx) return -1;
return tl_camera_get_binx_range(ctx->handle, min_out, max_out) == 0 ? 0 : -1;
}
int tsi_snap(TsiCtx* ctx, int timeout_ms) {
if (!ctx || ctx->in_sequence) return -1;
ctx->frame_ready = 0;
tl_camera_set_frames_per_trigger_zero_for_unlimited(ctx->handle, 1);
if (tl_camera_arm(ctx->handle, 2) != 0) return -1;
if (tl_camera_issue_software_trigger(ctx->handle) != 0) {
tl_camera_disarm(ctx->handle);
return -1;
}
int ret = wait_for_frame(ctx, timeout_ms);
tl_camera_disarm(ctx->handle);
return ret;
}
const uint16_t* tsi_get_frame_ptr(TsiCtx* ctx) {
return ctx ? ctx->image_buf : NULL;
}
int tsi_get_frame_bytes(TsiCtx* ctx) {
return ctx ? ctx->frame_bytes : 0;
}
int tsi_start_cont(TsiCtx* ctx) {
if (!ctx || ctx->in_sequence) return -1;
ctx->frame_ready = 0;
tl_camera_set_frames_per_trigger_zero_for_unlimited(ctx->handle, 0);
if (tl_camera_arm(ctx->handle, 8) != 0) return -1;
if (tl_camera_issue_software_trigger(ctx->handle) != 0) {
tl_camera_disarm(ctx->handle);
return -1;
}
ctx->in_sequence = 1;
return 0;
}
int tsi_get_next_frame(TsiCtx* ctx, int timeout_ms) {
if (!ctx || !ctx->in_sequence) return -1;
ctx->frame_ready = 0;
return wait_for_frame(ctx, timeout_ms);
}
int tsi_stop_cont(TsiCtx* ctx) {
if (!ctx) return -1;
if (!ctx->in_sequence) return 0;
tl_camera_disarm(ctx->handle);
ctx->in_sequence = 0;
return 0;
}