rpicamsrc: fix indentation
[platform/upstream/gstreamer.git] / sys / rpicamsrc / RaspiStillYUV.c
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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* */