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