1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON 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
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#include <stdlib.h>
28#include <limits.h>
29#include <stdio.h>
30#include <string.h>
31#include "mmalcam.h"
32
33#include "interface/mmal/mmal_logging.h"
34
35#define VIEWFINDER_LAYER 2
36#define DEFAULT_VIDEO_FORMAT "1280x720:h264";
37#define DEFAULT_BIT_RATE 5000000
38#define DEFAULT_CAM_NUM 0
39
40struct {
41 const char *name;
42 MMALCAM_CHANGE_T value;
43} mmalcam_change_table[] = {
44 { "image_effect", MMALCAM_CHANGE_IMAGE_EFFECT },
45 { "rotation", MMALCAM_CHANGE_ROTATION },
46 { "zoom", MMALCAM_CHANGE_ZOOM },
47 { "focus", MMALCAM_CHANGE_FOCUS },
48 { "drc", MMALCAM_CHANGE_DRC },
49 { "hdr", MMALCAM_CHANGE_HDR },
50 { "contrast", MMALCAM_CHANGE_CONTRAST },
51 { "brightness", MMALCAM_CHANGE_BRIGHTNESS },
52 { "saturation", MMALCAM_CHANGE_SATURATION },
53 { "sharpness", MMALCAM_CHANGE_SHARPNESS },
54};
55
56static int stop;
57static VCOS_THREAD_T camcorder_thread;
58static MMALCAM_BEHAVIOUR_T camcorder_behaviour;
59static uint32_t sleepy_time;
60static MMAL_BOOL_T stopped_already;
61
62/* Utility functions used by test program */
63static void *test_mmal_camcorder(void *id);
64static void test_signal_handler(int signum);
65static void test_mmalcam_dump_stats(const char *title, MMAL_PARAMETER_STATISTICS_T* stats);
66static int test_parse_cmdline(int argc, const char **argv);
67
68/*****************************************************************************/
69int main(int argc, const char **argv)
70{
71 VCOS_THREAD_ATTR_T attrs;
72 VCOS_STATUS_T status;
73 int result = 0;
74
75 vcos_log_register("mmalcam", VCOS_LOG_CATEGORY);
76 printf("MMAL Camera Test App\n");
77 signal(SIGINT, test_signal_handler);
78
79 camcorder_behaviour.layer = VIEWFINDER_LAYER;
80 camcorder_behaviour.vformat = DEFAULT_VIDEO_FORMAT;
81 camcorder_behaviour.zero_copy = 1;
82 camcorder_behaviour.bit_rate = DEFAULT_BIT_RATE;
83 camcorder_behaviour.focus_test = MMAL_PARAM_FOCUS_MAX;
84 camcorder_behaviour.camera_num = DEFAULT_CAM_NUM;
85
86 if(test_parse_cmdline(argc, argv))
87 {
88 result = -1;
89 goto error;
90 }
91
92 status = vcos_semaphore_create(&camcorder_behaviour.init_sem, "mmalcam-init", 0);
93 vcos_assert(status == VCOS_SUCCESS);
94
95 vcos_thread_attr_init(&attrs);
96 if (vcos_thread_create(&camcorder_thread, "mmal camcorder", &attrs, test_mmal_camcorder, &camcorder_behaviour) != VCOS_SUCCESS)
97 {
98 LOG_ERROR("Thread creation failure");
99 result = -2;
100 goto error;
101 }
102
103 vcos_semaphore_wait(&camcorder_behaviour.init_sem);
104 if (camcorder_behaviour.init_result != MMALCAM_INIT_SUCCESS)
105 {
106 LOG_ERROR("Initialisation failed: %d", camcorder_behaviour.init_result);
107 result = (int)camcorder_behaviour.init_result;
108 goto error;
109 }
110
111 if (sleepy_time != 0)
112 {
113 sleep(sleepy_time);
114 stop = 1;
115 }
116
117error:
118 LOG_TRACE("Waiting for camcorder thread to terminate");
119 vcos_thread_join(&camcorder_thread, NULL);
120
121 test_mmalcam_dump_stats("Render", &camcorder_behaviour.render_stats);
122 if (camcorder_behaviour.uri)
123 test_mmalcam_dump_stats("Encoder", &camcorder_behaviour.encoder_stats);
124
125 vcos_semaphore_delete(&camcorder_behaviour.init_sem);
126 return result;
127}
128
129
130/*****************************************************************************/
131static void *test_mmal_camcorder(void *id)
132{
133 MMALCAM_BEHAVIOUR_T *behaviour = (MMALCAM_BEHAVIOUR_T *)id;
134 int value;
135
136 value = test_mmal_start_camcorder(&stop, behaviour);
137
138 LOG_TRACE("Thread terminating, result %d", value);
139 return (void *)(uintptr_t)value;
140}
141
142/*****************************************************************************/
143static void test_signal_handler(int signum)
144{
145 (void)signum;
146
147 if (stopped_already)
148 {
149 LOG_ERROR("Killing program");
150 exit(255);
151 }
152 else
153 {
154 LOG_ERROR("Stopping normally. CTRL+C again to kill program");
155 stop = 1;
156 stopped_already = 1;
157 }
158}
159
160/*****************************************************************************/
161static void test_mmalcam_dump_stats(const char *title, MMAL_PARAMETER_STATISTICS_T* stats)
162{
163 printf("[%s]\n", title);
164 printf("buffer_count: %u\n", stats->buffer_count);
165 printf("frame_count: %u\n", stats->frame_count);
166 printf("frames_skipped: %u\n", stats->frames_skipped);
167 printf("frames_discarded: %u\n", stats->frames_discarded);
168 printf("eos_seen: %u\n", stats->eos_seen);
169 printf("maximum_frame_bytes: %u\n", stats->maximum_frame_bytes);
170 printf("total_bytes_hi: %u\n", (uint32_t)(stats->total_bytes >> 32));
171 printf("total_bytes_lo: %u\n", (uint32_t)(stats->total_bytes));
172 printf("corrupt_macroblocks: %u\n", stats->corrupt_macroblocks);
173}
174
175/*****************************************************************************/
176static MMAL_BOOL_T test_mmalcam_parse_rect(const char *str, MMAL_RECT_T *rect)
177{
178 /* coverity[secure_coding] Only reading integers, so can't overflow */
179 return sscanf(str, "%d,%d,%d,%d", &rect->x, &rect->y, &rect->width, &rect->height) == 4;
180}
181
182/*****************************************************************************/
183static int test_parse_cmdline(int argc, const char **argv)
184{
185 int i;
186 int passed_options = 0;
187
188 /* Parse the command line arguments */
189 for(i = 1; i < argc; i++)
190 {
191 if (!argv[i]) continue;
192
193 if (passed_options || argv[i][0] != '-')
194 {
195 /* Non-option argument */
196 continue;
197 }
198
199 /* We are now dealing with command line options */
200 switch(argv[i][1])
201 {
202 case '-': passed_options = 1; break;
203 case 'h': goto usage;
204 case 'o': if (i+1 >= argc) goto invalid_option;
205 camcorder_behaviour.uri = argv[++i];
206 break;
207 case 'v': if (i+1 >= argc) goto invalid_option;
208 camcorder_behaviour.vformat = argv[i+1];
209 break;
210 case 'r': if (i+1 >= argc) goto invalid_option;
211 if (!test_mmalcam_parse_rect(argv[i+1], &camcorder_behaviour.display_area)) goto invalid_option;
212 i++;
213 break;
214 case 'c': if (i+2 >= argc) goto invalid_option;
215 {
216 uint32_t table_index;
217
218 if (sscanf(argv[i+1], "%u", &camcorder_behaviour.seconds_per_change) != 1) goto invalid_option;
219
220 for (table_index = 0; table_index < countof(mmalcam_change_table); table_index++)
221 if (strcmp(mmalcam_change_table[table_index].name, argv[i+2]) == 0)
222 break;
223 if (table_index >= countof(mmalcam_change_table)) goto invalid_option;
224
225 camcorder_behaviour.change = mmalcam_change_table[table_index].value;
226 }
227 break;
228 case 't': if (i+1 >= argc) goto invalid_option;
229 if (sscanf(argv[i+1], "%u", &sleepy_time) != 1) goto invalid_option;
230 i++;
231 break;
232 case 'f': if (i+1 >= argc) goto invalid_option;
233 camcorder_behaviour.frame_rate.den = 1;
234 if (sscanf(argv[i+1], "%u/%u", &camcorder_behaviour.frame_rate.num, &camcorder_behaviour.frame_rate.den) == 0) goto invalid_option;
235 i++;
236 break;
237 case 'x': camcorder_behaviour.tunneling = 1; break;
238 case 'z': camcorder_behaviour.zero_copy = (argv[i][2] != '!'); break;
239 case 'O': camcorder_behaviour.opaque = 1; break;
240 case 'b': if (i+1 >= argc) goto invalid_option;
241 if (sscanf(argv[i+1], "%u", &camcorder_behaviour.bit_rate) == 0) goto invalid_option;
242 i++;
243 break;
244 case 'a': if (i+1 >= argc) goto invalid_option;
245 if (sscanf(argv[i+1], "%u", &camcorder_behaviour.focus_test) == 0) goto invalid_option;
246 if (camcorder_behaviour.focus_test > MMAL_PARAM_FOCUS_EDOF) goto invalid_option;
247 i++;
248 break;
249 case 'n': if (i+1 >= argc) goto invalid_option;
250 if (sscanf(argv[i+1], "%u", &camcorder_behaviour.camera_num) == 0) goto invalid_option;
251 i++;
252 break;
253 default: goto invalid_option;
254 }
255 continue;
256 }
257
258 return 0;
259
260 invalid_option:
261 printf("invalid command line option (%s)\n", argv[i]);
262
263 usage:
264 {
265 const char *program;
266
267 program = strrchr(argv[0], '\\');
268 if (program)
269 program++;
270 else
271 {
272 program = strrchr(argv[0], '/');
273 if (program)
274 program++;
275 else
276 program = argv[0];
277 }
278 printf("usage: %s [options]\n", program);
279 printf("options list:\n");
280 printf(" -h : help\n");
281 printf(" -o <file> : write encoded output to <file>\n");
282 printf(" -v <format> : set video resolution and encoding format (defaults to '1280x720:h264')\n");
283 printf(" -r <r> : put viewfinder at position <r>, given as x,y,width,height\n");
284 printf(" -c <n> <x> : change camera parameter every <n> seconds.\n");
285 printf(" The parameter changed is defined by <x>, one of\n");
286 printf(" image_effect, rotation, zoom, focus, hdr, drc, contrast,\n");
287 printf(" brightness, saturation, sharpness\n");
288 printf(" -t <n> : operate camera for <n> seconds\n");
289 printf(" -f <n>[/<d>]: set camera frame rate to <n>/<d>, where <d> is 1 if not given\n");
290 printf(" -x : use tunneling\n");
291 printf(" -z : use zero copy buffers (default)\n");
292 printf(" -z! : use full copy buffers\n");
293 printf(" -O : use opaque images\n");
294 printf(" -b <n> : use <n> as the bitrate (bits/s)\n");
295 printf(" -a <n> : Set to focus mode <n> (autofocus will cycle). Use MMAL_PARAM_FOCUS_T values.\n");
296 printf(" -n <n> : Set camera number <n>. Use MMAL_PARAMETER_CAMERA_NUM values.\n");
297 }
298 return 1;
299}
300