summaryrefslogtreecommitdiff
path: root/src/contrib/SDL-3.2.20/test/testcamera.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testcamera.c')
-rw-r--r--src/contrib/SDL-3.2.20/test/testcamera.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testcamera.c b/src/contrib/SDL-3.2.20/test/testcamera.c
new file mode 100644
index 0000000..b270921
--- /dev/null
+++ b/src/contrib/SDL-3.2.20/test/testcamera.c
@@ -0,0 +1,384 @@
1/*
2 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely.
11*/
12
13#define SDL_MAIN_USE_CALLBACKS 1
14#include <SDL3/SDL_main.h>
15#include <SDL3/SDL_test.h>
16#include <SDL3/SDL_test_common.h>
17
18static SDL_Window *window = NULL;
19static SDL_Renderer *renderer = NULL;
20static SDLTest_CommonState *state = NULL;
21static SDL_Camera *camera = NULL;
22static SDL_Texture *texture = NULL;
23static bool texture_updated = false;
24static SDL_Surface *frame_current = NULL;
25static SDL_CameraID front_camera = 0;
26static SDL_CameraID back_camera = 0;
27
28/* For frequency logging */
29static Uint64 last_log_time = 0;
30static int iterate_count = 0;
31static int frame_count = 0;
32
33static void PrintCameraSpecs(SDL_CameraID camera_id)
34{
35 SDL_CameraSpec **specs = SDL_GetCameraSupportedFormats(camera_id, NULL);
36 if (specs) {
37 int i;
38
39 SDL_Log("Available formats:");
40 for (i = 0; specs[i]; ++i) {
41 const SDL_CameraSpec *s = specs[i];
42 SDL_Log(" %dx%d %.2f FPS %s", s->width, s->height, (float)s->framerate_numerator / s->framerate_denominator, SDL_GetPixelFormatName(s->format));
43 }
44 SDL_free(specs);
45 }
46}
47
48static void PickCameraSpec(SDL_CameraID camera_id, SDL_CameraSpec *spec)
49{
50 SDL_CameraSpec **specs = SDL_GetCameraSupportedFormats(camera_id, NULL);
51
52 SDL_zerop(spec);
53
54 if (specs) {
55 int i;
56
57 int max_size = (int)SDL_GetNumberProperty(SDL_GetRendererProperties(state->renderers[0]), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 0);
58 for (i = 0; specs[i]; ++i) {
59 const SDL_CameraSpec *s = specs[i];
60 if (s->width <= max_size && s->height <= max_size) {
61 SDL_copyp(spec, s);
62 break;
63 }
64 }
65 SDL_free(specs);
66 }
67}
68
69SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
70{
71 char window_title[128];
72 int devcount = 0;
73 int i;
74 const char *camera_name = NULL;
75 SDL_CameraSpec spec;
76
77 /* Initialize test framework */
78 state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO | SDL_INIT_CAMERA);
79 if (!state) {
80 return SDL_APP_FAILURE;
81 }
82
83 /* Parse commandline */
84 for (i = 1; i < argc;) {
85 int consumed;
86
87 consumed = SDLTest_CommonArg(state, i);
88 if (!consumed) {
89 if (SDL_strcmp(argv[i], "--camera") == 0 && argv[i+1]) {
90 camera_name = argv[i+1];
91 consumed = 2;
92 }
93 }
94 if (consumed <= 0) {
95 static const char *options[] = {
96 "[--camera name]",
97 NULL,
98 };
99 SDLTest_CommonLogUsage(state, argv[0], options);
100 return SDL_APP_FAILURE;
101 }
102 i += consumed;
103 }
104
105 state->num_windows = 1;
106
107 /* Load the SDL library */
108 if (!SDLTest_CommonInit(state)) {
109 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
110 return SDL_APP_FAILURE;
111 }
112
113 window = state->windows[0];
114 if (!window) {
115 SDL_Log("Couldn't create window: %s", SDL_GetError());
116 return SDL_APP_FAILURE;
117 }
118
119 renderer = state->renderers[0];
120 if (!renderer) {
121 /* SDL_Log("Couldn't create renderer: %s", SDL_GetError()); */
122 return SDL_APP_FAILURE;
123 }
124
125 SDL_Log("Using SDL camera driver: %s", SDL_GetCurrentCameraDriver());
126
127 SDL_CameraID *devices = SDL_GetCameras(&devcount);
128 if (!devices) {
129 SDL_Log("SDL_GetCameras failed: %s", SDL_GetError());
130 return SDL_APP_FAILURE;
131 }
132
133 SDL_CameraID camera_id = 0;
134
135 SDL_Log("Saw %d camera devices.", devcount);
136 for (i = 0; i < devcount; i++) {
137 const SDL_CameraID device = devices[i];
138 const char *name = SDL_GetCameraName(device);
139 const SDL_CameraPosition position = SDL_GetCameraPosition(device);
140 const char *posstr = "";
141 if (position == SDL_CAMERA_POSITION_FRONT_FACING) {
142 if (!front_camera) {
143 front_camera = device;
144 }
145 posstr = "[front-facing] ";
146 } else if (position == SDL_CAMERA_POSITION_BACK_FACING) {
147 if (!back_camera) {
148 back_camera = device;
149 }
150 posstr = "[back-facing] ";
151 }
152 if (camera_name && SDL_strcasecmp(name, camera_name) == 0) {
153 camera_id = device;
154 }
155 SDL_Log(" - Camera #%d: %s %s", i, posstr, name);
156
157 PrintCameraSpecs(device);
158 }
159
160 if (!camera_id) {
161 if (camera_name) {
162 SDL_Log("Could not find camera \"%s\"", camera_name);
163 return SDL_APP_FAILURE;
164 }
165 if (front_camera) {
166 camera_id = front_camera;
167 } else if (devcount > 0) {
168 camera_id = devices[0];
169 }
170 }
171 SDL_free(devices);
172
173 if (!camera_id) {
174 SDL_Log("No cameras available?");
175 return SDL_APP_FAILURE;
176 }
177
178 PickCameraSpec(camera_id, &spec);
179 camera = SDL_OpenCamera(camera_id, &spec);
180 if (!camera) {
181 SDL_Log("Failed to open camera device: %s", SDL_GetError());
182 return SDL_APP_FAILURE;
183 }
184
185 SDL_snprintf(window_title, sizeof (window_title), "testcamera: %s (%s)", SDL_GetCameraName(camera_id), SDL_GetCurrentCameraDriver());
186 SDL_SetWindowTitle(window, window_title);
187
188 return SDL_APP_CONTINUE;
189}
190
191
192static int FlipCamera(void)
193{
194 static Uint64 last_flip = 0;
195 if ((SDL_GetTicks() - last_flip) < 3000) { /* must wait at least 3 seconds between flips. */
196 return SDL_APP_CONTINUE;
197 }
198
199 if (camera) {
200 const SDL_CameraID current = SDL_GetCameraID(camera);
201 SDL_CameraID nextcam = 0;
202 if (current == front_camera) {
203 nextcam = back_camera;
204 } else if (current == back_camera) {
205 nextcam = front_camera;
206 }
207
208 if (nextcam) {
209 SDL_CameraSpec spec;
210
211 SDL_Log("Flip camera!");
212
213 if (frame_current) {
214 SDL_ReleaseCameraFrame(camera, frame_current);
215 frame_current = NULL;
216 }
217
218 SDL_CloseCamera(camera);
219
220 if (texture) {
221 SDL_DestroyTexture(texture);
222 texture = NULL; /* will rebuild when new camera is approved. */
223 }
224
225 PickCameraSpec(nextcam, &spec);
226 camera = SDL_OpenCamera(nextcam, &spec);
227 if (!camera) {
228 SDL_Log("Failed to open camera device: %s", SDL_GetError());
229 return SDL_APP_FAILURE;
230 }
231
232 last_flip = SDL_GetTicks();
233 }
234 }
235
236 return SDL_APP_CONTINUE;
237}
238
239SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
240{
241 switch (event->type) {
242 case SDL_EVENT_KEY_DOWN: {
243 const SDL_Keycode sym = event->key.key;
244 if (sym == SDLK_ESCAPE || sym == SDLK_AC_BACK) {
245 SDL_Log("Key : Escape!");
246 return SDL_APP_SUCCESS;
247 } else if (sym == SDLK_SPACE) {
248 FlipCamera();
249 return SDL_APP_CONTINUE;
250 }
251 break;
252 }
253
254 case SDL_EVENT_MOUSE_BUTTON_DOWN:
255 /* !!! FIXME: only flip if clicked in the area of a "flip" icon. */
256 return FlipCamera();
257
258 case SDL_EVENT_QUIT:
259 SDL_Log("Quit!");
260 return SDL_APP_SUCCESS;
261
262 case SDL_EVENT_CAMERA_DEVICE_APPROVED:
263 SDL_Log("Camera approved!");
264 SDL_CameraSpec camera_spec;
265 SDL_GetCameraFormat(camera, &camera_spec);
266 float fps = 0;
267 if (camera_spec.framerate_denominator != 0) {
268 fps = (float)camera_spec.framerate_numerator / (float)camera_spec.framerate_denominator;
269 }
270 SDL_Log("Camera Spec: %dx%d %.2f FPS %s",
271 camera_spec.width, camera_spec.height, fps, SDL_GetPixelFormatName(camera_spec.format));
272 break;
273
274 case SDL_EVENT_CAMERA_DEVICE_DENIED:
275 SDL_Log("Camera denied!");
276 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Camera permission denied!", "User denied access to the camera!", window);
277 return SDL_APP_FAILURE;
278 default:
279 break;
280 }
281
282 return SDLTest_CommonEventMainCallbacks(state, event);
283}
284
285SDL_AppResult SDL_AppIterate(void *appstate)
286{
287 iterate_count++;
288
289 Uint64 current_time = SDL_GetTicks();
290
291 /* If a minute has passed, log the frequencies and reset the counters */
292 if (current_time - last_log_time >= 60000) {
293 SDL_Log("SDL_AppIterate() called %d times in the last minute", iterate_count);
294 float fps = (float)frame_count / 60.0f;
295 SDL_Log("SDL_AcquireCameraFrame() FPS: %.2f", fps);
296
297 iterate_count = 0;
298 frame_count = 0;
299 last_log_time = current_time;
300 }
301
302 SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255);
303 SDL_RenderClear(renderer);
304
305 int win_w, win_h;
306 SDL_FRect d;
307 Uint64 timestampNS = 0;
308 SDL_Surface *frame_next = camera ? SDL_AcquireCameraFrame(camera, &timestampNS) : NULL;
309
310 #if 0
311 if (frame_next) {
312 SDL_Log("frame: %p at %" SDL_PRIu64, frame_next->pixels, timestampNS);
313 }
314 #endif
315
316 if (frame_next) {
317 frame_count++;
318
319 if (frame_current) {
320 SDL_ReleaseCameraFrame(camera, frame_current);
321 }
322
323 /* It's not needed to keep the frame once updated the texture is updated.
324 * But in case of 0-copy, it's needed to have the frame while using the texture.
325 */
326 frame_current = frame_next;
327 texture_updated = false;
328 }
329
330 if (frame_current) {
331 if (!texture ||
332 texture->w != frame_current->w || texture->h != frame_current->h) {
333 /* Resize the window to match */
334 SDL_SetWindowSize(window, frame_current->w, frame_current->h);
335
336 if (texture) {
337 SDL_DestroyTexture(texture);
338 }
339
340 SDL_Colorspace colorspace = SDL_GetSurfaceColorspace(frame_current);
341
342 /* Create texture with appropriate format */
343 SDL_PropertiesID props = SDL_CreateProperties();
344 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, frame_current->format);
345 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, colorspace);
346 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING);
347 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, frame_current->w);
348 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, frame_current->h);
349 texture = SDL_CreateTextureWithProperties(renderer, props);
350 SDL_DestroyProperties(props);
351 if (!texture) {
352 SDL_Log("Couldn't create texture: %s", SDL_GetError());
353 return SDL_APP_FAILURE;
354 }
355 }
356
357 /* Update SDL_Texture with last video frame (only once per new frame) */
358 if (frame_current && !texture_updated) {
359 SDL_UpdateTexture(texture, NULL, frame_current->pixels, frame_current->pitch);
360 texture_updated = true;
361 }
362
363 SDL_GetRenderOutputSize(renderer, &win_w, &win_h);
364 d.x = ((win_w - texture->w) / 2.0f);
365 d.y = ((win_h - texture->h) / 2.0f);
366 d.w = (float)texture->w;
367 d.h = (float)texture->h;
368 SDL_RenderTexture(renderer, texture, NULL, &d);
369 }
370
371 /* !!! FIXME: Render a "flip" icon if front_camera and back_camera are both != 0. */
372
373 SDL_RenderPresent(renderer);
374
375 return SDL_APP_CONTINUE;
376}
377
378void SDL_AppQuit(void *appstate, SDL_AppResult result)
379{
380 SDL_ReleaseCameraFrame(camera, frame_current);
381 SDL_CloseCamera(camera);
382 SDL_DestroyTexture(texture);
383 SDLTest_CommonQuit(state);
384}