Atlas - testcamera.c
Home / ext / SDL / test Lines: 1 | Size: 12756 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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, ×tampNS) : 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 // the image might be coming from a mobile device that provides images in only one orientation, but the 364 // device might be rotated to a different one (like an iPhone providing portrait images even if you hold 365 // the phone in landscape mode). The rotation is how far to rotate the image clockwise to put it right-side 366 // up, for how the user would expect it to be for how they are holding the device. 367 const float rotation = SDL_GetFloatProperty(SDL_GetSurfaceProperties(frame_current), SDL_PROP_SURFACE_ROTATION_FLOAT, 0.0f); 368 SDL_GetRenderOutputSize(renderer, &win_w, &win_h); 369 d.x = ((win_w - texture->w) / 2.0f); 370 d.y = ((win_h - texture->h) / 2.0f); 371 d.w = (float)texture->w; 372 d.h = (float)texture->h; 373 SDL_RenderTextureRotated(renderer, texture, NULL, &d, rotation, NULL, SDL_FLIP_NONE); 374 } 375 376 /* !!! FIXME: Render a "flip" icon if front_camera and back_camera are both != 0. */ 377 378 SDL_RenderPresent(renderer); 379 380 return SDL_APP_CONTINUE; 381} 382 383void SDL_AppQuit(void *appstate, SDL_AppResult result) 384{ 385 SDL_ReleaseCameraFrame(camera, frame_current); 386 SDL_CloseCamera(camera); 387 SDL_DestroyTexture(texture); 388 SDLTest_CommonQuit(state); 389} 390[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.