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 RaspiStillYUV.c
34 * Command line program to capture a still frame and dump uncompressed it to file.
35 * Also optionally display a preview/viewfinder of current camera input.
36 *
37 * \date 4th March 2013
38 * \Author: James Hughes
39 *
40 * Description
41 *
42 * 2 components are created; camera and preview.
43 * Camera component has three ports, preview, video and stills.
44 * Preview is connected using standard mmal connections, the stills output
45 * is written straight to the file in YUV 420 format via the requisite buffer
46 * callback. video port is not used
47 *
48 * We use the RaspiCamControl code to handle the specific camera settings.
49 * We use the RaspiPreview code to handle the generic preview
50 */
51
52 // We use some GNU extensions (basename)
53 #define _GNU_SOURCE
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <memory.h>
59 #include <sysexits.h>
60
61 #define VERSION_STRING "v1.3.2"
62
63 #include "bcm_host.h"
64 #include "interface/vcos/vcos.h"
65
66 #include "interface/mmal/mmal.h"
67 #include "interface/mmal/mmal_logging.h"
68 #include "interface/mmal/mmal_buffer.h"
69 #include "interface/mmal/util/mmal_util.h"
70 #include "interface/mmal/util/mmal_util_params.h"
71 #include "interface/mmal/util/mmal_default_components.h"
72 #include "interface/mmal/util/mmal_connection.h"
73
74
75 #include "RaspiCamControl.h"
76 #include "RaspiPreview.h"
77 #include "RaspiCLI.h"
78
79 #include <semaphore.h>
80
81 /// Camera number to use - we only have one camera, indexed from 0.
82 #define CAMERA_NUMBER 0
83
84 // Standard port setting for the camera component
85 #define MMAL_CAMERA_PREVIEW_PORT 0
86 #define MMAL_CAMERA_VIDEO_PORT 1
87 #define MMAL_CAMERA_CAPTURE_PORT 2
88
89
90 // Stills format information
91 #define STILLS_FRAME_RATE_NUM 3
92 #define STILLS_FRAME_RATE_DEN 1
93
94 /// Video render needs at least 2 buffers.
95 #define VIDEO_OUTPUT_BUFFERS_NUM 3
96
97 int mmal_status_to_int(MMAL_STATUS_T status);
98
99 /** Structure containing all state information for the current run
100 */
101 typedef struct
102 {
103 int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds
104 int width; /// Requested width of image
105 int height; /// requested height of image
106 char *filename; /// filename of output file
107 int verbose; /// !0 if want detailed run information
108 int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse
109 int useRGB; /// Output RGB data rather than YUV
110
111 RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters
112 RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
113
114 MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component
115 MMAL_COMPONENT_T *null_sink_component; /// Pointer to the camera component
116 MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview
117 MMAL_POOL_T *camera_pool; /// Pointer to the pool of buffers used by camera stills port
118 } RASPISTILLYUV_STATE;
119
120
121 /** Struct used to pass information in camera still port userdata to callback
122 */
123 typedef struct
124 {
125 FILE *file_handle; /// File handle to write buffer data to.
126 VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault)
127 RASPISTILLYUV_STATE *pstate; /// pointer to our state in case required in callback
128 } PORT_USERDATA;
129
130 static void display_valid_parameters(char *app_name);
131
132 /// Comamnd ID's and Structure defining our command line options
133 #define CommandHelp 0
134 #define CommandWidth 1
135 #define CommandHeight 2
136 #define CommandOutput 3
137 #define CommandVerbose 4
138 #define CommandTimeout 5
139 #define CommandTimelapse 6
140 #define CommandUseRGB 7
141
142 static COMMAND_LIST cmdline_commands[] =
143 {
144 { CommandHelp, "-help", "?", "This help information", 0 },
145 { CommandWidth, "-width", "w", "Set image width <size>", 1 },
146 { CommandHeight, "-height", "h", "Set image height <size>", 1 },
147 { CommandOutput, "-output", "o", "Output filename <filename>. If not specifed, no image is saved", 1 },
148 { CommandVerbose, "-verbose", "v", "Output verbose information during run", 0 },
149 { CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down. If not specified set to 5s", 1 },
150 { CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every <t>ms", 1},
151 { CommandUseRGB, "-rgb", "rgb","Save as RGB data rather than YUV", 0},
152 };
153
154 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
155
156 /**
157 * Assign a default set of parameters to the state passed in
158 *
159 * @param state Pointer to state structure to assign defaults to
160 */
default_status(RASPISTILLYUV_STATE * state)161 static void default_status(RASPISTILLYUV_STATE *state)
162 {
163 if (!state)
164 {
165 vcos_assert(0);
166 return;
167 }
168
169 // Default everything to zero
170 memset(state, 0, sizeof(RASPISTILLYUV_STATE));
171
172 // Now set anything non-zero
173 state->timeout = 5000; // 5s delay before take image
174 state->width = 2592;
175 state->height = 1944;
176 state->timelapse = 0;
177
178 // Setup preview window defaults
179 raspipreview_set_defaults(&state->preview_parameters);
180
181 // Set up the camera_parameters to default
182 raspicamcontrol_set_defaults(&state->camera_parameters);
183 }
184
185 /**
186 * Dump image state parameters to stderr. Used for debugging
187 *
188 * @param state Pointer to state structure to assign defaults to
189 */
dump_status(RASPISTILLYUV_STATE * state)190 static void dump_status(RASPISTILLYUV_STATE *state)
191 {
192 if (!state)
193 {
194 vcos_assert(0);
195 return;
196 }
197
198 fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width, state->height, state->filename);
199 fprintf(stderr, "Time delay %d, Timelapse %d\n", state->timeout, state->timelapse);
200
201 raspipreview_dump_parameters(&state->preview_parameters);
202 raspicamcontrol_dump_parameters(&state->camera_parameters);
203 }
204
205 /**
206 * Parse the incoming command line and put resulting parameters in to the state
207 *
208 * @param argc Number of arguments in command line
209 * @param argv Array of pointers to strings from command line
210 * @param state Pointer to state structure to assign any discovered parameters to
211 * @return non-0 if failed for some reason, 0 otherwise
212 */
parse_cmdline(int argc,const char ** argv,RASPISTILLYUV_STATE * state)213 static int parse_cmdline(int argc, const char **argv, RASPISTILLYUV_STATE *state)
214 {
215 // Parse the command line arguments.
216 // We are looking for --<something> or -<abreviation of something>
217
218 int valid = 1; // set 0 if we have a bad parameter
219 int i;
220
221 for (i = 1; i < argc && valid; i++)
222 {
223 int command_id, num_parameters;
224
225 if (!argv[i])
226 continue;
227
228 if (argv[i][0] != '-')
229 {
230 valid = 0;
231 continue;
232 }
233
234 // Assume parameter is valid until proven otherwise
235 valid = 1;
236
237 command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
238
239 // If we found a command but are missing a parameter, continue (and we will drop out of the loop)
240 if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) )
241 continue;
242
243 // We are now dealing with a command line option
244 switch (command_id)
245 {
246 case CommandHelp:
247 display_valid_parameters(basename(argv[0]));
248 return -1;
249
250 case CommandWidth: // Width > 0
251 if (sscanf(argv[i + 1], "%u", &state->width) != 1)
252 valid = 0;
253 else
254 i++;
255 break;
256
257 case CommandHeight: // Height > 0
258 if (sscanf(argv[i + 1], "%u", &state->height) != 1)
259 valid = 0;
260 else
261 i++;
262 break;
263
264 case CommandOutput: // output filename
265 {
266 int len = strlen(argv[i + 1]);
267 if (len)
268 {
269 state->filename = malloc(len + 1);
270 vcos_assert(state->filename);
271 if (state->filename)
272 strncpy(state->filename, argv[i + 1], len);
273 i++;
274 }
275 else
276 valid = 0;
277 break;
278 }
279
280 case CommandVerbose: // display lots of data during run
281 state->verbose = 1;
282 break;
283
284 case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds
285 {
286 if (sscanf(argv[i + 1], "%u", &state->timeout) == 1)
287 {
288 // TODO : What limits do we need for timeout?
289 i++;
290 }
291 else
292 valid = 0;
293 break;
294 }
295
296 case CommandTimelapse:
297 if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1)
298 valid = 0;
299 else
300 i++;
301 break;
302
303 case CommandUseRGB: // display lots of data during run
304 state->useRGB = 1;
305 break;
306
307 default:
308 {
309 // Try parsing for any image specific parameters
310 // result indicates how many parameters were used up, 0,1,2
311 // but we adjust by -1 as we have used one already
312 const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL;
313
314 int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg));
315
316 // Still unused, try preview options
317 if (!parms_used)
318 parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
319
320
321 // If no parms were used, this must be a bad parameters
322 if (!parms_used)
323 valid = 0;
324 else
325 i += parms_used - 1;
326
327 break;
328 }
329 }
330 }
331
332 if (!valid)
333 {
334 fprintf(stderr, "Invalid command line option (%s)\n", argv[i]);
335 return 1;
336 }
337
338 return 0;
339 }
340
341 /**
342 * Display usage information for the application to stdout
343 *
344 * @param app_name String to display as the application name
345 *
346 */
display_valid_parameters(char * app_name)347 static void display_valid_parameters(char *app_name)
348 {
349 fprintf(stderr, "Runs camera for specific time, and take uncompressed YUV capture at end if requested\n\n");
350 fprintf(stderr, "usage: %s [options]\n\n", app_name);
351
352 fprintf(stderr, "Image parameter commands\n\n");
353
354 raspicli_display_help(cmdline_commands, cmdline_commands_size);
355
356 // Help for preview options
357 raspipreview_display_help();
358
359 // Now display any help information from the camcontrol code
360 raspicamcontrol_display_help();
361
362 fprintf(stderr, "\n");
363
364 return;
365 }
366
367 /**
368 * buffer header callback function for camera control
369 *
370 * @param port Pointer to port from which callback originated
371 * @param buffer mmal buffer header pointer
372 */
camera_control_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)373 static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
374 {
375 fprintf(stderr, "Camera control callback cmd=0x%08x", buffer->cmd);
376
377 if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED)
378 {
379 }
380 else
381 {
382 vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd);
383 }
384
385 mmal_buffer_header_release(buffer);
386 }
387
388 /**
389 * buffer header callback function for camera output port
390 *
391 * Callback will dump buffer data to the specific file
392 *
393 * @param port Pointer to port from which callback originated
394 * @param buffer mmal buffer header pointer
395 */
camera_buffer_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)396 static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
397 {
398 int complete = 0;
399 // We pass our file handle and other stuff in via the userdata field.
400
401
402 PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
403
404 if (pData)
405 {
406 int bytes_written = buffer->length;
407
408 if (buffer->length)
409 {
410 mmal_buffer_header_mem_lock(buffer);
411
412 bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle);
413
414 mmal_buffer_header_mem_unlock(buffer);
415 }
416
417 // We need to check we wrote what we wanted - it's possible we have run out of storage.
418 if (bytes_written != buffer->length)
419 {
420 vcos_log_error("Unable to write buffer to file - aborting");
421 complete = 1;
422 }
423
424 // Check end of frame or error
425 if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED))
426 complete = 1;
427 }
428 else
429 {
430 vcos_log_error("Received a camera still buffer callback with no state");
431 }
432
433 // release buffer back to the pool
434 mmal_buffer_header_release(buffer);
435
436 // and send one back to the port (if still open)
437 if (port->is_enabled)
438 {
439 MMAL_STATUS_T status;
440 MMAL_BUFFER_HEADER_T *new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue);
441
442 // and back to the port from there.
443 if (new_buffer)
444 {
445 status = mmal_port_send_buffer(port, new_buffer);
446 }
447
448 if (!new_buffer || status != MMAL_SUCCESS)
449 vcos_log_error("Unable to return the buffer to the camera still port");
450 }
451
452 if (complete)
453 {
454 vcos_semaphore_post(&(pData->complete_semaphore));
455 }
456 }
457
458
459 /**
460 * Create the camera component, set up its ports
461 *
462 * @param state Pointer to state control struct
463 *
464 * @return 0 if failed, pointer to component if successful
465 *
466 */
create_camera_component(RASPISTILLYUV_STATE * state)467 static MMAL_STATUS_T create_camera_component(RASPISTILLYUV_STATE *state)
468 {
469 MMAL_COMPONENT_T *camera = 0;
470 MMAL_ES_FORMAT_T *format;
471 MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
472 MMAL_STATUS_T status;
473 MMAL_POOL_T *pool;
474
475 /* Create the component */
476 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
477
478 if (status != MMAL_SUCCESS)
479 {
480 vcos_log_error("Failed to create camera component");
481 goto error;
482 }
483
484 if (!camera->output_num)
485 {
486 vcos_log_error("Camera doesn't have output ports");
487 goto error;
488 }
489
490 preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
491 video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
492 still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
493
494 // Enable the camera, and tell it its control callback function
495 status = mmal_port_enable(camera->control, camera_control_callback);
496
497 if (status)
498 {
499 vcos_log_error("Unable to enable control port : error %d", status);
500 goto error;
501 }
502
503 // set up the camera configuration
504 {
505 MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
506 {
507 { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
508 .max_stills_w = state->width,
509 .max_stills_h = state->height,
510 .stills_yuv422 = 0,
511 .one_shot_stills = 1,
512 .max_preview_video_w = state->preview_parameters.previewWindow.width,
513 .max_preview_video_h = state->preview_parameters.previewWindow.height,
514 .num_preview_video_frames = 3,
515 .stills_capture_circular_buffer_height = 0,
516 .fast_preview_resume = 0,
517 .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC
518 };
519 mmal_port_parameter_set(camera->control, &cam_config.hdr);
520 }
521
522 raspicamcontrol_set_all_parameters(camera, &state->camera_parameters);
523
524 // Now set up the port formats
525
526 format = preview_port->format;
527
528 format->encoding = MMAL_ENCODING_OPAQUE;
529 format->encoding_variant = MMAL_ENCODING_I420;
530
531 format->es->video.width = state->preview_parameters.previewWindow.width;
532 format->es->video.height = state->preview_parameters.previewWindow.height;
533 format->es->video.crop.x = 0;
534 format->es->video.crop.y = 0;
535 format->es->video.crop.width = state->preview_parameters.previewWindow.width;
536 format->es->video.crop.height = state->preview_parameters.previewWindow.height;
537 format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM;
538 format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN;
539
540 status = mmal_port_format_commit(preview_port);
541
542 if (status)
543 {
544 vcos_log_error("camera viewfinder format couldn't be set");
545 goto error;
546 }
547
548 // Set the same format on the video port (which we dont use here)
549 mmal_format_full_copy(video_port->format, format);
550 status = mmal_port_format_commit(video_port);
551
552 if (status)
553 {
554 vcos_log_error("camera video format couldn't be set");
555 goto error;
556 }
557
558 // Ensure there are enough buffers to avoid dropping frames
559 if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
560 video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
561
562 format = still_port->format;
563
564 // Set our stills format on the stills port
565 if (state->useRGB)
566 {
567 format->encoding = MMAL_ENCODING_BGR24;
568 format->encoding_variant = MMAL_ENCODING_BGR24;
569 }
570 else
571 {
572 format->encoding = MMAL_ENCODING_I420;
573 format->encoding_variant = MMAL_ENCODING_I420;
574 }
575 format->es->video.width = state->width;
576 format->es->video.height = state->height;
577 format->es->video.crop.x = 0;
578 format->es->video.crop.y = 0;
579 format->es->video.crop.width = state->width;
580 format->es->video.crop.height = state->height;
581 format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM;
582 format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN;
583
584 if (still_port->buffer_size < still_port->buffer_size_min)
585 still_port->buffer_size = still_port->buffer_size_min;
586
587 still_port->buffer_num = still_port->buffer_num_recommended;
588
589 status = mmal_port_format_commit(still_port);
590
591 if (status)
592 {
593 vcos_log_error("camera still format couldn't be set");
594 goto error;
595 }
596
597 /* Enable component */
598 status = mmal_component_enable(camera);
599
600 if (status)
601 {
602 vcos_log_error("camera component couldn't be enabled");
603 goto error;
604 }
605
606 /* Create pool of buffer headers for the output port to consume */
607 pool = mmal_port_pool_create(still_port, still_port->buffer_num, still_port->buffer_size);
608
609 if (!pool)
610 {
611 vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name);
612 }
613
614 state->camera_pool = pool;
615 state->camera_component = camera;
616
617 if (state->verbose)
618 fprintf(stderr, "Camera component done\n");
619
620 return status;
621
622 error:
623
624 if (camera)
625 mmal_component_destroy(camera);
626
627 return status;
628 }
629
630 /**
631 * Destroy the camera component
632 *
633 * @param state Pointer to state control struct
634 *
635 */
destroy_camera_component(RASPISTILLYUV_STATE * state)636 static void destroy_camera_component(RASPISTILLYUV_STATE *state)
637 {
638 if (state->camera_component)
639 {
640 mmal_component_destroy(state->camera_component);
641 state->camera_component = NULL;
642 }
643 }
644
645 /**
646 * Connect two specific ports together
647 *
648 * @param output_port Pointer the output port
649 * @param input_port Pointer the input port
650 * @param Pointer to a mmal connection pointer, reassigned if function successful
651 * @return Returns a MMAL_STATUS_T giving result of operation
652 *
653 */
connect_ports(MMAL_PORT_T * output_port,MMAL_PORT_T * input_port,MMAL_CONNECTION_T ** connection)654 static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
655 {
656 MMAL_STATUS_T status;
657
658 status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
659
660 if (status == MMAL_SUCCESS)
661 {
662 status = mmal_connection_enable(*connection);
663 if (status != MMAL_SUCCESS)
664 mmal_connection_destroy(*connection);
665 }
666
667 return status;
668 }
669
670 /**
671 * Checks if specified port is valid and enabled, then disables it
672 *
673 * @param port Pointer the port
674 *
675 */
check_disable_port(MMAL_PORT_T * port)676 static void check_disable_port(MMAL_PORT_T *port)
677 {
678 if (port && port->is_enabled)
679 mmal_port_disable(port);
680 }
681
682 /**
683 * Handler for sigint signals
684 *
685 * @param signal_number ID of incoming signal.
686 *
687 */
signal_handler(int signal_number)688 static void signal_handler(int signal_number)
689 {
690 // Going to abort on all signals
691 vcos_log_error("Aborting program\n");
692
693 // Need to close any open stuff...
694
695 exit(255);
696 }
697
698 /**
699 * main
700 */
main(int argc,const char ** argv)701 int main(int argc, const char **argv)
702 {
703 // Our main data storage vessel..
704 RASPISTILLYUV_STATE state;
705 int exit_code = EX_OK;
706
707 MMAL_STATUS_T status = MMAL_SUCCESS;
708 MMAL_PORT_T *camera_preview_port = NULL;
709 MMAL_PORT_T *camera_video_port = NULL;
710 MMAL_PORT_T *camera_still_port = NULL;
711 MMAL_PORT_T *preview_input_port = NULL;
712 FILE *output_file = NULL;
713
714 bcm_host_init();
715
716 // Register our application with the logging system
717 vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY);
718
719 signal(SIGINT, signal_handler);
720
721 // Do we have any parameters
722 if (argc == 1)
723 {
724 fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING);
725
726 display_valid_parameters(basename(argv[0]));
727 exit(EX_USAGE);
728 }
729
730 default_status(&state);
731
732 // Parse the command line and put options in to our status structure
733 if (parse_cmdline(argc, argv, &state))
734 {
735 status = -1;
736 exit(EX_USAGE);
737 }
738
739 if (state.verbose)
740 {
741 fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING);
742 dump_status(&state);
743 }
744
745 // OK, we have a nice set of parameters. Now set up our components
746 // We have two components. Camera and Preview
747 // Camera is different in stills/video, but preview
748 // is the same so handed off to a separate module
749
750 if ((status = create_camera_component(&state)) != MMAL_SUCCESS)
751 {
752 vcos_log_error("%s: Failed to create camera component", __func__);
753 exit_code = EX_SOFTWARE;
754 }
755 else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS)
756 {
757 vcos_log_error("%s: Failed to create preview component", __func__);
758 destroy_camera_component(&state);
759 exit_code = EX_SOFTWARE;
760 }
761 else
762 {
763 PORT_USERDATA callback_data;
764
765 if (state.verbose)
766 fprintf(stderr, "Starting component connection stage\n");
767
768 camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
769 camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT];
770 camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
771
772 // Note we are lucky that the preview and null sink components use the same input port
773 // so we can simple do this without conditionals
774 preview_input_port = state.preview_parameters.preview_component->input[0];
775
776 // Connect camera to preview (which might be a null_sink if no preview required)
777 status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection);
778
779 if (status == MMAL_SUCCESS)
780 {
781 VCOS_STATUS_T vcos_status;
782
783 if (state.filename)
784 {
785 if (state.verbose)
786 fprintf(stderr, "Opening output file %s\n", state.filename);
787
788 output_file = fopen(state.filename, "wb");
789 if (!output_file)
790 {
791 // Notify user, carry on but discarding output buffers
792 vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, state.filename);
793 }
794 }
795
796 // Set up our userdata - this is passed though to the callback where we need the information.
797 callback_data.file_handle = output_file;
798 callback_data.pstate = &state;
799
800 vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0);
801 vcos_assert(vcos_status == VCOS_SUCCESS);
802
803 camera_still_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data;
804
805 if (state.verbose)
806 fprintf(stderr, "Enabling camera still output port\n");
807
808 // Enable the camera still output port and tell it its callback function
809 status = mmal_port_enable(camera_still_port, camera_buffer_callback);
810
811 if (status != MMAL_SUCCESS)
812 {
813 vcos_log_error("Failed to setup camera output");
814 goto error;
815 }
816
817 if (state.verbose)
818 fprintf(stderr, "Starting video preview\n");
819
820 int num_iterations = state.timelapse ? state.timeout / state.timelapse : 1;
821 int frame;
822 FILE *output_file = NULL;
823
824 for (frame = 1;frame<=num_iterations; frame++)
825 {
826 if (state.timelapse)
827 vcos_sleep(state.timelapse);
828 else
829 vcos_sleep(state.timeout);
830
831 // Open the file
832 if (state.filename)
833 {
834 if (state.filename[0] == '-')
835 {
836 output_file = stdout;
837
838 // Ensure we don't upset the output stream with diagnostics/info
839 state.verbose = 0;
840 }
841 else
842 {
843 char *use_filename = state.filename;
844
845 if (state.timelapse)
846 asprintf(&use_filename, state.filename, frame);
847
848 if (state.verbose)
849 fprintf(stderr, "Opening output file %s\n", use_filename);
850
851 output_file = fopen(use_filename, "wb");
852
853 if (!output_file)
854 {
855 // Notify user, carry on but discarding encoded output buffers
856 vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, use_filename);
857 }
858
859 // asprintf used in timelapse mode allocates its own memory which we need to free
860 if (state.timelapse)
861 free(use_filename);
862 }
863
864 callback_data.file_handle = output_file;
865 }
866
867 // And only do the capture if we have specified a filename and its opened OK
868 if (output_file)
869 {
870 // Send all the buffers to the camera output port
871 {
872 int num = mmal_queue_length(state.camera_pool->queue);
873 int q;
874
875 for (q=0;q<num;q++)
876 {
877 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.camera_pool->queue);
878
879 if (!buffer)
880 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
881
882 if (mmal_port_send_buffer(camera_still_port, buffer)!= MMAL_SUCCESS)
883 vcos_log_error("Unable to send a buffer to camera output port (%d)", q);
884 }
885 }
886
887 if (state.verbose)
888 fprintf(stderr, "Starting capture %d\n", frame);
889
890 // Fire the capture
891 if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
892 {
893 vcos_log_error("%s: Failed to start capture", __func__);
894 }
895 else
896 {
897 // Wait for capture to complete
898 // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error
899 // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic
900 vcos_semaphore_wait(&callback_data.complete_semaphore);
901
902 if (state.verbose)
903 fprintf(stderr, "Finished capture %d\n", frame);
904 }
905
906 // Ensure we don't die if get callback with no open file
907 callback_data.file_handle = NULL;
908
909 if (output_file != stdout)
910 fclose(output_file);
911 }
912 }
913 vcos_semaphore_delete(&callback_data.complete_semaphore);
914 }
915 else
916 {
917 mmal_status_to_int(status);
918 vcos_log_error("%s: Failed to connect camera to preview", __func__);
919 }
920
921 error:
922
923 mmal_status_to_int(status);
924
925 if (state.verbose)
926 fprintf(stderr, "Closing down\n");
927
928 if (output_file)
929 fclose(output_file);
930
931 // Disable all our ports that are not handled by connections
932 check_disable_port(camera_video_port);
933
934 mmal_connection_destroy(state.preview_connection);
935
936 /* Disable components */
937 if (state.preview_parameters.preview_component)
938 mmal_component_disable(state.preview_parameters.preview_component);
939
940 if (state.camera_component)
941 mmal_component_disable(state.camera_component);
942
943 raspipreview_destroy(&state.preview_parameters);
944 destroy_camera_component(&state);
945
946 if (state.verbose)
947 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
948 }
949
950 if (status != MMAL_SUCCESS)
951 raspicamcontrol_check_configuration(128);
952
953 return exit_code;
954 }
955
956
957
958 /* *INDENT-ON* */
959