1 /* *INDENT-OFF* */
2 /*
3 * Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
4 Portions:
5 Copyright (c) 2013, Broadcom Europe Ltd
6 Copyright (c) 2013, James Hughes
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 * Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 * Neither the name of the copyright holder nor the
17 names of its contributors may be used to endorse or promote products
18 derived from this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
24 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /**
33 * \file RaspiStill.c
34 * Command line program to capture a still frame and encode it to file.
35 * Also optionally display a preview/viewfinder of current camera input.
36 *
37 * \date 31 Jan 2013
38 * \Author: James Hughes
39 *
40 * Description
41 *
42 * 3 components are created; camera, preview and JPG encoder.
43 * Camera component has three ports, preview, video and stills.
44 * This program connects preview and stills to the preview and jpg
45 * encoder. Using mmal we don't need to worry about buffers between these
46 * components, but we do need to handle buffers from the encoder, which
47 * are simply written straight to the file in the requisite buffer callback.
48 *
49 * We use the RaspiCamControl code to handle the specific camera settings.
50 */
51
52 // We use some GNU extensions (asprintf, basename)
53 #define _GNU_SOURCE
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <memory.h>
59 #include <unistd.h>
60 #include <errno.h>
61 #include <sysexits.h>
62
63 #define VERSION_STRING "v1.3.2"
64
65 #include "bcm_host.h"
66 #include "interface/vcos/vcos.h"
67
68 #include "interface/mmal/mmal.h"
69 #include "interface/mmal/mmal_logging.h"
70 #include "interface/mmal/mmal_buffer.h"
71 #include "interface/mmal/util/mmal_util.h"
72 #include "interface/mmal/util/mmal_util_params.h"
73 #include "interface/mmal/util/mmal_default_components.h"
74 #include "interface/mmal/util/mmal_connection.h"
75
76
77 #include "RaspiCamControl.h"
78 #include "RaspiPreview.h"
79 #include "RaspiCLI.h"
80
81 #include <semaphore.h>
82
83 /// Camera number to use - we only have one camera, indexed from 0.
84 #define CAMERA_NUMBER 0
85
86 // Standard port setting for the camera component
87 #define MMAL_CAMERA_PREVIEW_PORT 0
88 #define MMAL_CAMERA_VIDEO_PORT 1
89 #define MMAL_CAMERA_CAPTURE_PORT 2
90
91
92 // Stills format information
93 #define STILLS_FRAME_RATE_NUM 15
94 #define STILLS_FRAME_RATE_DEN 1
95
96 /// Video render needs at least 2 buffers.
97 #define VIDEO_OUTPUT_BUFFERS_NUM 3
98
99 #define MAX_USER_EXIF_TAGS 32
100 #define MAX_EXIF_PAYLOAD_LENGTH 128
101
102 int mmal_status_to_int(MMAL_STATUS_T status);
103
104 /** Structure containing all state information for the current run
105 */
106 typedef struct
107 {
108 int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds
109 int width; /// Requested width of image
110 int height; /// requested height of image
111 int quality; /// JPEG quality setting (1-100)
112 int wantRAW; /// Flag for whether the JPEG metadata also contains the RAW bayer image
113 char *filename; /// filename of output file
114 char *linkname; /// filename of output file
115 MMAL_PARAM_THUMBNAIL_CONFIG_T thumbnailConfig;
116 int verbose; /// !0 if want detailed run information
117 int demoMode; /// Run app in demo mode
118 int demoInterval; /// Interval between camera settings changes
119 MMAL_FOURCC_T encoding; /// Encoding to use for the output file.
120 const char *exifTags[MAX_USER_EXIF_TAGS]; /// Array of pointers to tags supplied from the command line
121 int numExifTags; /// Number of supplied tags
122 int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse
123 int fullResPreview; /// If set, the camera preview port runs at capture resolution. Reduces fps.
124
125 RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters
126 RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
127
128 MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component
129 MMAL_COMPONENT_T *encoder_component; /// Pointer to the encoder component
130 MMAL_COMPONENT_T *null_sink_component; /// Pointer to the null sink component
131 MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview
132 MMAL_CONNECTION_T *encoder_connection; /// Pointer to the connection from camera to encoder
133
134 MMAL_POOL_T *encoder_pool; /// Pointer to the pool of buffers used by encoder output port
135
136 } RASPISTILL_STATE;
137
138 /** Struct used to pass information in encoder port userdata to callback
139 */
140 typedef struct
141 {
142 FILE *file_handle; /// File handle to write buffer data to.
143 VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault)
144 RASPISTILL_STATE *pstate; /// pointer to our state in case required in callback
145 } PORT_USERDATA;
146
147 static void display_valid_parameters(char *app_name);
148 static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag);
149
150 /// Comamnd ID's and Structure defining our command line options
151 #define CommandHelp 0
152 #define CommandWidth 1
153 #define CommandHeight 2
154 #define CommandQuality 3
155 #define CommandRaw 4
156 #define CommandOutput 5
157 #define CommandVerbose 6
158 #define CommandTimeout 7
159 #define CommandThumbnail 8
160 #define CommandDemoMode 9
161 #define CommandEncoding 10
162 #define CommandExifTag 11
163 #define CommandTimelapse 12
164 #define CommandFullResPreview 13
165 #define CommandLink 14
166
167 static COMMAND_LIST cmdline_commands[] =
168 {
169 { CommandHelp, "-help", "?", "This help information", 0 },
170 { CommandWidth, "-width", "w", "Set image width <size>", 1 },
171 { CommandHeight, "-height", "h", "Set image height <size>", 1 },
172 { CommandQuality, "-quality", "q", "Set jpeg quality <0 to 100>", 1 },
173 { CommandRaw, "-raw", "r", "Add raw bayer data to jpeg metadata", 0 },
174 { CommandOutput, "-output", "o", "Output filename <filename> (to write to stdout, use '-o -'). If not specified, no file is saved", 1 },
175 { CommandLink, "-latest", "l", "Link latest complete image to filename <filename>", 1},
176 { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 },
177 { CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down (if not specified, set to 5s)", 1 },
178 { CommandThumbnail,"-thumb", "th", "Set thumbnail parameters (x:y:quality)", 1},
179 { CommandDemoMode,"-demo", "d", "Run a demo mode (cycle through range of camera options, no capture)", 0},
180 { CommandEncoding,"-encoding", "e", "Encoding to use for output file (jpg, bmp, gif, png)", 1},
181 { CommandExifTag, "-exif", "x", "EXIF tag to apply to captures (format as 'key=value')", 1},
182 { CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every <t>ms", 1},
183 { CommandFullResPreview,"-fullpreview", "fp", "Run the preview using the still capture resolution (may reduce preview fps)", 0},
184 };
185
186 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
187
188 static struct
189 {
190 char *format;
191 MMAL_FOURCC_T encoding;
192 } encoding_xref[] =
193 {
194 {"jpg", MMAL_ENCODING_JPEG},
195 {"bmp", MMAL_ENCODING_BMP},
196 {"gif", MMAL_ENCODING_GIF},
197 {"png", MMAL_ENCODING_PNG}
198 };
199
200 static int encoding_xref_size = sizeof(encoding_xref) / sizeof(encoding_xref[0]);
201
202
203 /**
204 * Assign a default set of parameters to the state passed in
205 *
206 * @param state Pointer to state structure to assign defaults to
207 */
default_status(RASPISTILL_STATE * state)208 static void default_status(RASPISTILL_STATE *state)
209 {
210 if (!state)
211 {
212 vcos_assert(0);
213 return;
214 }
215
216 state->timeout = 5000; // 5s delay before take image
217 state->width = 2592;
218 state->height = 1944;
219 state->quality = 85;
220 state->wantRAW = 0;
221 state->filename = NULL;
222 state->linkname = NULL;
223 state->verbose = 0;
224 state->thumbnailConfig.enable = 1;
225 state->thumbnailConfig.width = 64;
226 state->thumbnailConfig.height = 48;
227 state->thumbnailConfig.quality = 35;
228 state->demoMode = 0;
229 state->demoInterval = 250; // ms
230 state->camera_component = NULL;
231 state->encoder_component = NULL;
232 state->preview_connection = NULL;
233 state->encoder_connection = NULL;
234 state->encoder_pool = NULL;
235 state->encoding = MMAL_ENCODING_JPEG;
236 state->numExifTags = 0;
237 state->timelapse = 0;
238 state->fullResPreview = 0;
239
240 // Setup preview window defaults
241 raspipreview_set_defaults(&state->preview_parameters);
242
243 // Set up the camera_parameters to default
244 raspicamcontrol_set_defaults(&state->camera_parameters);
245 }
246
247 /**
248 * Dump image state parameters to stderr. Used for debugging
249 *
250 * @param state Pointer to state structure to assign defaults to
251 */
dump_status(RASPISTILL_STATE * state)252 static void dump_status(RASPISTILL_STATE *state)
253 {
254 int i;
255
256 if (!state)
257 {
258 vcos_assert(0);
259 return;
260 }
261
262 fprintf(stderr, "Width %d, Height %d, quality %d, filename %s\n", state->width,
263 state->height, state->quality, state->filename);
264 fprintf(stderr, "Time delay %d, Raw %s\n", state->timeout,
265 state->wantRAW ? "yes" : "no");
266 fprintf(stderr, "Thumbnail enabled %s, width %d, height %d, quality %d\n",
267 state->thumbnailConfig.enable ? "Yes":"No", state->thumbnailConfig.width,
268 state->thumbnailConfig.height, state->thumbnailConfig.quality);
269 fprintf(stderr, "Link to latest frame enabled ");
270 if (state->linkname)
271 {
272 fprintf(stderr, " yes, -> %s\n", state->linkname);
273 }
274 else
275 {
276 fprintf(stderr, " no\n");
277 }
278 fprintf(stderr, "Full resolution preview %s\n\n", state->fullResPreview ? "Yes": "No");
279
280 if (state->numExifTags)
281 {
282 fprintf(stderr, "User supplied EXIF tags :\n");
283
284 for (i=0;i<state->numExifTags;i++)
285 {
286 fprintf(stderr, "%s", state->exifTags[i]);
287 if (i != state->numExifTags-1)
288 fprintf(stderr, ",");
289 }
290 fprintf(stderr, "\n\n");
291 }
292
293 raspipreview_dump_parameters(&state->preview_parameters);
294 //raspicamcontrol_dump_parameters(&state->camera_parameters);
295 }
296
297 /**
298 * Parse the incoming command line and put resulting parameters in to the state
299 *
300 * @param argc Number of arguments in command line
301 * @param argv Array of pointers to strings from command line
302 * @param state Pointer to state structure to assign any discovered parameters to
303 * @return non-0 if failed for some reason, 0 otherwise
304 */
parse_cmdline(int argc,const char ** argv,RASPISTILL_STATE * state)305 static int parse_cmdline(int argc, const char **argv, RASPISTILL_STATE *state)
306 {
307 // Parse the command line arguments.
308 // We are looking for --<something> or -<abreviation of something>
309
310 int valid = 1;
311 int i;
312
313 for (i = 1; i < argc && valid; i++)
314 {
315 int command_id, num_parameters;
316
317 if (!argv[i])
318 continue;
319
320 if (argv[i][0] != '-')
321 {
322 valid = 0;
323 continue;
324 }
325
326 // Assume parameter is valid until proven otherwise
327 valid = 1;
328
329 command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
330
331 // If we found a command but are missing a parameter, continue (and we will drop out of the loop)
332 if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) )
333 continue;
334
335 // We are now dealing with a command line option
336 switch (command_id)
337 {
338 case CommandHelp:
339 display_valid_parameters(basename(argv[0]));
340 // exit straight away if help requested
341 return -1;
342
343 case CommandWidth: // Width > 0
344 if (sscanf(argv[i + 1], "%u", &state->width) != 1)
345 valid = 0;
346 else
347 i++;
348 break;
349
350 case CommandHeight: // Height > 0
351 if (sscanf(argv[i + 1], "%u", &state->height) != 1)
352 valid = 0;
353 else
354 i++;
355 break;
356
357 case CommandQuality: // Quality = 1-100
358 if (sscanf(argv[i + 1], "%u", &state->quality) == 1)
359 {
360 if (state->quality > 100)
361 {
362 fprintf(stderr, "Setting max quality = 100\n");
363 state->quality = 100;
364 }
365 i++;
366 }
367 else
368 valid = 0;
369
370 break;
371
372 case CommandRaw: // Add raw bayer data in metadata
373 state->wantRAW = 1;
374 break;
375
376 case CommandOutput: // output filename
377 {
378 int len = strlen(argv[i + 1]);
379 if (len)
380 {
381 state->filename = malloc(len + 10); // leave enough space for any timelapse generated changes to filename
382 vcos_assert(state->filename);
383 if (state->filename)
384 strncpy(state->filename, argv[i + 1], len);
385 i++;
386 }
387 else
388 valid = 0;
389 break;
390 }
391
392 case CommandLink :
393 {
394 int len = strlen(argv[i+1]);
395 if (len)
396 {
397 state->linkname = malloc(len + 10);
398 vcos_assert(state->linkname);
399 if (state->linkname)
400 strncpy(state->linkname, argv[i + 1], len);
401 i++;
402 }
403 else
404 valid = 0;
405 break;
406
407 }
408 case CommandVerbose: // display lots of data during run
409 state->verbose = 1;
410 break;
411
412 case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds
413 {
414 if (sscanf(argv[i + 1], "%u", &state->timeout) == 1)
415 {
416 // TODO : What limits do we need for timeout?
417 i++;
418 }
419 else
420 valid = 0;
421 break;
422 }
423 case CommandThumbnail : // thumbnail parameters - needs string "x:y:quality"
424 sscanf(argv[i + 1], "%d:%d:%d", &state->thumbnailConfig.width,&state->thumbnailConfig.height,
425 &state->thumbnailConfig.quality);
426 i++;
427 break;
428
429 case CommandDemoMode: // Run in demo mode - no capture
430 {
431 // Demo mode might have a timing parameter
432 // so check if a) we have another parameter, b) its not the start of the next option
433 if (i + 1 < argc && argv[i+1][0] != '-')
434 {
435 if (sscanf(argv[i + 1], "%u", &state->demoInterval) == 1)
436 {
437 // TODO : What limits do we need for timeout?
438 state->demoMode = 1;
439 i++;
440 }
441 else
442 valid = 0;
443 }
444 else
445 {
446 state->demoMode = 1;
447 }
448
449 break;
450 }
451
452 case CommandEncoding :
453 {
454 int len = strlen(argv[i + 1]);
455 valid = 0;
456
457 if (len)
458 {
459 int j;
460 for (j=0;j<encoding_xref_size;j++)
461 {
462 if (strcmp(encoding_xref[j].format, argv[i+1]) == 0)
463 {
464 state->encoding = encoding_xref[j].encoding;
465 valid = 1;
466 i++;
467 break;
468 }
469 }
470 }
471 break;
472 }
473
474 case CommandExifTag:
475 store_exif_tag(state, argv[i+1]);
476 i++;
477 break;
478
479 case CommandTimelapse:
480 if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1)
481 valid = 0;
482 else
483 i++;
484 break;
485
486 case CommandFullResPreview:
487 state->fullResPreview = 1;
488 break;
489
490 default:
491 {
492 // Try parsing for any image specific parameters
493 // result indicates how many parameters were used up, 0,1,2
494 // but we adjust by -1 as we have used one already
495 const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL;
496 int parms_used = raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg);
497
498 // Still unused, try preview options
499 if (!parms_used)
500 parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
501
502 // If no parms were used, this must be a bad parameters
503 if (!parms_used)
504 valid = 0;
505 else
506 i += parms_used - 1;
507
508 break;
509 }
510 }
511 }
512
513 if (!valid)
514 {
515 fprintf(stderr, "Invalid command line option (%s)\n", argv[i]);
516 return 1;
517 }
518
519 return 0;
520 }
521
522 /**
523 * Display usage information for the application to stdout
524 *
525 * @param app_name String to display as the application name
526 */
display_valid_parameters(char * app_name)527 static void display_valid_parameters(char *app_name)
528 {
529 fprintf(stderr, "Runs camera for specific time, and take JPG capture at end if requested\n\n");
530 fprintf(stderr, "usage: %s [options]\n\n", app_name);
531
532 fprintf(stderr, "Image parameter commands\n\n");
533
534 raspicli_display_help(cmdline_commands, cmdline_commands_size);
535
536 // Help for preview options
537 raspipreview_display_help();
538
539 // Now display any help information from the camcontrol code
540 raspicamcontrol_display_help();
541
542 fprintf(stderr, "\n");
543
544 return;
545 }
546
547 /**
548 * buffer header callback function for camera control
549 *
550 * No actions taken in current version
551 *
552 * @param port Pointer to port from which callback originated
553 * @param buffer mmal buffer header pointer
554 */
camera_control_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)555 static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
556 {
557 if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED)
558 {
559 }
560 else
561 {
562 vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd);
563 }
564
565 mmal_buffer_header_release(buffer);
566 }
567
568 /**
569 * buffer header callback function for encoder
570 *
571 * Callback will dump buffer data to the specific file
572 *
573 * @param port Pointer to port from which callback originated
574 * @param buffer mmal buffer header pointer
575 */
encoder_buffer_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)576 static void encoder_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
577 {
578 int complete = 0;
579
580 // We pass our file handle and other stuff in via the userdata field.
581
582 PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
583
584 if (pData)
585 {
586 int bytes_written = buffer->length;
587
588 if (buffer->length && pData->file_handle)
589 {
590 mmal_buffer_header_mem_lock(buffer);
591
592 bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle);
593
594 mmal_buffer_header_mem_unlock(buffer);
595 }
596
597 // We need to check we wrote what we wanted - it's possible we have run out of storage.
598 if (bytes_written != buffer->length)
599 {
600 vcos_log_error("Unable to write buffer to file - aborting");
601 complete = 1;
602 }
603
604 // Now flag if we have completed
605 if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED))
606 complete = 1;
607 }
608 else
609 {
610 vcos_log_error("Received a encoder buffer callback with no state");
611 }
612
613 // release buffer back to the pool
614 mmal_buffer_header_release(buffer);
615
616 // and send one back to the port (if still open)
617 if (port->is_enabled)
618 {
619 MMAL_STATUS_T status = MMAL_SUCCESS;
620 MMAL_BUFFER_HEADER_T *new_buffer;
621
622 new_buffer = mmal_queue_get(pData->pstate->encoder_pool->queue);
623
624 if (new_buffer)
625 {
626 status = mmal_port_send_buffer(port, new_buffer);
627 }
628 if (!new_buffer || status != MMAL_SUCCESS)
629 vcos_log_error("Unable to return a buffer to the encoder port");
630 }
631
632 if (complete)
633 vcos_semaphore_post(&(pData->complete_semaphore));
634
635 }
636
637
638 /**
639 * Create the camera component, set up its ports
640 *
641 * @param state Pointer to state control struct. camera_component member set to the created camera_component if successfull.
642 *
643 * @return MMAL_SUCCESS if all OK, something else otherwise
644 *
645 */
create_camera_component(RASPISTILL_STATE * state)646 static MMAL_STATUS_T create_camera_component(RASPISTILL_STATE *state)
647 {
648 MMAL_COMPONENT_T *camera = 0;
649 MMAL_ES_FORMAT_T *format;
650 MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
651 MMAL_STATUS_T status;
652
653 /* Create the component */
654 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
655
656 if (status != MMAL_SUCCESS)
657 {
658 vcos_log_error("Failed to create camera component");
659 goto error;
660 }
661
662 if (!camera->output_num)
663 {
664 status = MMAL_ENOSYS;
665 vcos_log_error("Camera doesn't have output ports");
666 goto error;
667 }
668
669 preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
670 video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
671 still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
672
673 // Enable the camera, and tell it its control callback function
674 status = mmal_port_enable(camera->control, camera_control_callback);
675
676 if (status != MMAL_SUCCESS)
677 {
678 vcos_log_error("Unable to enable control port : error %d", status);
679 goto error;
680 }
681
682 // set up the camera configuration
683 {
684 MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
685 {
686 { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
687 .max_stills_w = state->width,
688 .max_stills_h = state->height,
689 .stills_yuv422 = 0,
690 .one_shot_stills = 1,
691 .max_preview_video_w = state->preview_parameters.previewWindow.width,
692 .max_preview_video_h = state->preview_parameters.previewWindow.height,
693 .num_preview_video_frames = 3,
694 .stills_capture_circular_buffer_height = 0,
695 .fast_preview_resume = 0,
696 .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC
697 };
698
699 if (state->fullResPreview)
700 {
701 cam_config.max_preview_video_w = state->width;
702 cam_config.max_preview_video_h = state->height;
703 }
704
705 mmal_port_parameter_set(camera->control, &cam_config.hdr);
706 }
707
708 raspicamcontrol_set_all_parameters(camera, &state->camera_parameters);
709
710 // Now set up the port formats
711
712 format = preview_port->format;
713
714 format->encoding = MMAL_ENCODING_OPAQUE;
715 format->encoding_variant = MMAL_ENCODING_I420;
716
717 if (state->fullResPreview)
718 {
719 // In this mode we are forcing the preview to be generated from the full capture resolution.
720 // This runs at a max of 15fps with the OV5647 sensor.
721 format->es->video.width = state->width;
722 format->es->video.height = state->height;
723 format->es->video.crop.x = 0;
724 format->es->video.crop.y = 0;
725 format->es->video.crop.width = state->width;
726 format->es->video.crop.height = state->height;
727 format->es->video.frame_rate.num = FULL_RES_PREVIEW_FRAME_RATE_NUM;
728 format->es->video.frame_rate.den = FULL_RES_PREVIEW_FRAME_RATE_DEN;
729 }
730 else
731 {
732 // use our normal preview mode - probably 1080p30
733 format->es->video.width = state->preview_parameters.previewWindow.width;
734 format->es->video.height = state->preview_parameters.previewWindow.height;
735 format->es->video.crop.x = 0;
736 format->es->video.crop.y = 0;
737 format->es->video.crop.width = state->preview_parameters.previewWindow.width;
738 format->es->video.crop.height = state->preview_parameters.previewWindow.height;
739 format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM;
740 format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN;
741 }
742
743 status = mmal_port_format_commit(preview_port);
744
745 if (status != MMAL_SUCCESS)
746 {
747 vcos_log_error("camera viewfinder format couldn't be set");
748 goto error;
749 }
750
751 // Set the same format on the video port (which we dont use here)
752 mmal_format_full_copy(video_port->format, format);
753 status = mmal_port_format_commit(video_port);
754
755 if (status != MMAL_SUCCESS)
756 {
757 vcos_log_error("camera video format couldn't be set");
758 goto error;
759 }
760
761 // Ensure there are enough buffers to avoid dropping frames
762 if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
763 video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
764
765 format = still_port->format;
766
767 // Set our stills format on the stills (for encoder) port
768 format->encoding = MMAL_ENCODING_OPAQUE;
769 format->es->video.width = state->width;
770 format->es->video.height = state->height;
771 format->es->video.crop.x = 0;
772 format->es->video.crop.y = 0;
773 format->es->video.crop.width = state->width;
774 format->es->video.crop.height = state->height;
775 format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM;
776 format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN;
777
778
779 status = mmal_port_format_commit(still_port);
780
781 if (status != MMAL_SUCCESS)
782 {
783 vcos_log_error("camera still format couldn't be set");
784 goto error;
785 }
786
787 /* Ensure there are enough buffers to avoid dropping frames */
788 if (still_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
789 still_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
790
791 /* Enable component */
792 status = mmal_component_enable(camera);
793
794 if (status != MMAL_SUCCESS)
795 {
796 vcos_log_error("camera component couldn't be enabled");
797 goto error;
798 }
799
800 state->camera_component = camera;
801
802 if (state->verbose)
803 fprintf(stderr, "Camera component done\n");
804
805 return status;
806
807 error:
808
809 if (camera)
810 mmal_component_destroy(camera);
811
812 return status;
813 }
814
815 /**
816 * Destroy the camera component
817 *
818 * @param state Pointer to state control struct
819 *
820 */
destroy_camera_component(RASPISTILL_STATE * state)821 static void destroy_camera_component(RASPISTILL_STATE *state)
822 {
823 if (state->camera_component)
824 {
825 mmal_component_destroy(state->camera_component);
826 state->camera_component = NULL;
827 }
828 }
829
830
831
832 /**
833 * Create the encoder component, set up its ports
834 *
835 * @param state Pointer to state control struct. encoder_component member set to the created camera_component if successfull.
836 *
837 * @return a MMAL_STATUS, MMAL_SUCCESS if all OK, something else otherwise
838 */
create_encoder_component(RASPISTILL_STATE * state)839 static MMAL_STATUS_T create_encoder_component(RASPISTILL_STATE *state)
840 {
841 MMAL_COMPONENT_T *encoder = 0;
842 MMAL_PORT_T *encoder_input = NULL, *encoder_output = NULL;
843 MMAL_STATUS_T status;
844 MMAL_POOL_T *pool;
845
846 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_IMAGE_ENCODER, &encoder);
847
848 if (status != MMAL_SUCCESS)
849 {
850 vcos_log_error("Unable to create JPEG encoder component");
851 goto error;
852 }
853
854 if (!encoder->input_num || !encoder->output_num)
855 {
856 status = MMAL_ENOSYS;
857 vcos_log_error("JPEG encoder doesn't have input/output ports");
858 goto error;
859 }
860
861 encoder_input = encoder->input[0];
862 encoder_output = encoder->output[0];
863
864 // We want same format on input and output
865 mmal_format_copy(encoder_output->format, encoder_input->format);
866
867 // Specify out output format
868 encoder_output->format->encoding = state->encoding;
869
870 encoder_output->buffer_size = encoder_output->buffer_size_recommended;
871
872 if (encoder_output->buffer_size < encoder_output->buffer_size_min)
873 encoder_output->buffer_size = encoder_output->buffer_size_min;
874
875 encoder_output->buffer_num = encoder_output->buffer_num_recommended;
876
877 if (encoder_output->buffer_num < encoder_output->buffer_num_min)
878 encoder_output->buffer_num = encoder_output->buffer_num_min;
879
880 // Commit the port changes to the output port
881 status = mmal_port_format_commit(encoder_output);
882
883 if (status != MMAL_SUCCESS)
884 {
885 vcos_log_error("Unable to set format on video encoder output port");
886 goto error;
887 }
888
889 // Set the JPEG quality level
890 status = mmal_port_parameter_set_uint32(encoder_output, MMAL_PARAMETER_JPEG_Q_FACTOR, state->quality);
891
892 if (status != MMAL_SUCCESS)
893 {
894 vcos_log_error("Unable to set JPEG quality");
895 goto error;
896 }
897
898 // Set up any required thumbnail
899 {
900 MMAL_PARAMETER_THUMBNAIL_CONFIG_T param_thumb = {{MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, sizeof(MMAL_PARAMETER_THUMBNAIL_CONFIG_T)}, 0, 0, 0, 0};
901
902 if ( state->thumbnailConfig.width > 0 && state->thumbnailConfig.height > 0 )
903 {
904 // Have a valid thumbnail defined
905 param_thumb.enable = 1;
906 param_thumb.width = state->thumbnailConfig.width;
907 param_thumb.height = state->thumbnailConfig.height;
908 param_thumb.quality = state->thumbnailConfig.quality;
909 }
910 status = mmal_port_parameter_set(encoder->control, ¶m_thumb.hdr);
911 }
912
913 // Enable component
914 status = mmal_component_enable(encoder);
915
916 if (status != MMAL_SUCCESS)
917 {
918 vcos_log_error("Unable to enable video encoder component");
919 goto error;
920 }
921
922 /* Create pool of buffer headers for the output port to consume */
923 pool = mmal_port_pool_create(encoder_output, encoder_output->buffer_num, encoder_output->buffer_size);
924
925 if (!pool)
926 {
927 vcos_log_error("Failed to create buffer header pool for encoder output port %s", encoder_output->name);
928 }
929
930 state->encoder_pool = pool;
931 state->encoder_component = encoder;
932
933 if (state->verbose)
934 fprintf(stderr, "Encoder component done\n");
935
936 return status;
937
938 error:
939
940 if (encoder)
941 mmal_component_destroy(encoder);
942
943 return status;
944 }
945
946 /**
947 * Destroy the encoder component
948 *
949 * @param state Pointer to state control struct
950 *
951 */
destroy_encoder_component(RASPISTILL_STATE * state)952 static void destroy_encoder_component(RASPISTILL_STATE *state)
953 {
954 // Get rid of any port buffers first
955 if (state->encoder_pool)
956 {
957 mmal_port_pool_destroy(state->encoder_component->output[0], state->encoder_pool);
958 }
959
960 if (state->encoder_component)
961 {
962 mmal_component_destroy(state->encoder_component);
963 state->encoder_component = NULL;
964 }
965 }
966
967
968 /**
969 * Add an exif tag to the capture
970 *
971 * @param state Pointer to state control struct
972 * @param exif_tag String containing a "key=value" pair.
973 * @return Returns a MMAL_STATUS_T giving result of operation
974 */
add_exif_tag(RASPISTILL_STATE * state,const char * exif_tag)975 static MMAL_STATUS_T add_exif_tag(RASPISTILL_STATE *state, const char *exif_tag)
976 {
977 MMAL_STATUS_T status;
978 MMAL_PARAMETER_EXIF_T *exif_param = (MMAL_PARAMETER_EXIF_T*)calloc(sizeof(MMAL_PARAMETER_EXIF_T) + MAX_EXIF_PAYLOAD_LENGTH, 1);
979
980 vcos_assert(state);
981 vcos_assert(state->encoder_component);
982
983 // Check to see if the tag is present or is indeed a key=value pair.
984 if (!exif_tag || strchr(exif_tag, '=') == NULL || strlen(exif_tag) > MAX_EXIF_PAYLOAD_LENGTH-1)
985 return MMAL_EINVAL;
986
987 exif_param->hdr.id = MMAL_PARAMETER_EXIF;
988
989 strncpy((char*)exif_param->data, exif_tag, MAX_EXIF_PAYLOAD_LENGTH-1);
990
991 exif_param->hdr.size = sizeof(MMAL_PARAMETER_EXIF_T) + strlen((char*)exif_param->data);
992
993 status = mmal_port_parameter_set(state->encoder_component->output[0], &exif_param->hdr);
994
995 free(exif_param);
996
997 return status;
998 }
999
1000 /**
1001 * Add a basic set of EXIF tags to the capture
1002 * Make, Time etc
1003 *
1004 * @param state Pointer to state control struct
1005 *
1006 */
add_exif_tags(RASPISTILL_STATE * state)1007 static void add_exif_tags(RASPISTILL_STATE *state)
1008 {
1009 time_t rawtime;
1010 struct tm *timeinfo;
1011 char time_buf[32];
1012 char exif_buf[128];
1013 int i;
1014
1015 add_exif_tag(state, "IFD0.Model=RP_OV5647");
1016 add_exif_tag(state, "IFD0.Make=RaspberryPi");
1017
1018 time(&rawtime);
1019 timeinfo = localtime(&rawtime);
1020
1021 snprintf(time_buf, sizeof(time_buf),
1022 "%04d:%02d:%02d %02d:%02d:%02d",
1023 timeinfo->tm_year+1900,
1024 timeinfo->tm_mon+1,
1025 timeinfo->tm_mday,
1026 timeinfo->tm_hour,
1027 timeinfo->tm_min,
1028 timeinfo->tm_sec);
1029
1030 snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeDigitized=%s", time_buf);
1031 add_exif_tag(state, exif_buf);
1032
1033 snprintf(exif_buf, sizeof(exif_buf), "EXIF.DateTimeOriginal=%s", time_buf);
1034 add_exif_tag(state, exif_buf);
1035
1036 snprintf(exif_buf, sizeof(exif_buf), "IFD0.DateTime=%s", time_buf);
1037 add_exif_tag(state, exif_buf);
1038
1039 // Now send any user supplied tags
1040
1041 for (i=0;i<state->numExifTags && i < MAX_USER_EXIF_TAGS;i++)
1042 {
1043 if (state->exifTags[i])
1044 {
1045 add_exif_tag(state, state->exifTags[i]);
1046 }
1047 }
1048 }
1049
1050 /**
1051 * Stores an EXIF tag in the state, incrementing various pointers as necessary.
1052 * Any tags stored in this way will be added to the image file when add_exif_tags
1053 * is called
1054 *
1055 * Will not store if run out of storage space
1056 *
1057 * @param state Pointer to state control struct
1058 * @param exif_tag EXIF tag string
1059 *
1060 */
store_exif_tag(RASPISTILL_STATE * state,const char * exif_tag)1061 static void store_exif_tag(RASPISTILL_STATE *state, const char *exif_tag)
1062 {
1063 if (state->numExifTags < MAX_USER_EXIF_TAGS)
1064 {
1065 state->exifTags[state->numExifTags] = exif_tag;
1066 state->numExifTags++;
1067 }
1068 }
1069
1070 /**
1071 * Connect two specific ports together
1072 *
1073 * @param output_port Pointer the output port
1074 * @param input_port Pointer the input port
1075 * @param Pointer to a mmal connection pointer, reassigned if function successful
1076 * @return Returns a MMAL_STATUS_T giving result of operation
1077 *
1078 */
connect_ports(MMAL_PORT_T * output_port,MMAL_PORT_T * input_port,MMAL_CONNECTION_T ** connection)1079 static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
1080 {
1081 MMAL_STATUS_T status;
1082
1083 status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
1084
1085 if (status == MMAL_SUCCESS)
1086 {
1087 status = mmal_connection_enable(*connection);
1088 if (status != MMAL_SUCCESS)
1089 mmal_connection_destroy(*connection);
1090 }
1091
1092 return status;
1093 }
1094
1095
1096 /**
1097 * Allocates and generates a filename based on the
1098 * user-supplied pattern and the frame number.
1099 * On successful return, finalName and tempName point to malloc()ed strings
1100 * which must be freed externally. (On failure, returns nulls that
1101 * don't need free()ing.)
1102 *
1103 * @param finalName pointer receives an
1104 * @param pattern sprintf pattern with %d to be replaced by frame
1105 * @param frame for timelapse, the frame number
1106 * @return Returns a MMAL_STATUS_T giving result of operation
1107 */
1108
create_filenames(char ** finalName,char ** tempName,char * pattern,int frame)1109 MMAL_STATUS_T create_filenames(char** finalName, char** tempName, char * pattern, int frame)
1110 {
1111 *finalName = NULL;
1112 *tempName = NULL;
1113 if (0 > asprintf(finalName, pattern, frame) ||
1114 0 > asprintf(tempName, "%s~", *finalName))
1115 {
1116 if (*finalName != NULL)
1117 {
1118 free(*finalName);
1119 }
1120 return MMAL_ENOMEM; // It may be some other error, but it is not worth getting it right
1121 }
1122 return MMAL_SUCCESS;
1123 }
1124
1125 /**
1126 * Checks if specified port is valid and enabled, then disables it
1127 *
1128 * @param port Pointer the port
1129 *
1130 */
check_disable_port(MMAL_PORT_T * port)1131 static void check_disable_port(MMAL_PORT_T *port)
1132 {
1133 if (port && port->is_enabled)
1134 mmal_port_disable(port);
1135 }
1136
1137 /**
1138 * Handler for sigint signals
1139 *
1140 * @param signal_number ID of incoming signal.
1141 *
1142 */
signal_handler(int signal_number)1143 static void signal_handler(int signal_number)
1144 {
1145 // Going to abort on all signals
1146 vcos_log_error("Aborting program\n");
1147
1148 // Need to close any open stuff...
1149
1150 exit(130);
1151 }
1152
1153 /**
1154 * main
1155 */
main(int argc,const char ** argv)1156 int main(int argc, const char **argv)
1157 {
1158 // Our main data storage vessel..
1159 RASPISTILL_STATE state;
1160 int exit_code = EX_OK;
1161
1162 MMAL_STATUS_T status = MMAL_SUCCESS;
1163 MMAL_PORT_T *camera_preview_port = NULL;
1164 MMAL_PORT_T *camera_video_port = NULL;
1165 MMAL_PORT_T *camera_still_port = NULL;
1166 MMAL_PORT_T *preview_input_port = NULL;
1167 MMAL_PORT_T *encoder_input_port = NULL;
1168 MMAL_PORT_T *encoder_output_port = NULL;
1169
1170 bcm_host_init();
1171
1172 // Register our application with the logging system
1173 vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY);
1174
1175 signal(SIGINT, signal_handler);
1176
1177 default_status(&state);
1178
1179 // Do we have any parameters
1180 if (argc == 1)
1181 {
1182 fprintf(stderr, "\%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING);
1183
1184 display_valid_parameters(basename(argv[0]));
1185 exit(EX_USAGE);
1186 }
1187
1188 // Parse the command line and put options in to our status structure
1189 if (parse_cmdline(argc, argv, &state))
1190 {
1191 exit(EX_USAGE);
1192 }
1193
1194 if (state.verbose)
1195 {
1196 fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING);
1197
1198 dump_status(&state);
1199 }
1200
1201 // OK, we have a nice set of parameters. Now set up our components
1202 // We have three components. Camera, Preview and encoder.
1203 // Camera and encoder are different in stills/video, but preview
1204 // is the same so handed off to a separate module
1205
1206 if ((status = create_camera_component(&state)) != MMAL_SUCCESS)
1207 {
1208 vcos_log_error("%s: Failed to create camera component", __func__);
1209 exit_code = EX_SOFTWARE;
1210 }
1211 else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS)
1212 {
1213 vcos_log_error("%s: Failed to create preview component", __func__);
1214 destroy_camera_component(&state);
1215 exit_code = EX_SOFTWARE;
1216 }
1217 else if ((status = create_encoder_component(&state)) != MMAL_SUCCESS)
1218 {
1219 vcos_log_error("%s: Failed to create encode component", __func__);
1220 raspipreview_destroy(&state.preview_parameters);
1221 destroy_camera_component(&state);
1222 exit_code = EX_SOFTWARE;
1223 }
1224 else
1225 {
1226 PORT_USERDATA callback_data;
1227
1228 if (state.verbose)
1229 fprintf(stderr, "Starting component connection stage\n");
1230
1231 camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
1232 camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT];
1233 camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
1234 encoder_input_port = state.encoder_component->input[0];
1235 encoder_output_port = state.encoder_component->output[0];
1236
1237 // Note we are lucky that the preview and null sink components use the same input port
1238 // so we can simple do this without conditionals
1239 preview_input_port = state.preview_parameters.preview_component->input[0];
1240
1241 // Connect camera to preview (which might be a null_sink if no preview required)
1242 status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection);
1243
1244 if (status == MMAL_SUCCESS)
1245 {
1246 VCOS_STATUS_T vcos_status;
1247
1248 if (state.verbose)
1249 fprintf(stderr, "Connecting camera stills port to encoder input port\n");
1250
1251 // Now connect the camera to the encoder
1252 status = connect_ports(camera_still_port, encoder_input_port, &state.encoder_connection);
1253
1254 if (status != MMAL_SUCCESS)
1255 {
1256 vcos_log_error("%s: Failed to connect camera video port to encoder input", __func__);
1257 goto error;
1258 }
1259
1260 // Set up our userdata - this is passed though to the callback where we need the information.
1261 // Null until we open our filename
1262 callback_data.file_handle = NULL;
1263 callback_data.pstate = &state;
1264 vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0);
1265
1266 vcos_assert(vcos_status == VCOS_SUCCESS);
1267
1268 if (status != MMAL_SUCCESS)
1269 {
1270 vcos_log_error("Failed to setup encoder output");
1271 goto error;
1272 }
1273
1274 if (state.demoMode)
1275 {
1276 // Run for the user specific time..
1277 int num_iterations = state.timeout / state.demoInterval;
1278 int i;
1279 for (i=0;i<num_iterations;i++)
1280 {
1281 raspicamcontrol_cycle_test(state.camera_component);
1282 vcos_sleep(state.demoInterval);
1283 }
1284 }
1285 else
1286 {
1287 int num_iterations = state.timelapse ? state.timeout / state.timelapse : 1;
1288 int frame;
1289 FILE *output_file = NULL;
1290 char *use_filename = NULL; // Temporary filename while image being written
1291 char *final_filename = NULL; // Name that gets file once complete
1292 int64_t next_frame_ms = vcos_getmicrosecs64()/1000;
1293
1294 // If in timelapse mode, and timeout set to zero (or less), then take frames forever
1295 for (frame = 1; (num_iterations <= 0) || (frame<=num_iterations); frame++)
1296 {
1297 if (state.timelapse)
1298 {
1299 int64_t this_delay_ms = next_frame_ms - vcos_getmicrosecs64()/1000;
1300 if (this_delay_ms < 0)
1301 { // We are already past the next exposure time
1302 if (-this_delay_ms < -state.timelapse/2)
1303 { // Less than a half frame late, take a frame and hope to catch up next time
1304 next_frame_ms += state.timelapse;
1305 vcos_log_error("Frame %d is %d ms late", frame, (int)(-this_delay_ms));
1306 }
1307 else
1308 {
1309 int nskip = 1 + (-this_delay_ms)/state.timelapse;
1310 vcos_log_error("Skipping frame %d to restart at frame %d", frame, frame+nskip);
1311 frame += nskip;
1312 this_delay_ms += nskip * state.timelapse;
1313 vcos_sleep(this_delay_ms);
1314 next_frame_ms += (nskip + 1) * state.timelapse;
1315 }
1316 }
1317 else
1318 {
1319 vcos_sleep(this_delay_ms);
1320 next_frame_ms += state.timelapse;
1321 }
1322 }
1323 else
1324 vcos_sleep(state.timeout);
1325
1326 // Open the file
1327 if (state.filename)
1328 {
1329 if (state.filename[0] == '-')
1330 {
1331 output_file = stdout;
1332
1333 // Ensure we don't upset the output stream with diagnostics/info
1334 state.verbose = 0;
1335 }
1336 else
1337 {
1338 vcos_assert(use_filename == NULL && final_filename == NULL);
1339 status = create_filenames(&final_filename, &use_filename, state.filename, frame);
1340 if (status != MMAL_SUCCESS)
1341 {
1342 vcos_log_error("Unable to create filenames");
1343 goto error;
1344 }
1345
1346 if (state.verbose)
1347 fprintf(stderr, "Opening output file %s\n", final_filename);
1348 // Technically it is opening the temp~ filename which will be ranamed to the final filename
1349
1350 output_file = fopen(use_filename, "wb");
1351
1352 if (!output_file)
1353 {
1354 // Notify user, carry on but discarding encoded output buffers
1355 vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, use_filename);
1356 }
1357
1358 // asprintf used in timelapse mode allocates its own memory which we need to free
1359 }
1360
1361 callback_data.file_handle = output_file;
1362 }
1363
1364 // We only capture if a filename was specified and it opened
1365 if (output_file)
1366 {
1367 int num, q;
1368
1369 // Must do this before the encoder output port is enabled since
1370 // once enabled no further exif data is accepted
1371 add_exif_tags(&state);
1372
1373 // Same with raw, apparently need to set it for each capture, whilst port
1374 // is not enabled
1375 if (state.wantRAW)
1376 {
1377 if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_ENABLE_RAW_CAPTURE, 1) != MMAL_SUCCESS)
1378 {
1379 vcos_log_error("RAW was requested, but failed to enable");
1380 }
1381 }
1382
1383 // Enable the encoder output port
1384 encoder_output_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data;
1385
1386 if (state.verbose)
1387 fprintf(stderr, "Enabling encoder output port\n");
1388
1389 // Enable the encoder output port and tell it its callback function
1390 status = mmal_port_enable(encoder_output_port, encoder_buffer_callback);
1391
1392 // Send all the buffers to the encoder output port
1393 num = mmal_queue_length(state.encoder_pool->queue);
1394
1395 for (q=0;q<num;q++)
1396 {
1397 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.encoder_pool->queue);
1398
1399 if (!buffer)
1400 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
1401
1402 if (mmal_port_send_buffer(encoder_output_port, buffer)!= MMAL_SUCCESS)
1403 vcos_log_error("Unable to send a buffer to encoder output port (%d)", q);
1404 }
1405
1406 if (state.verbose)
1407 fprintf(stderr, "Starting capture %d\n", frame);
1408
1409 if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
1410 {
1411 vcos_log_error("%s: Failed to start capture", __func__);
1412 }
1413 else
1414 {
1415 // Wait for capture to complete
1416 // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error
1417 // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic
1418 vcos_semaphore_wait(&callback_data.complete_semaphore);
1419 if (state.verbose)
1420 fprintf(stderr, "Finished capture %d\n", frame);
1421 }
1422
1423 // Ensure we don't die if get callback with no open file
1424 callback_data.file_handle = NULL;
1425
1426 if (output_file != stdout)
1427 {
1428 fclose(output_file);
1429 vcos_assert(use_filename != NULL && final_filename != NULL);
1430 if (0 != rename(use_filename, final_filename))
1431 {
1432 vcos_log_error("Could not rename temp file to: %s; %s",
1433 final_filename,strerror(errno));
1434 }
1435 if (state.linkname)
1436 {
1437 char *use_link;
1438 char *final_link;
1439 status = create_filenames(&final_link, &use_link, state.linkname, frame);
1440
1441 // Create hard link if possible, symlink otherwise
1442 if (status != MMAL_SUCCESS
1443 || (0 != link(final_filename, use_link)
1444 && 0 != symlink(final_filename, use_link))
1445 || 0 != rename(use_link, final_link))
1446 {
1447 vcos_log_error("Could not link as filename: %s; %s",
1448 state.linkname,strerror(errno));
1449 }
1450 if (use_link) free(use_link);
1451 if (final_link) free(final_link);
1452 }
1453 }
1454 // Disable encoder output port
1455 status = mmal_port_disable(encoder_output_port);
1456 }
1457
1458 if (use_filename)
1459 {
1460 free(use_filename);
1461 use_filename = NULL;
1462 }
1463 if (final_filename)
1464 {
1465 free(final_filename);
1466 final_filename = NULL;
1467 }
1468 } // end for (frame)
1469
1470 vcos_semaphore_delete(&callback_data.complete_semaphore);
1471 }
1472 }
1473 else
1474 {
1475 mmal_status_to_int(status);
1476 vcos_log_error("%s: Failed to connect camera to preview", __func__);
1477 }
1478
1479 error:
1480
1481 mmal_status_to_int(status);
1482
1483 if (state.verbose)
1484 fprintf(stderr, "Closing down\n");
1485
1486 // Disable all our ports that are not handled by connections
1487 check_disable_port(camera_video_port);
1488 check_disable_port(encoder_output_port);
1489
1490 mmal_connection_destroy(state.preview_connection);
1491
1492 mmal_connection_destroy(state.encoder_connection);
1493
1494 /* Disable components */
1495 if (state.encoder_component)
1496 mmal_component_disable(state.encoder_component);
1497
1498 if (state.preview_parameters.preview_component)
1499 mmal_component_disable(state.preview_parameters.preview_component);
1500
1501 if (state.camera_component)
1502 mmal_component_disable(state.camera_component);
1503
1504 destroy_encoder_component(&state);
1505 raspipreview_destroy(&state.preview_parameters);
1506 destroy_camera_component(&state);
1507
1508 if (state.verbose)
1509 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
1510 }
1511
1512 if (status != MMAL_SUCCESS)
1513 raspicamcontrol_check_configuration(128);
1514
1515 return exit_code;
1516 }
1517 /* *INDENT-ON* */
1518