3 * Copyright (c) 2013 Jan Schmidt <jan@centricular.com>
5 Copyright (c) 2013, Broadcom Europe Ltd
6 Copyright (c) 2013, James Hughes
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.
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.
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.
37 * \date 4th March 2013
38 * \Author: James Hughes
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
48 * We use the RaspiCamControl code to handle the specific camera settings.
49 * We use the RaspiPreview code to handle the generic preview
52 // We use some GNU extensions (basename)
61 #define VERSION_STRING "v1.3.2"
64 #include "interface/vcos/vcos.h"
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"
75 #include "RaspiCamControl.h"
76 #include "RaspiPreview.h"
79 #include <semaphore.h>
81 /// Camera number to use - we only have one camera, indexed from 0.
82 #define CAMERA_NUMBER 0
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
90 // Stills format information
91 #define STILLS_FRAME_RATE_NUM 3
92 #define STILLS_FRAME_RATE_DEN 1
94 /// Video render needs at least 2 buffers.
95 #define VIDEO_OUTPUT_BUFFERS_NUM 3
97 int mmal_status_to_int(MMAL_STATUS_T status);
99 /** Structure containing all state information for the current run
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
111 RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters
112 RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
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;
121 /** Struct used to pass information in camera still port userdata to callback
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
130 static void display_valid_parameters(char *app_name);
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
142 static COMMAND_LIST cmdline_commands[] =
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},
154 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
157 * Assign a default set of parameters to the state passed in
159 * @param state Pointer to state structure to assign defaults to
161 static void default_status(RASPISTILLYUV_STATE *state)
169 // Default everything to zero
170 memset(state, 0, sizeof(RASPISTILLYUV_STATE));
172 // Now set anything non-zero
173 state->timeout = 5000; // 5s delay before take image
175 state->height = 1944;
176 state->timelapse = 0;
178 // Setup preview window defaults
179 raspipreview_set_defaults(&state->preview_parameters);
181 // Set up the camera_parameters to default
182 raspicamcontrol_set_defaults(&state->camera_parameters);
186 * Dump image state parameters to stderr. Used for debugging
188 * @param state Pointer to state structure to assign defaults to
190 static void dump_status(RASPISTILLYUV_STATE *state)
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);
201 raspipreview_dump_parameters(&state->preview_parameters);
202 raspicamcontrol_dump_parameters(&state->camera_parameters);
206 * Parse the incoming command line and put resulting parameters in to the state
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
213 static int parse_cmdline(int argc, const char **argv, RASPISTILLYUV_STATE *state)
215 // Parse the command line arguments.
216 // We are looking for --<something> or -<abreviation of something>
218 int valid = 1; // set 0 if we have a bad parameter
221 for (i = 1; i < argc && valid; i++)
223 int command_id, num_parameters;
228 if (argv[i][0] != '-')
234 // Assume parameter is valid until proven otherwise
237 command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
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) )
243 // We are now dealing with a command line option
247 display_valid_parameters(basename(argv[0]));
250 case CommandWidth: // Width > 0
251 if (sscanf(argv[i + 1], "%u", &state->width) != 1)
257 case CommandHeight: // Height > 0
258 if (sscanf(argv[i + 1], "%u", &state->height) != 1)
264 case CommandOutput: // output filename
266 int len = strlen(argv[i + 1]);
269 state->filename = malloc(len + 1);
270 vcos_assert(state->filename);
272 strncpy(state->filename, argv[i + 1], len);
280 case CommandVerbose: // display lots of data during run
284 case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds
286 if (sscanf(argv[i + 1], "%u", &state->timeout) == 1)
288 // TODO : What limits do we need for timeout?
296 case CommandTimelapse:
297 if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1)
303 case CommandUseRGB: // display lots of data during run
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;
314 int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg));
316 // Still unused, try preview options
318 parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
321 // If no parms were used, this must be a bad parameters
334 fprintf(stderr, "Invalid command line option (%s)\n", argv[i]);
342 * Display usage information for the application to stdout
344 * @param app_name String to display as the application name
347 static void display_valid_parameters(char *app_name)
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);
352 fprintf(stderr, "Image parameter commands\n\n");
354 raspicli_display_help(cmdline_commands, cmdline_commands_size);
356 // Help for preview options
357 raspipreview_display_help();
359 // Now display any help information from the camcontrol code
360 raspicamcontrol_display_help();
362 fprintf(stderr, "\n");
368 * buffer header callback function for camera control
370 * @param port Pointer to port from which callback originated
371 * @param buffer mmal buffer header pointer
373 static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
375 fprintf(stderr, "Camera control callback cmd=0x%08x", buffer->cmd);
377 if (buffer->cmd == MMAL_EVENT_PARAMETER_CHANGED)
382 vcos_log_error("Received unexpected camera control callback event, 0x%08x", buffer->cmd);
385 mmal_buffer_header_release(buffer);
389 * buffer header callback function for camera output port
391 * Callback will dump buffer data to the specific file
393 * @param port Pointer to port from which callback originated
394 * @param buffer mmal buffer header pointer
396 static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
399 // We pass our file handle and other stuff in via the userdata field.
402 PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
406 int bytes_written = buffer->length;
410 mmal_buffer_header_mem_lock(buffer);
412 bytes_written = fwrite(buffer->data, 1, buffer->length, pData->file_handle);
414 mmal_buffer_header_mem_unlock(buffer);
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)
420 vcos_log_error("Unable to write buffer to file - aborting");
424 // Check end of frame or error
425 if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED))
430 vcos_log_error("Received a camera still buffer callback with no state");
433 // release buffer back to the pool
434 mmal_buffer_header_release(buffer);
436 // and send one back to the port (if still open)
437 if (port->is_enabled)
439 MMAL_STATUS_T status;
440 MMAL_BUFFER_HEADER_T *new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue);
442 // and back to the port from there.
445 status = mmal_port_send_buffer(port, new_buffer);
448 if (!new_buffer || status != MMAL_SUCCESS)
449 vcos_log_error("Unable to return the buffer to the camera still port");
454 vcos_semaphore_post(&(pData->complete_semaphore));
460 * Create the camera component, set up its ports
462 * @param state Pointer to state control struct
464 * @return 0 if failed, pointer to component if successful
467 static MMAL_STATUS_T create_camera_component(RASPISTILLYUV_STATE *state)
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;
475 /* Create the component */
476 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
478 if (status != MMAL_SUCCESS)
480 vcos_log_error("Failed to create camera component");
484 if (!camera->output_num)
486 vcos_log_error("Camera doesn't have output ports");
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];
494 // Enable the camera, and tell it its control callback function
495 status = mmal_port_enable(camera->control, camera_control_callback);
499 vcos_log_error("Unable to enable control port : error %d", status);
503 // set up the camera configuration
505 MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
507 { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
508 .max_stills_w = state->width,
509 .max_stills_h = state->height,
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
519 mmal_port_parameter_set(camera->control, &cam_config.hdr);
522 raspicamcontrol_set_all_parameters(camera, &state->camera_parameters);
524 // Now set up the port formats
526 format = preview_port->format;
528 format->encoding = MMAL_ENCODING_OPAQUE;
529 format->encoding_variant = MMAL_ENCODING_I420;
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;
540 status = mmal_port_format_commit(preview_port);
544 vcos_log_error("camera viewfinder format couldn't be set");
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);
554 vcos_log_error("camera video format couldn't be set");
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;
562 format = still_port->format;
564 // Set our stills format on the stills port
567 format->encoding = MMAL_ENCODING_BGR24;
568 format->encoding_variant = MMAL_ENCODING_BGR24;
572 format->encoding = MMAL_ENCODING_I420;
573 format->encoding_variant = MMAL_ENCODING_I420;
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;
584 if (still_port->buffer_size < still_port->buffer_size_min)
585 still_port->buffer_size = still_port->buffer_size_min;
587 still_port->buffer_num = still_port->buffer_num_recommended;
589 status = mmal_port_format_commit(still_port);
593 vcos_log_error("camera still format couldn't be set");
597 /* Enable component */
598 status = mmal_component_enable(camera);
602 vcos_log_error("camera component couldn't be enabled");
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);
611 vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name);
614 state->camera_pool = pool;
615 state->camera_component = camera;
618 fprintf(stderr, "Camera component done\n");
625 mmal_component_destroy(camera);
631 * Destroy the camera component
633 * @param state Pointer to state control struct
636 static void destroy_camera_component(RASPISTILLYUV_STATE *state)
638 if (state->camera_component)
640 mmal_component_destroy(state->camera_component);
641 state->camera_component = NULL;
646 * Connect two specific ports together
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
654 static MMAL_STATUS_T connect_ports(MMAL_PORT_T *output_port, MMAL_PORT_T *input_port, MMAL_CONNECTION_T **connection)
656 MMAL_STATUS_T status;
658 status = mmal_connection_create(connection, output_port, input_port, MMAL_CONNECTION_FLAG_TUNNELLING | MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT);
660 if (status == MMAL_SUCCESS)
662 status = mmal_connection_enable(*connection);
663 if (status != MMAL_SUCCESS)
664 mmal_connection_destroy(*connection);
671 * Checks if specified port is valid and enabled, then disables it
673 * @param port Pointer the port
676 static void check_disable_port(MMAL_PORT_T *port)
678 if (port && port->is_enabled)
679 mmal_port_disable(port);
683 * Handler for sigint signals
685 * @param signal_number ID of incoming signal.
688 static void signal_handler(int signal_number)
690 // Going to abort on all signals
691 vcos_log_error("Aborting program\n");
693 // Need to close any open stuff...
701 int main(int argc, const char **argv)
703 // Our main data storage vessel..
704 RASPISTILLYUV_STATE state;
705 int exit_code = EX_OK;
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;
716 // Register our application with the logging system
717 vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY);
719 signal(SIGINT, signal_handler);
721 // Do we have any parameters
724 fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING);
726 display_valid_parameters(basename(argv[0]));
730 default_status(&state);
732 // Parse the command line and put options in to our status structure
733 if (parse_cmdline(argc, argv, &state))
741 fprintf(stderr, "\n%s Camera App %s\n\n", basename(argv[0]), VERSION_STRING);
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
750 if ((status = create_camera_component(&state)) != MMAL_SUCCESS)
752 vcos_log_error("%s: Failed to create camera component", __func__);
753 exit_code = EX_SOFTWARE;
755 else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS)
757 vcos_log_error("%s: Failed to create preview component", __func__);
758 destroy_camera_component(&state);
759 exit_code = EX_SOFTWARE;
763 PORT_USERDATA callback_data;
766 fprintf(stderr, "Starting component connection stage\n");
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];
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];
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);
779 if (status == MMAL_SUCCESS)
781 VCOS_STATUS_T vcos_status;
786 fprintf(stderr, "Opening output file %s\n", state.filename);
788 output_file = fopen(state.filename, "wb");
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);
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;
800 vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0);
801 vcos_assert(vcos_status == VCOS_SUCCESS);
803 camera_still_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data;
806 fprintf(stderr, "Enabling camera still output port\n");
808 // Enable the camera still output port and tell it its callback function
809 status = mmal_port_enable(camera_still_port, camera_buffer_callback);
811 if (status != MMAL_SUCCESS)
813 vcos_log_error("Failed to setup camera output");
818 fprintf(stderr, "Starting video preview\n");
820 int num_iterations = state.timelapse ? state.timeout / state.timelapse : 1;
822 FILE *output_file = NULL;
824 for (frame = 1;frame<=num_iterations; frame++)
827 vcos_sleep(state.timelapse);
829 vcos_sleep(state.timeout);
834 if (state.filename[0] == '-')
836 output_file = stdout;
838 // Ensure we don't upset the output stream with diagnostics/info
843 char *use_filename = state.filename;
846 asprintf(&use_filename, state.filename, frame);
849 fprintf(stderr, "Opening output file %s\n", use_filename);
851 output_file = fopen(use_filename, "wb");
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);
859 // asprintf used in timelapse mode allocates its own memory which we need to free
864 callback_data.file_handle = output_file;
867 // And only do the capture if we have specified a filename and its opened OK
870 // Send all the buffers to the camera output port
872 int num = mmal_queue_length(state.camera_pool->queue);
877 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.camera_pool->queue);
880 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
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);
888 fprintf(stderr, "Starting capture %d\n", frame);
891 if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
893 vcos_log_error("%s: Failed to start capture", __func__);
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);
903 fprintf(stderr, "Finished capture %d\n", frame);
906 // Ensure we don't die if get callback with no open file
907 callback_data.file_handle = NULL;
909 if (output_file != stdout)
913 vcos_semaphore_delete(&callback_data.complete_semaphore);
917 mmal_status_to_int(status);
918 vcos_log_error("%s: Failed to connect camera to preview", __func__);
923 mmal_status_to_int(status);
926 fprintf(stderr, "Closing down\n");
931 // Disable all our ports that are not handled by connections
932 check_disable_port(camera_video_port);
934 mmal_connection_destroy(state.preview_connection);
936 /* Disable components */
937 if (state.preview_parameters.preview_component)
938 mmal_component_disable(state.preview_parameters.preview_component);
940 if (state.camera_component)
941 mmal_component_disable(state.camera_component);
943 raspipreview_destroy(&state.preview_parameters);
944 destroy_camera_component(&state);
947 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
950 if (status != MMAL_SUCCESS)
951 raspicamcontrol_check_configuration(128);