1 /*
2 Copyright (c) 2018, Raspberry Pi (Trading) Ltd.
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above copyright
10       notice, this list of conditions and the following disclaimer in the
11       documentation and/or other materials provided with the distribution.
12     * Neither the name of the copyright holder nor the
13       names of its contributors may be used to endorse or promote products
14       derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
28 /**
29  * \file RaspiCommonSettings.c
30  *
31  * Description
32  *
33  * Handles general settings applicable to all the camera applications
34  */
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <string.h>
40 #include <memory.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <sysexits.h>
44 
45 #include "interface/vcos/vcos.h"
46 #include "interface/mmal/mmal.h"
47 #include "interface/mmal/mmal_logging.h"
48 #include "interface/mmal/mmal_buffer.h"
49 #include "interface/mmal/util/mmal_util.h"
50 #include "interface/mmal/util/mmal_util_params.h"
51 #include "interface/mmal/util/mmal_default_components.h"
52 #include "interface/mmal/util/mmal_connection.h"
53 #include "interface/mmal/mmal_parameters_camera.h"
54 
55 #include "RaspiCommonSettings.h"
56 #include "RaspiCLI.h"
57 #include "RaspiHelpers.h"
58 #include "RaspiGPS.h"
59 
60 enum
61 {
62    CommandHelp,
63    CommandWidth,
64    CommandHeight,
65    CommandOutput,
66    CommandVerbose,
67    CommandCamSelect,
68    CommandSensorMode,
69    CommandGpsd,
70 };
71 
72 static COMMAND_LIST cmdline_commands[] =
73 {
74    { CommandHelp,    "-help",       "?",  "This help information", 0 },
75    { CommandWidth,   "-width",      "w",  "Set image width <size>", 1 },
76    { CommandHeight,  "-height",     "h",  "Set image height <size>", 1 },
77    { CommandOutput,  "-output",     "o",  "Output filename <filename> (to write to stdout, use '-o -'). If not specified, no file is saved", 1 },
78    { CommandVerbose, "-verbose",    "v",  "Output verbose information during run", 0 },
79    { CommandCamSelect, "-camselect","cs", "Select camera <number>. Default 0", 1 },
80    { CommandSensorMode,"-mode",     "md", "Force sensor mode. 0=auto. See docs for other modes available", 1},
81    { CommandGpsd,    "-gpsdexif",   "gps","Apply real-time GPS information to output (e.g. EXIF in JPG, annotation in video (requires " LIBGPS_SO_VERSION ")", 0},
82 };
83 
84 static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]);
85 
raspicommonsettings_set_defaults(RASPICOMMONSETTINGS_PARAMETERS * state)86 void raspicommonsettings_set_defaults(RASPICOMMONSETTINGS_PARAMETERS *state)
87 {
88    strncpy(state->camera_name, "(Unknown)", MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN);
89    // We dont set width and height since these will be specific to the app being built.
90    state->width = 0;
91    state->height = 0;
92    state->filename = NULL;
93    state->verbose = 0;
94    state->cameraNum = 0;
95    state->sensor_mode = 0;
96    state->gps = 0;
97 };
98 
99 /**
100  * Dump parameters as human readable to stderr
101  *
102  * @param state Pointer to parameter block
103  *
104  */
raspicommonsettings_dump_parameters(RASPICOMMONSETTINGS_PARAMETERS * state)105 void raspicommonsettings_dump_parameters(RASPICOMMONSETTINGS_PARAMETERS *state)
106 {
107    fprintf(stderr, "Camera Name %s\n", state->camera_name);
108    fprintf(stderr, "Width %d, Height %d, filename %s\n", state->width,
109            state->height, state->filename);
110    fprintf(stderr, "Using camera %d, sensor mode %d\n\n", state->cameraNum, state->sensor_mode);
111    fprintf(stderr, "GPS output %s\n\n", state->gps ? "Enabled" : "Disabled");
112 };
113 
114 /**
115  * Display help for command line options for this module
116  */
raspicommonsettings_display_help()117 void raspicommonsettings_display_help()
118 {
119    fprintf(stdout, "\nCommon Settings commands\n\n");
120    raspicli_display_help(cmdline_commands, cmdline_commands_size);
121 }
122 
123 /**
124  * Parse a possible command pair - command and parameter
125  * @param arg1 Command
126  * @param arg2 Parameter (could be NULL)
127  * @return How many parameters were used, 0,1,2
128  */
raspicommonsettings_parse_cmdline(RASPICOMMONSETTINGS_PARAMETERS * state,const char * arg1,const char * arg2,void (* app_help)(char *))129 int raspicommonsettings_parse_cmdline(RASPICOMMONSETTINGS_PARAMETERS *state, const char *arg1, const char *arg2, void (*app_help)(char*))
130 {
131    int command_id, used = 0, num_parameters;
132 
133    if (!arg1)
134       return 0;
135 
136    command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters);
137 
138    // If invalid command, or we are missing a parameter, drop out
139    if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL))
140       return 0;
141 
142    switch (command_id)
143    {
144    case CommandHelp:
145    {
146       display_valid_parameters(basename(get_app_name()), app_help);
147       exit(0);
148       break;
149    }
150    case CommandWidth: // Width > 0
151       if (sscanf(arg2, "%u", &state->width) == 1)
152          used = 2;
153       break;
154 
155    case CommandHeight: // Height > 0
156       if (sscanf(arg2, "%u", &state->height) == 1)
157          used = 2;
158       break;
159 
160    case CommandOutput:  // output filename
161    {
162       int len = strlen(arg2);
163       if (len)
164       {
165          // Ensure that any %<char> is either %% or %d.
166          const char *percent = arg2;
167 
168          while(*percent && (percent=strchr(percent, '%')) != NULL)
169          {
170             int digits=0;
171             percent++;
172             while(isdigit(*percent))
173             {
174                percent++;
175                digits++;
176             }
177             if(!((*percent == '%' && !digits) || *percent == 'd'))
178             {
179                used = 0;
180                fprintf(stderr, "Filename contains %% characters, but not %%d or %%%% - sorry, will fail\n");
181                break;
182             }
183             percent++;
184          }
185 
186          state->filename = malloc(len + 10); // leave enough space for any timelapse generated changes to filename
187          vcos_assert(state->filename);
188          if (state->filename)
189             strncpy(state->filename, arg2, len+1);
190          used = 2;
191       }
192       else
193          used = 0;
194       break;
195    }
196 
197    case CommandVerbose: // display lots of data during run
198       state->verbose = 1;
199       used = 1;
200       break;
201 
202    case CommandCamSelect:  //Select camera input port
203    {
204       if (sscanf(arg2, "%u", &state->cameraNum) == 1)
205          used = 2;
206       break;
207    }
208 
209    case CommandSensorMode:
210    {
211       if (sscanf(arg2, "%u", &state->sensor_mode) == 1)
212          used = 2;
213       break;
214    }
215 
216    case CommandGpsd:
217       state->gps = 1;
218       used = 1;
219       break;
220    }
221 
222    return used;
223 }
224