1 /*
2 Copyright (c) 2018, Raspberry Pi (Trading) Ltd.
3 Copyright (c) 2013, Broadcom Europe Ltd
4 Copyright (c) 2013, James Hughes
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 * Neither the name of the copyright holder nor the
15 names of its contributors may be used to endorse or promote products
16 derived from this software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
22 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * \file RaspiStillYUV.c
32 * Command line program to capture a still frame and dump uncompressed it to file.
33 * Also optionally display a preview/viewfinder of current camera input.
34 *
35 * Description
36 *
37 * 2 components are created; camera and preview.
38 * Camera component has three ports, preview, video and stills.
39 * Preview is connected using standard mmal connections, the stills output
40 * is written straight to the file in YUV 420 format via the requisite buffer
41 * callback. video port is not used
42 *
43 * We use the RaspiCamControl code to handle the specific camera settings.
44 * We use the RaspiPreview code to handle the generic preview
45 */
46
47 // We use some GNU extensions (basename)
48 #ifndef _GNU_SOURCE
49 #define _GNU_SOURCE
50 #endif
51
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <memory.h>
56 #include <sysexits.h>
57 #include <unistd.h>
58 #include <errno.h>
59
60 #include "bcm_host.h"
61 #include "interface/vcos/vcos.h"
62
63 #include "interface/mmal/mmal.h"
64 #include "interface/mmal/mmal_logging.h"
65 #include "interface/mmal/mmal_buffer.h"
66 #include "interface/mmal/util/mmal_util.h"
67 #include "interface/mmal/util/mmal_util_params.h"
68 #include "interface/mmal/util/mmal_default_components.h"
69 #include "interface/mmal/util/mmal_connection.h"
70
71 #include "RaspiCamControl.h"
72 #include "RaspiPreview.h"
73 #include "RaspiCLI.h"
74 #include "RaspiCommonSettings.h"
75 #include "RaspiHelpers.h"
76 #include "RaspiGPS.h"
77
78 #include <semaphore.h>
79
80 // Standard port setting for the camera component
81 #define MMAL_CAMERA_PREVIEW_PORT 0
82 #define MMAL_CAMERA_VIDEO_PORT 1
83 #define MMAL_CAMERA_CAPTURE_PORT 2
84
85 // Stills format information
86 // 0 implies variable
87 #define STILLS_FRAME_RATE_NUM 0
88 #define STILLS_FRAME_RATE_DEN 1
89
90 /// Video render needs at least 2 buffers.
91 #define VIDEO_OUTPUT_BUFFERS_NUM 3
92
93 /// Frame advance method
94 enum
95 {
96 FRAME_NEXT_SINGLE,
97 FRAME_NEXT_TIMELAPSE,
98 FRAME_NEXT_KEYPRESS,
99 FRAME_NEXT_FOREVER,
100 FRAME_NEXT_GPIO,
101 FRAME_NEXT_SIGNAL,
102 FRAME_NEXT_IMMEDIATELY
103 };
104
105 #define CAMERA_SETTLE_TIME 1000
106
107 int mmal_status_to_int(MMAL_STATUS_T status);
108
109 /** Structure containing all state information for the current run
110 */
111 typedef struct
112 {
113 RASPICOMMONSETTINGS_PARAMETERS common_settings; /// Non-app specific settings
114 int timeout; /// Time taken before frame is grabbed and app then shuts down. Units are milliseconds
115 char *linkname; /// filename of output file
116 int timelapse; /// Delay between each picture in timelapse mode. If 0, disable timelapse
117 MMAL_FOURCC_T encoding; /// Use a MMAL encoding other than YUV
118 int fullResPreview; /// If set, the camera preview port runs at capture resolution. Reduces fps.
119 int frameNextMethod; /// Which method to use to advance to next frame
120 int burstCaptureMode; /// Enable burst mode
121 int onlyLuma; /// Only output the luma / Y plane of the YUV data
122
123 RASPIPREVIEW_PARAMETERS preview_parameters; /// Preview setup parameters
124 RASPICAM_CAMERA_PARAMETERS camera_parameters; /// Camera setup parameters
125
126 MMAL_COMPONENT_T *camera_component; /// Pointer to the camera component
127 MMAL_COMPONENT_T *null_sink_component; /// Pointer to the camera component
128 MMAL_CONNECTION_T *preview_connection; /// Pointer to the connection from camera to preview
129 MMAL_POOL_T *camera_pool; /// Pointer to the pool of buffers used by camera stills port
130 } RASPISTILLYUV_STATE;
131
132 /** Struct used to pass information in camera still port userdata to callback
133 */
134 typedef struct
135 {
136 FILE *file_handle; /// File handle to write buffer data to.
137 VCOS_SEMAPHORE_T complete_semaphore; /// semaphore which is posted when we reach end of frame (indicates end of capture or fault)
138 RASPISTILLYUV_STATE *pstate; /// pointer to our state in case required in callback
139 } PORT_USERDATA;
140
141 /// Comamnd ID's and Structure defining our command line options
142 enum
143 {
144 CommandTimeout,
145 CommandTimelapse,
146 CommandUseRGB,
147 CommandFullResPreview,
148 CommandLink,
149 CommandKeypress,
150 CommandSignal,
151 CommandBurstMode,
152 CommandOnlyLuma,
153 CommandUseBGR
154 };
155
156 static COMMAND_LIST cmdline_commands[] =
157 {
158 { CommandTimeout, "-timeout", "t", "Time (in ms) before takes picture and shuts down. If not specified set to 5s", 1 },
159 { CommandTimelapse,"-timelapse", "tl", "Timelapse mode. Takes a picture every <t>ms", 1},
160 { CommandUseRGB, "-rgb", "rgb","Save as RGB data rather than YUV", 0},
161 { CommandFullResPreview,"-fullpreview","fp", "Run the preview using the still capture resolution (may reduce preview fps)", 0},
162 { CommandLink, "-latest", "l", "Link latest complete image to filename <filename>", 1},
163 { CommandKeypress,"-keypress", "k", "Wait between captures for a ENTER, X then ENTER to exit", 0},
164 { CommandSignal, "-signal", "s", "Wait between captures for a SIGUSR1 from another process", 0},
165 { CommandBurstMode, "-burst", "bm", "Enable 'burst capture mode'", 0},
166 { CommandOnlyLuma, "-luma", "y", "Only output the luma / Y of the YUV data'", 0},
167 { CommandUseBGR, "-bgr", "bgr","Save as BGR data rather than YUV", 0},
168 };
169
170 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
171
172 static struct
173 {
174 char *description;
175 int nextFrameMethod;
176 } next_frame_description[] =
177 {
178 {"Single capture", FRAME_NEXT_SINGLE},
179 {"Capture on timelapse", FRAME_NEXT_TIMELAPSE},
180 {"Capture on keypress", FRAME_NEXT_KEYPRESS},
181 {"Run forever", FRAME_NEXT_FOREVER},
182 {"Capture on GPIO", FRAME_NEXT_GPIO},
183 {"Capture on signal", FRAME_NEXT_SIGNAL},
184 };
185
186 static int next_frame_description_size = sizeof(next_frame_description) / sizeof(next_frame_description[0]);
187
188 /**
189 * Assign a default set of parameters to the state passed in
190 *
191 * @param state Pointer to state structure to assign defaults to
192 */
default_status(RASPISTILLYUV_STATE * state)193 static void default_status(RASPISTILLYUV_STATE *state)
194 {
195 if (!state)
196 {
197 vcos_assert(0);
198 return;
199 }
200
201 // Default everything to zero
202 memset(state, 0, sizeof(RASPISTILLYUV_STATE));
203
204 raspicommonsettings_set_defaults(&state->common_settings);
205
206 // Now set anything non-zero
207 state->timeout = -1; // replaced with 5000ms later if unset
208 state->timelapse = 0;
209 state->linkname = NULL;
210 state->fullResPreview = 0;
211 state->frameNextMethod = FRAME_NEXT_SINGLE;
212 state->burstCaptureMode=0;
213 state->onlyLuma = 0;
214
215 // Setup preview window defaults
216 raspipreview_set_defaults(&state->preview_parameters);
217
218 // Set up the camera_parameters to default
219 raspicamcontrol_set_defaults(&state->camera_parameters);
220 }
221
222 /**
223 * Dump image state parameters to stderr. Used for debugging
224 *
225 * @param state Pointer to state structure to assign defaults to
226 */
dump_status(RASPISTILLYUV_STATE * state)227 static void dump_status(RASPISTILLYUV_STATE *state)
228 {
229 int i;
230 if (!state)
231 {
232 vcos_assert(0);
233 return;
234 }
235
236 raspicommonsettings_dump_parameters(&state->common_settings);
237
238 fprintf(stderr, "Time delay %d, Timelapse %d\n", state->timeout, state->timelapse);
239 fprintf(stderr, "Link to latest frame enabled ");
240 if (state->linkname)
241 {
242 fprintf(stderr, " yes, -> %s\n", state->linkname);
243 }
244 else
245 {
246 fprintf(stderr, " no\n");
247 }
248 fprintf(stderr, "Full resolution preview %s\n", state->fullResPreview ? "Yes": "No");
249
250 fprintf(stderr, "Capture method : ");
251 for (i=0; i<next_frame_description_size; i++)
252 {
253 if (state->frameNextMethod == next_frame_description[i].nextFrameMethod)
254 fprintf(stderr, "%s", next_frame_description[i].description);
255 }
256 fprintf(stderr, "\n\n");
257
258 raspipreview_dump_parameters(&state->preview_parameters);
259 raspicamcontrol_dump_parameters(&state->camera_parameters);
260 }
261
262 /**
263 * Display usage information for the application to stdout
264 *
265 * @param app_name String to display as the application name
266 *
267 */
application_help_message(char * app_name)268 static void application_help_message(char *app_name)
269 {
270 fprintf(stdout, "Runs camera for specific time, and take uncompressed YUV or RGB capture at end if requested\n\n");
271 fprintf(stdout, "usage: %s [options]\n\n", app_name);
272
273 fprintf(stdout, "Image parameter commands\n\n");
274
275 raspicli_display_help(cmdline_commands, cmdline_commands_size);
276
277 return;
278 }
279
280 /**
281 * Parse the incoming command line and put resulting parameters in to the state
282 *
283 * @param argc Number of arguments in command line
284 * @param argv Array of pointers to strings from command line
285 * @param state Pointer to state structure to assign any discovered parameters to
286 * @return non-0 if failed for some reason, 0 otherwise
287 */
parse_cmdline(int argc,const char ** argv,RASPISTILLYUV_STATE * state)288 static int parse_cmdline(int argc, const char **argv, RASPISTILLYUV_STATE *state)
289 {
290 // Parse the command line arguments.
291 // We are looking for --<something> or -<abbreviation of something>
292
293 int valid = 1; // set 0 if we have a bad parameter
294 int i;
295
296 for (i = 1; i < argc && valid; i++)
297 {
298 int command_id, num_parameters;
299
300 if (!argv[i])
301 continue;
302
303 if (argv[i][0] != '-')
304 {
305 valid = 0;
306 continue;
307 }
308
309 // Assume parameter is valid until proven otherwise
310 valid = 1;
311
312 command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, &argv[i][1], &num_parameters);
313
314 // If we found a command but are missing a parameter, continue (and we will drop out of the loop)
315 if (command_id != -1 && num_parameters > 0 && (i + 1 >= argc) )
316 continue;
317
318 // We are now dealing with a command line option
319 switch (command_id)
320 {
321 case CommandLink :
322 {
323 int len = strlen(argv[i+1]);
324 if (len)
325 {
326 state->linkname = malloc(len + 10);
327 vcos_assert(state->linkname);
328 if (state->linkname)
329 strncpy(state->linkname, argv[i + 1], len+1);
330 i++;
331 }
332 else
333 valid = 0;
334 break;
335
336 }
337
338 case CommandTimeout: // Time to run viewfinder for before taking picture, in seconds
339 {
340 if (sscanf(argv[i + 1], "%d", &state->timeout) == 1)
341 {
342 // Ensure that if previously selected CommandKeypress we don't overwrite it
343 if (state->timeout == 0 && state->frameNextMethod == FRAME_NEXT_SINGLE)
344 state->frameNextMethod = FRAME_NEXT_FOREVER;
345
346 i++;
347 }
348 else
349 valid = 0;
350 break;
351 }
352
353 case CommandTimelapse:
354 if (sscanf(argv[i + 1], "%u", &state->timelapse) != 1)
355 valid = 0;
356 else
357 {
358 if (state->timelapse)
359 state->frameNextMethod = FRAME_NEXT_TIMELAPSE;
360 else
361 state->frameNextMethod = FRAME_NEXT_IMMEDIATELY;
362
363 i++;
364 }
365 break;
366
367 case CommandUseRGB: // display lots of data during run
368 if (state->onlyLuma)
369 {
370 fprintf(stderr, "--luma and --rgb/--bgr are mutually exclusive\n");
371 valid = 0;
372 }
373 state->encoding = MMAL_ENCODING_RGB24;
374 break;
375
376 case CommandFullResPreview:
377 state->fullResPreview = 1;
378 break;
379
380 case CommandKeypress: // Set keypress between capture mode
381 state->frameNextMethod = FRAME_NEXT_KEYPRESS;
382
383 if (state->timeout == -1)
384 state->timeout = 0;
385
386 break;
387
388 case CommandSignal: // Set SIGUSR1 between capture mode
389 state->frameNextMethod = FRAME_NEXT_SIGNAL;
390 // Reenable the signal
391 signal(SIGUSR1, default_signal_handler);
392
393 if (state->timeout == -1)
394 state->timeout = 0;
395
396 break;
397
398 case CommandBurstMode:
399 state->burstCaptureMode=1;
400 break;
401
402 case CommandOnlyLuma:
403 if (state->encoding)
404 {
405 fprintf(stderr, "--luma and --rgb are mutually exclusive\n");
406 valid = 0;
407 }
408 state->onlyLuma = 1;
409 break;
410
411 case CommandUseBGR:
412 if (state->onlyLuma)
413 {
414 fprintf(stderr, "--luma and --rgb/--bgr are mutually exclusive\n");
415 valid = 0;
416 }
417 state->encoding = MMAL_ENCODING_BGR24;
418 break;
419
420 default:
421 {
422 // Try parsing for any image specific parameters
423 // result indicates how many parameters were used up, 0,1,2
424 // but we adjust by -1 as we have used one already
425 const char *second_arg = (i + 1 < argc) ? argv[i + 1] : NULL;
426
427 int parms_used = (raspicamcontrol_parse_cmdline(&state->camera_parameters, &argv[i][1], second_arg));
428
429 // Still unused, try common settings
430 if (!parms_used)
431 parms_used = raspicommonsettings_parse_cmdline(&state->common_settings, &argv[i][1], second_arg, &application_help_message);
432
433 // Still unused, try preview options
434 if (!parms_used)
435 parms_used = raspipreview_parse_cmdline(&state->preview_parameters, &argv[i][1], second_arg);
436
437 // If no parms were used, this must be a bad parameters
438 if (!parms_used)
439 valid = 0;
440 else
441 i += parms_used - 1;
442
443 break;
444 }
445 }
446 }
447
448 if (!valid)
449 {
450 fprintf(stderr, "Invalid command line option (%s)\n", argv[i-1]);
451 return 1;
452 }
453
454 return 0;
455 }
456
457 /**
458 * buffer header callback function for camera output port
459 *
460 * Callback will dump buffer data to the specific file
461 *
462 * @param port Pointer to port from which callback originated
463 * @param buffer mmal buffer header pointer
464 */
camera_buffer_callback(MMAL_PORT_T * port,MMAL_BUFFER_HEADER_T * buffer)465 static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
466 {
467 int complete = 0;
468 // We pass our file handle and other stuff in via the userdata field.
469
470 PORT_USERDATA *pData = (PORT_USERDATA *)port->userdata;
471
472 if (pData)
473 {
474 int bytes_written = 0;
475 int bytes_to_write = buffer->length;
476
477 if (pData->pstate->onlyLuma)
478 bytes_to_write = vcos_min(buffer->length, port->format->es->video.width * port->format->es->video.height);
479
480 if (bytes_to_write && pData->file_handle)
481 {
482 mmal_buffer_header_mem_lock(buffer);
483
484 bytes_written = fwrite(buffer->data, 1, bytes_to_write, pData->file_handle);
485
486 mmal_buffer_header_mem_unlock(buffer);
487 }
488
489 // We need to check we wrote what we wanted - it's possible we have run out of storage.
490 if (buffer->length && bytes_written != bytes_to_write)
491 {
492 vcos_log_error("Unable to write buffer to file - aborting %d vs %d", bytes_written, bytes_to_write);
493 complete = 1;
494 }
495
496 // Check end of frame or error
497 if (buffer->flags & (MMAL_BUFFER_HEADER_FLAG_FRAME_END | MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED))
498 complete = 1;
499 }
500 else
501 {
502 vcos_log_error("Received a camera still buffer callback with no state");
503 }
504
505 // release buffer back to the pool
506 mmal_buffer_header_release(buffer);
507
508 // and send one back to the port (if still open)
509 if (port->is_enabled)
510 {
511 MMAL_STATUS_T status;
512 MMAL_BUFFER_HEADER_T *new_buffer = mmal_queue_get(pData->pstate->camera_pool->queue);
513
514 // and back to the port from there.
515 if (new_buffer)
516 {
517 status = mmal_port_send_buffer(port, new_buffer);
518 }
519
520 if (!new_buffer || status != MMAL_SUCCESS)
521 vcos_log_error("Unable to return the buffer to the camera still port");
522 }
523
524 if (complete)
525 {
526 vcos_semaphore_post(&(pData->complete_semaphore));
527 }
528 }
529
530 /**
531 * Create the camera component, set up its ports
532 *
533 * @param state Pointer to state control struct
534 *
535 * @return 0 if failed, pointer to component if successful
536 *
537 */
create_camera_component(RASPISTILLYUV_STATE * state)538 static MMAL_STATUS_T create_camera_component(RASPISTILLYUV_STATE *state)
539 {
540 MMAL_COMPONENT_T *camera = 0;
541 MMAL_ES_FORMAT_T *format;
542 MMAL_PORT_T *preview_port = NULL, *video_port = NULL, *still_port = NULL;
543 MMAL_STATUS_T status;
544 MMAL_POOL_T *pool;
545
546 /* Create the component */
547 status = mmal_component_create(MMAL_COMPONENT_DEFAULT_CAMERA, &camera);
548
549 if (status != MMAL_SUCCESS)
550 {
551 vcos_log_error("Failed to create camera component");
552 goto error;
553 }
554
555 MMAL_PARAMETER_INT32_T camera_num =
556 {{MMAL_PARAMETER_CAMERA_NUM, sizeof(camera_num)}, state->common_settings.cameraNum};
557
558 status = mmal_port_parameter_set(camera->control, &camera_num.hdr);
559
560 if (status != MMAL_SUCCESS)
561 {
562 vcos_log_error("Could not select camera : error %d", status);
563 goto error;
564 }
565
566 if (!camera->output_num)
567 {
568 status = MMAL_ENOSYS;
569 vcos_log_error("Camera doesn't have output ports");
570 goto error;
571 }
572
573 status = mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, state->common_settings.sensor_mode);
574
575 if (status != MMAL_SUCCESS)
576 {
577 vcos_log_error("Could not set sensor mode : error %d", status);
578 goto error;
579 }
580
581 preview_port = camera->output[MMAL_CAMERA_PREVIEW_PORT];
582 video_port = camera->output[MMAL_CAMERA_VIDEO_PORT];
583 still_port = camera->output[MMAL_CAMERA_CAPTURE_PORT];
584
585 // Enable the camera, and tell it its control callback function
586 status = mmal_port_enable(camera->control, default_camera_control_callback);
587
588 if (status != MMAL_SUCCESS )
589 {
590 vcos_log_error("Unable to enable control port : error %d", status);
591 goto error;
592 }
593
594 // set up the camera configuration
595 {
596 MMAL_PARAMETER_CAMERA_CONFIG_T cam_config =
597 {
598 { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) },
599 .max_stills_w = state->common_settings.width,
600 .max_stills_h = state->common_settings.height,
601 .stills_yuv422 = 0,
602 .one_shot_stills = 1,
603 .max_preview_video_w = state->preview_parameters.previewWindow.width,
604 .max_preview_video_h = state->preview_parameters.previewWindow.height,
605 .num_preview_video_frames = 3,
606 .stills_capture_circular_buffer_height = 0,
607 .fast_preview_resume = 0,
608 .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC
609 };
610
611 if (state->fullResPreview)
612 {
613 cam_config.max_preview_video_w = state->common_settings.width;
614 cam_config.max_preview_video_h = state->common_settings.height;
615 }
616
617 mmal_port_parameter_set(camera->control, &cam_config.hdr);
618 }
619
620 raspicamcontrol_set_all_parameters(camera, &state->camera_parameters);
621
622 // Now set up the port formats
623
624 format = preview_port->format;
625
626 format->encoding = MMAL_ENCODING_OPAQUE;
627 format->encoding_variant = MMAL_ENCODING_I420;
628
629 if(state->camera_parameters.shutter_speed > 6000000)
630 {
631 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
632 { 50, 1000 }, {166, 1000}
633 };
634 mmal_port_parameter_set(preview_port, &fps_range.hdr);
635 }
636 else if(state->camera_parameters.shutter_speed > 1000000)
637 {
638 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
639 { 166, 1000 }, {999, 1000}
640 };
641 mmal_port_parameter_set(preview_port, &fps_range.hdr);
642 }
643 if (state->fullResPreview)
644 {
645 // In this mode we are forcing the preview to be generated from the full capture resolution.
646 // This runs at a max of 15fps with the OV5647 sensor.
647 format->es->video.width = VCOS_ALIGN_UP(state->common_settings.width, 32);
648 format->es->video.height = VCOS_ALIGN_UP(state->common_settings.height, 16);
649 format->es->video.crop.x = 0;
650 format->es->video.crop.y = 0;
651 format->es->video.crop.width = state->common_settings.width;
652 format->es->video.crop.height = state->common_settings.height;
653 format->es->video.frame_rate.num = FULL_RES_PREVIEW_FRAME_RATE_NUM;
654 format->es->video.frame_rate.den = FULL_RES_PREVIEW_FRAME_RATE_DEN;
655 }
656 else
657 {
658 // Use a full FOV 4:3 mode
659 format->es->video.width = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.width, 32);
660 format->es->video.height = VCOS_ALIGN_UP(state->preview_parameters.previewWindow.height, 16);
661 format->es->video.crop.x = 0;
662 format->es->video.crop.y = 0;
663 format->es->video.crop.width = state->preview_parameters.previewWindow.width;
664 format->es->video.crop.height = state->preview_parameters.previewWindow.height;
665 format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM;
666 format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN;
667 }
668
669 status = mmal_port_format_commit(preview_port);
670
671 if (status != MMAL_SUCCESS )
672 {
673 vcos_log_error("camera viewfinder format couldn't be set");
674 goto error;
675 }
676
677 // Set the same format on the video port (which we don't use here)
678 mmal_format_full_copy(video_port->format, format);
679 status = mmal_port_format_commit(video_port);
680
681 if (status != MMAL_SUCCESS )
682 {
683 vcos_log_error("camera video format couldn't be set");
684 goto error;
685 }
686
687 // Ensure there are enough buffers to avoid dropping frames
688 if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM)
689 video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM;
690
691 format = still_port->format;
692
693 if(state->camera_parameters.shutter_speed > 6000000)
694 {
695 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
696 { 50, 1000 }, {166, 1000}
697 };
698 mmal_port_parameter_set(still_port, &fps_range.hdr);
699 }
700 else if(state->camera_parameters.shutter_speed > 1000000)
701 {
702 MMAL_PARAMETER_FPS_RANGE_T fps_range = {{MMAL_PARAMETER_FPS_RANGE, sizeof(fps_range)},
703 { 167, 1000 }, {999, 1000}
704 };
705 mmal_port_parameter_set(still_port, &fps_range.hdr);
706 }
707 // Set our stills format on the stills port
708 if (state->encoding)
709 {
710 format->encoding = state->encoding;
711 if (!mmal_util_rgb_order_fixed(still_port))
712 {
713 if (format->encoding == MMAL_ENCODING_RGB24)
714 format->encoding = MMAL_ENCODING_BGR24;
715 else if (format->encoding == MMAL_ENCODING_BGR24)
716 format->encoding = MMAL_ENCODING_RGB24;
717 }
718 format->encoding_variant = 0; //Irrelevant when not in opaque mode
719 }
720 else
721 {
722 format->encoding = MMAL_ENCODING_I420;
723 format->encoding_variant = MMAL_ENCODING_I420;
724 }
725 format->es->video.width = VCOS_ALIGN_UP(state->common_settings.width, 32);
726 format->es->video.height = VCOS_ALIGN_UP(state->common_settings.height, 16);
727 format->es->video.crop.x = 0;
728 format->es->video.crop.y = 0;
729 format->es->video.crop.width = state->common_settings.width;
730 format->es->video.crop.height = state->common_settings.height;
731 format->es->video.frame_rate.num = STILLS_FRAME_RATE_NUM;
732 format->es->video.frame_rate.den = STILLS_FRAME_RATE_DEN;
733
734 if (still_port->buffer_size < still_port->buffer_size_min)
735 still_port->buffer_size = still_port->buffer_size_min;
736
737 still_port->buffer_num = still_port->buffer_num_recommended;
738
739 status = mmal_port_parameter_set_boolean(video_port, MMAL_PARAMETER_ZERO_COPY, MMAL_TRUE);
740 if (status != MMAL_SUCCESS)
741 {
742 vcos_log_error("Failed to select zero copy");
743 goto error;
744 }
745
746 status = mmal_port_format_commit(still_port);
747
748 if (status != MMAL_SUCCESS )
749 {
750 vcos_log_error("camera still format couldn't be set");
751 goto error;
752 }
753
754 /* Enable component */
755 status = mmal_component_enable(camera);
756
757 if (status != MMAL_SUCCESS )
758 {
759 vcos_log_error("camera component couldn't be enabled");
760 goto error;
761 }
762
763 /* Create pool of buffer headers for the output port to consume */
764 pool = mmal_port_pool_create(still_port, still_port->buffer_num, still_port->buffer_size);
765
766 if (!pool)
767 {
768 vcos_log_error("Failed to create buffer header pool for camera still port %s", still_port->name);
769 }
770
771 state->camera_pool = pool;
772 state->camera_component = camera;
773
774 if (state->common_settings.verbose)
775 fprintf(stderr, "Camera component done\n");
776
777 return status;
778
779 error:
780
781 if (camera)
782 mmal_component_destroy(camera);
783
784 return status;
785 }
786
787 /**
788 * Destroy the camera component
789 *
790 * @param state Pointer to state control struct
791 *
792 */
destroy_camera_component(RASPISTILLYUV_STATE * state)793 static void destroy_camera_component(RASPISTILLYUV_STATE *state)
794 {
795 if (state->camera_component)
796 {
797 mmal_component_destroy(state->camera_component);
798 state->camera_component = NULL;
799 }
800 }
801
802 /**
803 * Allocates and generates a filename based on the
804 * user-supplied pattern and the frame number.
805 * On successful return, finalName and tempName point to malloc()ed strings
806 * which must be freed externally. (On failure, returns nulls that
807 * don't need free()ing.)
808 *
809 * @param finalName pointer receives an
810 * @param pattern sprintf pattern with %d to be replaced by frame
811 * @param frame for timelapse, the frame number
812 * @return Returns a MMAL_STATUS_T giving result of operation
813 */
814
create_filenames(char ** finalName,char ** tempName,char * pattern,int frame)815 MMAL_STATUS_T create_filenames(char** finalName, char** tempName, char * pattern, int frame)
816 {
817 *finalName = NULL;
818 *tempName = NULL;
819 if (0 > asprintf(finalName, pattern, frame) ||
820 0 > asprintf(tempName, "%s~", *finalName))
821 {
822 if (*finalName != NULL)
823 {
824 free(*finalName);
825 }
826 return MMAL_ENOMEM; // It may be some other error, but it is not worth getting it right
827 }
828 return MMAL_SUCCESS;
829 }
830
831 /**
832 * Function to wait in various ways (depending on settings) for the next frame
833 *
834 * @param state Pointer to the state data
835 * @param [in][out] frame The last frame number, adjusted to next frame number on output
836 * @return !0 if to continue, 0 if reached end of run
837 */
wait_for_next_frame(RASPISTILLYUV_STATE * state,int * frame)838 static int wait_for_next_frame(RASPISTILLYUV_STATE *state, int *frame)
839 {
840 static int64_t complete_time = -1;
841 int keep_running = 1;
842
843 int64_t current_time = get_microseconds64()/1000;
844
845 if (complete_time == -1)
846 complete_time = current_time + state->timeout;
847
848 // if we have run out of time, flag we need to exit
849 // If timeout = 0 then always continue
850 if (current_time >= complete_time && state->timeout != 0)
851 keep_running = 0;
852
853 switch (state->frameNextMethod)
854 {
855 case FRAME_NEXT_SINGLE :
856 // simple timeout for a single capture
857 vcos_sleep(state->timeout);
858 return 0;
859
860 case FRAME_NEXT_FOREVER :
861 {
862 *frame+=1;
863
864 // Have a sleep so we don't hog the CPU.
865 vcos_sleep(10000);
866
867 // Run forever so never indicate end of loop
868 return 1;
869 }
870
871 case FRAME_NEXT_TIMELAPSE :
872 {
873 static int64_t next_frame_ms = -1;
874
875 // Always need to increment by at least one, may add a skip later
876 *frame += 1;
877
878 if (next_frame_ms == -1)
879 {
880 vcos_sleep(CAMERA_SETTLE_TIME);
881
882 // Update our current time after the sleep
883 current_time = get_microseconds64()/1000;
884
885 // Set our initial 'next frame time'
886 next_frame_ms = current_time + state->timelapse;
887 }
888 else
889 {
890 int64_t this_delay_ms = next_frame_ms - current_time;
891
892 if (this_delay_ms < 0)
893 {
894 // We are already past the next exposure time
895 if (-this_delay_ms < -state->timelapse/2)
896 {
897 // Less than a half frame late, take a frame and hope to catch up next time
898 next_frame_ms += state->timelapse;
899 vcos_log_error("Frame %d is %d ms late", *frame, (int)(-this_delay_ms));
900 }
901 else
902 {
903 int nskip = 1 + (-this_delay_ms)/state->timelapse;
904 vcos_log_error("Skipping frame %d to restart at frame %d", *frame, *frame+nskip);
905 *frame += nskip;
906 this_delay_ms += nskip * state->timelapse;
907 vcos_sleep(this_delay_ms);
908 next_frame_ms += (nskip + 1) * state->timelapse;
909 }
910 }
911 else
912 {
913 vcos_sleep(this_delay_ms);
914 next_frame_ms += state->timelapse;
915 }
916 }
917
918 return keep_running;
919 }
920
921 case FRAME_NEXT_KEYPRESS :
922 {
923 int ch;
924
925 if (state->common_settings.verbose)
926 fprintf(stderr, "Press Enter to capture, X then ENTER to exit\n");
927
928 ch = getchar();
929 *frame+=1;
930 if (ch == 'x' || ch == 'X')
931 return 0;
932 else
933 {
934 return keep_running;
935 }
936 }
937
938 case FRAME_NEXT_IMMEDIATELY :
939 {
940 // Not waiting, just go to next frame.
941 // Actually, we do need a slight delay here otherwise exposure goes
942 // badly wrong since we never allow it frames to work it out
943 // This could probably be tuned down.
944 // First frame has a much longer delay to ensure we get exposure to a steady state
945 if (*frame == 0)
946 vcos_sleep(CAMERA_SETTLE_TIME);
947 else
948 vcos_sleep(30);
949
950 *frame+=1;
951
952 return keep_running;
953 }
954
955 case FRAME_NEXT_GPIO :
956 {
957 // Intended for GPIO firing of a capture
958 return 0;
959 }
960
961 case FRAME_NEXT_SIGNAL :
962 {
963 // Need to wait for a SIGUSR1 signal
964 sigset_t waitset;
965 int sig;
966 int result = 0;
967
968 sigemptyset( &waitset );
969 sigaddset( &waitset, SIGUSR1 );
970
971 // We are multi threaded because we use mmal, so need to use the pthread
972 // variant of procmask to block SIGUSR1 so we can wait on it.
973 pthread_sigmask( SIG_BLOCK, &waitset, NULL );
974
975 if (state->common_settings.verbose)
976 {
977 fprintf(stderr, "Waiting for SIGUSR1 to initiate capture\n");
978 }
979
980 result = sigwait( &waitset, &sig );
981
982 if (state->common_settings.verbose)
983 {
984 if( result == 0)
985 {
986 fprintf(stderr, "Received SIGUSR1\n");
987 }
988 else
989 {
990 fprintf(stderr, "Bad signal received - error %d\n", errno);
991 }
992 }
993
994 *frame+=1;
995
996 return keep_running;
997 }
998 } // end of switch
999
1000 // Should have returned by now, but default to timeout
1001 return keep_running;
1002 }
1003
rename_file(RASPISTILLYUV_STATE * state,FILE * output_file,const char * final_filename,const char * use_filename,int frame)1004 static void rename_file(RASPISTILLYUV_STATE *state, FILE *output_file,
1005 const char *final_filename, const char *use_filename, int frame)
1006 {
1007 MMAL_STATUS_T status;
1008
1009 fclose(output_file);
1010 vcos_assert(use_filename != NULL && final_filename != NULL);
1011 if (0 != rename(use_filename, final_filename))
1012 {
1013 vcos_log_error("Could not rename temp file to: %s; %s",
1014 final_filename,strerror(errno));
1015 }
1016 if (state->linkname)
1017 {
1018 char *use_link;
1019 char *final_link;
1020 status = create_filenames(&final_link, &use_link, state->linkname, frame);
1021
1022 // Create hard link if possible, symlink otherwise
1023 if (status != MMAL_SUCCESS
1024 || (0 != link(final_filename, use_link)
1025 && 0 != symlink(final_filename, use_link))
1026 || 0 != rename(use_link, final_link))
1027 {
1028 vcos_log_error("Could not link as filename: %s; %s",
1029 state->linkname,strerror(errno));
1030 }
1031 if (use_link) free(use_link);
1032 if (final_link) free(final_link);
1033 }
1034 }
1035
1036 /**
1037 * main
1038 */
main(int argc,const char ** argv)1039 int main(int argc, const char **argv)
1040 {
1041 // Our main data storage vessel..
1042 RASPISTILLYUV_STATE state;
1043 int exit_code = EX_OK;
1044
1045 MMAL_STATUS_T status = MMAL_SUCCESS;
1046 MMAL_PORT_T *camera_preview_port = NULL;
1047 MMAL_PORT_T *camera_video_port = NULL;
1048 MMAL_PORT_T *camera_still_port = NULL;
1049 MMAL_PORT_T *preview_input_port = NULL;
1050 FILE *output_file = NULL;
1051
1052 bcm_host_init();
1053
1054 // Register our application with the logging system
1055 vcos_log_register("RaspiStill", VCOS_LOG_CATEGORY);
1056
1057 signal(SIGINT, default_signal_handler);
1058
1059 // Disable USR1 for the moment - may be reenabled if go in to signal capture mode
1060 signal(SIGUSR1, SIG_IGN);
1061
1062 set_app_name(argv[0]);
1063
1064 // Do we have any parameters
1065 if (argc == 1)
1066 {
1067 display_valid_parameters(basename(argv[0]), &application_help_message);
1068 exit(EX_USAGE);
1069 }
1070
1071 default_status(&state);
1072
1073 // Parse the command line and put options in to our status structure
1074 if (parse_cmdline(argc, argv, &state))
1075 {
1076 status = -1;
1077 exit(EX_USAGE);
1078 }
1079
1080 if (state.timeout == -1)
1081 state.timeout = 5000;
1082
1083 if (state.common_settings.gps)
1084 if (raspi_gps_setup(state.common_settings.verbose))
1085 state.common_settings.gps = false;
1086
1087 // Setup for sensor specific parameters
1088 get_sensor_defaults(state.common_settings.cameraNum, state.common_settings.camera_name,
1089 &state.common_settings.width, &state.common_settings.height);
1090
1091 if (state.common_settings.verbose)
1092 {
1093 print_app_details(stderr);
1094 dump_status(&state);
1095 }
1096
1097 // OK, we have a nice set of parameters. Now set up our components
1098 // We have two components. Camera and Preview
1099 // Camera is different in stills/video, but preview
1100 // is the same so handed off to a separate module
1101
1102 if ((status = create_camera_component(&state)) != MMAL_SUCCESS)
1103 {
1104 vcos_log_error("%s: Failed to create camera component", __func__);
1105 exit_code = EX_SOFTWARE;
1106 }
1107 else if ((status = raspipreview_create(&state.preview_parameters)) != MMAL_SUCCESS)
1108 {
1109 vcos_log_error("%s: Failed to create preview component", __func__);
1110 destroy_camera_component(&state);
1111 exit_code = EX_SOFTWARE;
1112 }
1113 else
1114 {
1115 PORT_USERDATA callback_data;
1116
1117 if (state.common_settings.verbose)
1118 fprintf(stderr, "Starting component connection stage\n");
1119
1120 camera_preview_port = state.camera_component->output[MMAL_CAMERA_PREVIEW_PORT];
1121 camera_video_port = state.camera_component->output[MMAL_CAMERA_VIDEO_PORT];
1122 camera_still_port = state.camera_component->output[MMAL_CAMERA_CAPTURE_PORT];
1123
1124 // Note we are lucky that the preview and null sink components use the same input port
1125 // so we can simple do this without conditionals
1126 preview_input_port = state.preview_parameters.preview_component->input[0];
1127
1128 // Connect camera to preview (which might be a null_sink if no preview required)
1129 status = connect_ports(camera_preview_port, preview_input_port, &state.preview_connection);
1130
1131 if (status == MMAL_SUCCESS)
1132 {
1133 VCOS_STATUS_T vcos_status;
1134
1135 // Set up our userdata - this is passed though to the callback where we need the information.
1136 // Null until we open our filename
1137 callback_data.file_handle = NULL;
1138 callback_data.pstate = &state;
1139
1140 vcos_status = vcos_semaphore_create(&callback_data.complete_semaphore, "RaspiStill-sem", 0);
1141 vcos_assert(vcos_status == VCOS_SUCCESS);
1142
1143 camera_still_port->userdata = (struct MMAL_PORT_USERDATA_T *)&callback_data;
1144
1145 if (state.common_settings.verbose)
1146 fprintf(stderr, "Enabling camera still output port\n");
1147
1148 // Enable the camera still output port and tell it its callback function
1149 status = mmal_port_enable(camera_still_port, camera_buffer_callback);
1150
1151 if (status != MMAL_SUCCESS)
1152 {
1153 vcos_log_error("Failed to setup camera output");
1154 goto error;
1155 }
1156
1157 if (state.common_settings.verbose)
1158 fprintf(stderr, "Starting video preview\n");
1159
1160 int frame, keep_looping = 1;
1161 FILE *output_file = NULL;
1162 char *use_filename = NULL; // Temporary filename while image being written
1163 char *final_filename = NULL; // Name that file gets once writing complete
1164
1165 frame = 0;
1166
1167 while (keep_looping)
1168 {
1169 keep_looping = wait_for_next_frame(&state, &frame);
1170
1171 // Open the file
1172 if (state.common_settings.filename)
1173 {
1174 if (state.common_settings.filename[0] == '-')
1175 {
1176 output_file = stdout;
1177
1178 // Ensure we don't upset the output stream with diagnostics/info
1179 state.common_settings.verbose = 0;
1180 }
1181 else
1182 {
1183 vcos_assert(use_filename == NULL && final_filename == NULL);
1184 status = create_filenames(&final_filename, &use_filename, state.common_settings.filename, frame);
1185 if (status != MMAL_SUCCESS)
1186 {
1187 vcos_log_error("Unable to create filenames");
1188 goto error;
1189 }
1190
1191 if (state.common_settings.verbose)
1192 fprintf(stderr, "Opening output file %s\n", final_filename);
1193 // Technically it is opening the temp~ filename which will be ranamed to the final filename
1194
1195 output_file = fopen(use_filename, "wb");
1196
1197 if (!output_file)
1198 {
1199 // Notify user, carry on but discarding encoded output buffers
1200 vcos_log_error("%s: Error opening output file: %s\nNo output file will be generated\n", __func__, use_filename);
1201 }
1202 }
1203
1204 callback_data.file_handle = output_file;
1205 }
1206
1207 if (output_file)
1208 {
1209 int num, q;
1210
1211 // There is a possibility that shutter needs to be set each loop.
1212 if (mmal_status_to_int(mmal_port_parameter_set_uint32(state.camera_component->control, MMAL_PARAMETER_SHUTTER_SPEED, state.camera_parameters.shutter_speed) != MMAL_SUCCESS))
1213 vcos_log_error("Unable to set shutter speed");
1214
1215 // Send all the buffers to the camera output port
1216 num = mmal_queue_length(state.camera_pool->queue);
1217
1218 for (q=0; q<num; q++)
1219 {
1220 MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(state.camera_pool->queue);
1221
1222 if (!buffer)
1223 vcos_log_error("Unable to get a required buffer %d from pool queue", q);
1224
1225 if (mmal_port_send_buffer(camera_still_port, buffer)!= MMAL_SUCCESS)
1226 vcos_log_error("Unable to send a buffer to camera output port (%d)", q);
1227 }
1228
1229 if (state.burstCaptureMode && frame==1)
1230 {
1231 mmal_port_parameter_set_boolean(state.camera_component->control, MMAL_PARAMETER_CAMERA_BURST_CAPTURE, 1);
1232 }
1233
1234 if(state.camera_parameters.enable_annotate)
1235 {
1236 if ((state.camera_parameters.enable_annotate & ANNOTATE_APP_TEXT) && state.common_settings.gps)
1237 {
1238 char *text = raspi_gps_location_string();
1239 raspicamcontrol_set_annotate(state.camera_component, state.camera_parameters.enable_annotate,
1240 text,
1241 state.camera_parameters.annotate_text_size,
1242 state.camera_parameters.annotate_text_colour,
1243 state.camera_parameters.annotate_bg_colour,
1244 state.camera_parameters.annotate_justify,
1245 state.camera_parameters.annotate_x,
1246 state.camera_parameters.annotate_y
1247 );
1248 free(text);
1249 }
1250 else
1251 raspicamcontrol_set_annotate(state.camera_component, state.camera_parameters.enable_annotate,
1252 state.camera_parameters.annotate_string,
1253 state.camera_parameters.annotate_text_size,
1254 state.camera_parameters.annotate_text_colour,
1255 state.camera_parameters.annotate_bg_colour,
1256 state.camera_parameters.annotate_justify,
1257 state.camera_parameters.annotate_x,
1258 state.camera_parameters.annotate_y
1259 );
1260 }
1261
1262 if (state.common_settings.verbose)
1263 fprintf(stderr, "Starting capture %d\n", frame);
1264
1265 if (mmal_port_parameter_set_boolean(camera_still_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS)
1266 {
1267 vcos_log_error("%s: Failed to start capture", __func__);
1268 }
1269 else
1270 {
1271 // Wait for capture to complete
1272 // For some reason using vcos_semaphore_wait_timeout sometimes returns immediately with bad parameter error
1273 // even though it appears to be all correct, so reverting to untimed one until figure out why its erratic
1274 vcos_semaphore_wait(&callback_data.complete_semaphore);
1275 if (state.common_settings.verbose)
1276 fprintf(stderr, "Finished capture %d\n", frame);
1277 }
1278
1279 // Ensure we don't die if get callback with no open file
1280 callback_data.file_handle = NULL;
1281
1282 if (output_file != stdout)
1283 {
1284 rename_file(&state, output_file, final_filename, use_filename, frame);
1285 }
1286 else
1287 {
1288 fflush(output_file);
1289 }
1290 }
1291
1292 if (use_filename)
1293 {
1294 free(use_filename);
1295 use_filename = NULL;
1296 }
1297 if (final_filename)
1298 {
1299 free(final_filename);
1300 final_filename = NULL;
1301 }
1302 } // end for (frame)
1303
1304 vcos_semaphore_delete(&callback_data.complete_semaphore);
1305 }
1306 else
1307 {
1308 mmal_status_to_int(status);
1309 vcos_log_error("%s: Failed to connect camera to preview", __func__);
1310 }
1311
1312 error:
1313
1314 mmal_status_to_int(status);
1315
1316 if (state.common_settings.verbose)
1317 fprintf(stderr, "Closing down\n");
1318
1319 if (output_file)
1320 fclose(output_file);
1321
1322 // Disable all our ports that are not handled by connections
1323 check_disable_port(camera_video_port);
1324
1325 if (state.preview_connection)
1326 mmal_connection_destroy(state.preview_connection);
1327
1328 /* Disable components */
1329 if (state.preview_parameters.preview_component)
1330 mmal_component_disable(state.preview_parameters.preview_component);
1331
1332 if (state.camera_component)
1333 mmal_component_disable(state.camera_component);
1334
1335 raspipreview_destroy(&state.preview_parameters);
1336 destroy_camera_component(&state);
1337
1338 if (state.common_settings.gps)
1339 raspi_gps_shutdown(state.common_settings.verbose);
1340
1341 if (state.common_settings.verbose)
1342 fprintf(stderr, "Close down completed, all components disconnected, disabled and destroyed\n\n");
1343 }
1344
1345 if (status != MMAL_SUCCESS)
1346 raspicamcontrol_check_configuration(128);
1347
1348 return exit_code;
1349 }
1350
1351