• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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