1 /*
2 * This program tries various ISOs for best ISO with a shutterspeed limit
3 *
4 * compile with: gcc -Wall -o best-iso best-iso.c -lgphoto2 -lgphoto2_port
5 *
6 */
7
8 #include <stdlib.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <gphoto2/gphoto2.h>
16
17 #if !defined (O_BINARY)
18 /*To have portable binary open() on *nix and on Windows */
19 #define O_BINARY 0
20 #endif
21
22 #define DEBUG
23
24 static int aperture;
25 static float shutterspeed;
26
errordumper(GPLogLevel level,const char * domain,const char * str,void * data)27 static void errordumper(GPLogLevel level, const char *domain, const char *str,
28 void *data) {
29 fprintf(stdout, "%s\n", str);
30 }
31
32 static void
ctx_error_func(GPContext * context,const char * str,void * data)33 ctx_error_func (GPContext *context, const char *str, void *data)
34 {
35 fprintf (stderr, "\n*** Contexterror *** \n%s\n",str);
36 fflush (stderr);
37 }
38
39 static void
ctx_status_func(GPContext * context,const char * str,void * data)40 ctx_status_func (GPContext *context, const char *str, void *data)
41 {
42 fprintf (stderr, "%s\n", str);
43 fflush (stderr);
44 }
45
46 static
sample_create_context()47 GPContext* sample_create_context() {
48 GPContext *context;
49
50 /* This is the mandatory part */
51 context = gp_context_new();
52
53 /* All the parts below are optional! */
54 gp_context_set_error_func (context, ctx_error_func, NULL);
55 gp_context_set_status_func (context, ctx_status_func, NULL);
56
57 /* also:
58 gp_context_set_cancel_func (p->context, ctx_cancel_func, p);
59 gp_context_set_message_func (p->context, ctx_message_func, p);
60 if (isatty (STDOUT_FILENO))
61 gp_context_set_progress_funcs (p->context,
62 ctx_progress_start_func, ctx_progress_update_func,
63 ctx_progress_stop_func, p);
64 */
65 return context;
66 }
67 /*
68 * This function looks up a label or key entry of
69 * a configuration widget.
70 * The functions descend recursively, so you can just
71 * specify the last component.
72 */
73
74 static int
_lookup_widget(CameraWidget * widget,const char * key,CameraWidget ** child)75 _lookup_widget(CameraWidget*widget, const char *key, CameraWidget **child) {
76 int ret;
77 ret = gp_widget_get_child_by_name (widget, key, child);
78 if (ret < GP_OK)
79 ret = gp_widget_get_child_by_label (widget, key, child);
80 return ret;
81 }
82
83 /* Gets a string configuration value.
84 * This can be:
85 * - A Text widget
86 * - The current selection of a Radio Button choice
87 * - The current selection of a Menu choice
88 *
89 * Sample (for Canons eg):
90 * get_config_value_string (camera, "owner", &ownerstr, context);
91 */
92 static int
get_config_value_string(Camera * camera,const char * key,char ** str,GPContext * context)93 get_config_value_string (Camera *camera, const char *key, char **str, GPContext *context) {
94 CameraWidget *widget = NULL, *child = NULL;
95 CameraWidgetType type;
96 int ret;
97 char *val;
98
99 ret = gp_camera_get_config (camera, &widget, context);
100 if (ret < GP_OK) {
101 fprintf (stderr, "camera_get_config failed: %d\n", ret);
102 return ret;
103 }
104 ret = _lookup_widget (widget, key, &child);
105 if (ret < GP_OK) {
106 fprintf (stderr, "lookup widget failed: %d\n", ret);
107 goto out;
108 }
109
110 /* This type check is optional, if you know what type the label
111 * has already. If you are not sure, better check. */
112 ret = gp_widget_get_type (child, &type);
113 if (ret < GP_OK) {
114 fprintf (stderr, "widget get type failed: %d\n", ret);
115 goto out;
116 }
117 switch (type) {
118 case GP_WIDGET_MENU:
119 case GP_WIDGET_RADIO:
120 case GP_WIDGET_TEXT:
121 break;
122 default:
123 fprintf (stderr, "widget has bad type %d\n", type);
124 ret = GP_ERROR_BAD_PARAMETERS;
125 goto out;
126 }
127
128 /* This is the actual query call. Note that we just
129 * a pointer reference to the string, not a copy... */
130 ret = gp_widget_get_value (child, &val);
131 if (ret < GP_OK) {
132 fprintf (stderr, "could not query widget value: %d\n", ret);
133 goto out;
134 }
135 /* Create a new copy for our caller. */
136 *str = strdup (val);
137 out:
138 gp_widget_free (widget);
139 return ret;
140 }
141
142
143 /* Sets a string configuration value.
144 * This can set for:
145 * - A Text widget
146 * - The current selection of a Radio Button choice
147 * - The current selection of a Menu choice
148 *
149 * Sample (for Canons eg):
150 * get_config_value_string (camera, "owner", &ownerstr, context);
151 */
152 static int
set_config_value_string(Camera * camera,const char * key,const char * val,GPContext * context)153 set_config_value_string (Camera *camera, const char *key, const char *val, GPContext *context) {
154 CameraWidget *widget = NULL, *child = NULL;
155 CameraWidgetType type;
156 int ret;
157
158 ret = gp_camera_get_config (camera, &widget, context);
159 if (ret < GP_OK) {
160 fprintf (stderr, "camera_get_config failed: %d\n", ret);
161 return ret;
162 }
163 ret = _lookup_widget (widget, key, &child);
164 if (ret < GP_OK) {
165 fprintf (stderr, "lookup widget failed: %d\n", ret);
166 goto out;
167 }
168
169 /* This type check is optional, if you know what type the label
170 * has already. If you are not sure, better check. */
171 ret = gp_widget_get_type (child, &type);
172 if (ret < GP_OK) {
173 fprintf (stderr, "widget get type failed: %d\n", ret);
174 goto out;
175 }
176 switch (type) {
177 case GP_WIDGET_MENU:
178 case GP_WIDGET_RADIO:
179 case GP_WIDGET_TEXT:
180 /* This is the actual set call. Note that we keep
181 * ownership of the string and have to free it if necessary.
182 */
183 ret = gp_widget_set_value (child, val);
184 if (ret < GP_OK) {
185 fprintf (stderr, "could not set widget value: %d\n", ret);
186 goto out;
187 }
188 break;
189 case GP_WIDGET_TOGGLE: {
190 int ival;
191
192 sscanf(val,"%d",&ival);
193 ret = gp_widget_set_value (child, &ival);
194 if (ret < GP_OK) {
195 fprintf (stderr, "could not set widget value: %d\n", ret);
196 goto out;
197 }
198 break;
199 }
200 default:
201 fprintf (stderr, "widget has bad type %d\n", type);
202 ret = GP_ERROR_BAD_PARAMETERS;
203 goto out;
204 }
205
206 /* This stores it on the camera again */
207 ret = gp_camera_set_config (camera, widget, context);
208 if (ret < GP_OK) {
209 fprintf (stderr, "camera_set_config failed: %d\n", ret);
210 return ret;
211 }
212 out:
213 gp_widget_free (widget);
214 return ret;
215 }
216
217
218 static int
camera_tether(Camera * camera,GPContext * context)219 camera_tether(Camera *camera, GPContext *context) {
220 int fd, retval;
221 CameraFile *file;
222 CameraEventType evttype;
223 CameraFilePath *path;
224 void *evtdata;
225
226 printf("Tethering...\n");
227
228 while (1) {
229 retval = gp_camera_wait_for_event (camera, 2000, &evttype, &evtdata, context);
230 if (retval != GP_OK)
231 return retval;
232 switch (evttype) {
233 case GP_EVENT_FILE_ADDED:
234 path = (CameraFilePath*)evtdata;
235 printf("File added on the camera: %s/%s\n", path->folder, path->name);
236
237 fd = open(path->name, O_CREAT | O_WRONLY | O_BINARY, 0644);
238 retval = gp_file_new_from_fd(&file, fd);
239 printf(" Downloading %s...\n", path->name);
240 retval = gp_camera_file_get(camera, path->folder, path->name,
241 GP_FILE_TYPE_NORMAL, file, context);
242
243 printf(" Deleting %s on camera...\n", path->name);
244 retval = gp_camera_file_delete(camera, path->folder, path->name, context);
245 gp_file_free(file);
246 free(path);
247 break;
248 case GP_EVENT_FOLDER_ADDED:
249 path = (CameraFilePath*)evtdata;
250 printf("Folder added on camera: %s / %s\n", path->folder, path->name);
251 free(path);
252 break;
253 case GP_EVENT_FILE_CHANGED:
254 path = (CameraFilePath*)evtdata;
255 printf("File changed on camera: %s / %s\n", path->folder, path->name);
256 free(path);
257 break;
258 case GP_EVENT_CAPTURE_COMPLETE:
259 printf("Capture Complete.\n");
260 break;
261 case GP_EVENT_TIMEOUT:
262 printf("Timeout.\n");
263 return GP_OK;
264 case GP_EVENT_UNKNOWN:
265 if (evtdata) {
266 char *val;
267 int ret;
268
269 /* printf("Unknown event: %s.\n", (char*)evtdata); */
270
271 /* 3 eos properties hardcoded */
272 if (strstr(evtdata,"d103")) {
273 ret = get_config_value_string(camera,"iso",&val,context);
274 if (ret == GP_OK) {
275 printf("ISO is %s\n", val);
276 free(val);
277 }
278 }
279 if (strstr(evtdata,"d102")) {
280 ret = get_config_value_string(camera,"shutterspeed",&val,context);
281 if (ret == GP_OK) {
282 if (strchr(val,'/')) {
283 int zaehler,nenner;
284 sscanf(val,"%d/%d",&zaehler,&nenner);
285 shutterspeed = 1.0*zaehler/nenner;
286 } else {
287 if (!sscanf(val,"%g",&shutterspeed))
288 shutterspeed = 0.0;
289 }
290 printf("Shutterspeed is %s (%g)\n", val, shutterspeed);
291 free(val);
292 }
293 }
294 if (strstr(evtdata,"d101")) {
295 ret = get_config_value_string(camera,"aperture",&val,context);
296 if (ret == GP_OK) {
297 float ap;
298
299 sscanf(val,"%g",&ap);
300 aperture = 10*ap;
301 printf("Aperture is %s (%d)\n", val, aperture);
302 free (val);
303 }
304 }
305 } else {
306 printf("Unknown event.\n");
307 }
308 break;
309 default:
310 printf("Type %d?\n", evttype);
311 break;
312 }
313 }
314 }
315
316 int
main(int argc,char ** argv)317 main(int argc, char **argv) {
318 Camera *camera;
319 int retval, iso, tries;
320 char buf[20];
321 GPContext *context = sample_create_context();
322 /*int fd;*/
323 CameraFile *file;
324 /*CameraFilePath camera_file_path;*/
325
326 gp_log_add_func(GP_LOG_ERROR, errordumper, NULL);
327 gp_camera_new(&camera);
328
329 retval = gp_camera_init(camera, context);
330 if (retval != GP_OK) {
331 printf(" camera failed to initialize: %d\n", retval);
332 exit (1);
333 }
334
335 retval = camera_tether(camera, context);
336 if (retval != GP_OK) {
337 printf("Tether error: %d\n", retval);
338 exit (1);
339 }
340
341 iso = 100;
342
343 shutterspeed = 0.0;
344 aperture = 0;
345
346 #if 1
347 gp_file_new (&file);
348 retval = gp_camera_capture_preview (camera, file, context);
349 if (retval != GP_OK) {
350 printf("Preview capture error: %d\n", retval);
351 exit (1);
352 }
353 #endif
354
355 while (1) {
356 sprintf(buf,"%d",iso);
357 printf("Setting ISO to %d\n",iso);
358
359 retval = set_config_value_string(camera, "iso", buf, context);
360 if (retval != GP_OK) {
361 printf(" failed setting ISO to %d: %d\n", iso, retval);
362
363 /* usually also happens if too dark */
364 exit (1);
365 }
366
367 printf("eosremoterelease half press\n");
368 retval = set_config_value_string(camera,"eosremoterelease", "Press Half", context);
369 if (retval != GP_OK) {
370 printf(" failed pressing shutter to half: %d\n", retval);
371 exit (1);
372 }
373
374 retval = camera_tether(camera, context);
375 if (retval != GP_OK) {
376 printf("Tether error: %d\n", retval);
377 exit (1);
378 }
379
380 if (shutterspeed < 0.000000001) {
381 printf("Camera did not report shutterspeed?\n");
382 }
383
384 printf("eosremoterelease release\n");
385 retval = set_config_value_string(camera,"eosremoterelease", "Release Half", context);
386 if (retval != GP_OK) {
387 printf(" failed releasing shutter button from half: %d\n", retval);
388 exit (1);
389 }
390
391 if (shutterspeed < 30.0) {
392
393 printf("ISO %d gets Aperture %g and Shutterspeed %g\n", iso, aperture/10.0, shutterspeed);
394
395 break;
396 }
397 /* ISO always goes up in 2 multiplicator steps */
398 iso = iso*2;
399 }
400
401 /* we can take the picture now */
402
403 #if 0
404
405 printf("Capturing.\n");
406
407 retval = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
408 if (retval != GP_OK) {
409 printf(" capture failed: %d\n", retval);
410 exit(1);
411 }
412
413 printf("Pathname on the camera: %s/%s\n", camera_file_path.folder, camera_file_path.name);
414
415 fd = open("capture.jpg", O_CREAT | O_WRONLY | O_BINARY, 0644);
416
417 retval = gp_file_new_from_fd(&file, fd);
418 if (retval != GP_OK) printf(" gp_file_new failed: %d\n", retval);
419
420 retval = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name,
421 GP_FILE_TYPE_NORMAL, file, context);
422 if (retval != GP_OK) printf(" file_get failed: %d\n", retval);
423
424 printf("Deleting downloaded image.\n");
425 retval = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name,
426 context);
427 if (retval != GP_OK) printf(" Retval: %d\n", retval);
428
429 gp_file_free(file);
430
431 #endif
432 #if 1
433 /* SAMPLE Capture code with eosremoterelease ... normal gp_camera_capture_image also works. */
434 printf("eosremoterelease release\n");
435 retval = set_config_value_string(camera,"eosremoterelease", "Immediate", context);
436
437 if (retval != GP_OK) {
438 printf(" failed pressing shutter button full: %d\n", retval);
439 exit (1);
440 }
441
442 retval = set_config_value_string(camera,"eosremoterelease", "Release Full", context);
443
444 if (retval != GP_OK) {
445 printf(" failed releasing shutter button full: %d\n", retval);
446 exit (1);
447 }
448
449
450 retval = camera_tether(camera, context);
451 if (retval != GP_OK) {
452 printf("Tether error: %d\n", retval);
453 exit (1);
454 }
455 #endif
456
457 tries = 30; /* seconds at most, discounting events. */
458 while (tries--) {
459 retval = camera_tether(camera, context);
460 if (retval != GP_OK) {
461 printf("Tether error: %d\n", retval);
462 exit (1);
463 }
464 }
465
466 #if 1
467 retval = set_config_value_string(camera,"eosviewfinder", "0", context);
468 if (retval != GP_OK) {
469 printf("setting eosviewfinder off error: %d\n", retval);
470 exit (1);
471 }
472 #endif
473
474 gp_camera_exit(camera, context);
475 return 0;
476 }
477