1 #include <unistd.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <inttypes.h>
6 #include <getopt.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <sys/ioctl.h>
13 #include <sys/time.h>
14 #include <dirent.h>
15 #include <math.h>
16 #include <cstdlib>
17 
18 #include "v4l2-ctl.h"
19 
20 struct v4l2_decoder_cmd dec_cmd; /* (try_)decoder_cmd */
21 static struct v4l2_encoder_cmd enc_cmd; /* (try_)encoder_cmd */
22 static struct v4l2_jpegcompression jpegcomp; /* jpeg compression */
23 static struct v4l2_streamparm parm;	/* get/set parm */
24 static double fps = 0;			/* set framerate speed, in fps */
25 static double output_fps = 0;		/* set framerate speed, in fps */
26 
misc_usage()27 void misc_usage()
28 {
29 	printf("\nMiscellaneous options:\n"
30 	       "  --wait-for-event <event>\n"
31 	       "                     wait for an event [VIDIOC_DQEVENT]\n"
32 	       "                     <event> is the event number or one of:\n"
33 	       "                     eos, vsync, ctrl=<id>, frame_sync, source_change=<pad>,\n"
34 	       "                     motion_det\n"
35 	       "                     where <id> is the name of the control\n"
36 	       "                     and where <pad> is the index of the pad or input\n"
37 	       "  --poll-for-event <event>\n"
38 	       "                     poll for an event [VIDIOC_DQEVENT]\n"
39 	       "                     see --wait-for-event for possible events\n"
40 	       "  --epoll-for-event <event>\n"
41 	       "                     epoll for an event [VIDIOC_DQEVENT]\n"
42 	       "                     see --wait-for-event for possible events\n"
43 	       "  -P, --get-parm     display video parameters [VIDIOC_G_PARM]\n"
44 	       "  -p, --set-parm <fps>\n"
45 	       "                     set video framerate in <fps> [VIDIOC_S_PARM]\n"
46 	       "  --get-output-parm  display output video parameters [VIDIOC_G_PARM]\n"
47 	       "  --set-output-parm <fps>\n"
48 	       "                     set output video framerate in <fps> [VIDIOC_S_PARM]\n"
49 	       "  --get-jpeg-comp    query the JPEG compression [VIDIOC_G_JPEGCOMP]\n"
50 	       "  --set-jpeg-comp quality=<q>,markers=<markers>,comment=<c>,app<n>=<a>\n"
51 	       "                     set the JPEG compression [VIDIOC_S_JPEGCOMP]\n"
52 	       "                     <n> is the app segment: 0-9/a-f, <a> is the actual string.\n"
53 	       "                     <markers> is a colon separated list of:\n"
54 	       "                     dht:      Define Huffman Tables\n"
55 	       "                     dqt:      Define Quantization Tables\n"
56 	       "                     dri:      Define Restart Interval\n"
57 	       "  --encoder-cmd cmd=<cmd>,flags=<flags>\n"
58 	       "                     Send a command to the encoder [VIDIOC_ENCODER_CMD]\n"
59 	       "                     cmd=start|stop|pause|resume\n"
60 	       "                     flags=stop_at_gop_end\n"
61 	       "  --try-encoder-cmd cmd=<cmd>,flags=<flags>\n"
62 	       "                     Try an encoder command [VIDIOC_TRY_ENCODER_CMD]\n"
63 	       "                     See --encoder-cmd for the arguments.\n"
64 	       "  --decoder-cmd cmd=<cmd>,flags=<flags>,stop_pts=<pts>,start_speed=<speed>,\n"
65 	       "                     start_format=<none|gop>\n"
66 	       "                     Send a command to the decoder [VIDIOC_DECODER_CMD]\n"
67 	       "                     cmd=start|stop|pause|resume\n"
68 	       "                     flags=start_mute_audio|pause_to_black|stop_to_black|\n"
69 	       "                           stop_immediately\n"
70 	       "  --try-decoder-cmd cmd=<cmd>,flags=<flags>\n"
71 	       "                     Try a decoder command [VIDIOC_TRY_DECODER_CMD]\n"
72 	       "                     See --decoder-cmd for the arguments.\n"
73 	       );
74 }
75 
markers2s(unsigned markers)76 static std::string markers2s(unsigned markers)
77 {
78 	std::string s;
79 
80 	if (markers & V4L2_JPEG_MARKER_DHT)
81 		s += "\t\tDefine Huffman Tables\n";
82 	if (markers & V4L2_JPEG_MARKER_DQT)
83 		s += "\t\tDefine Quantization Tables\n";
84 	if (markers & V4L2_JPEG_MARKER_DRI)
85 		s += "\t\tDefine Restart Interval\n";
86 	if (markers & V4L2_JPEG_MARKER_COM)
87 		s += "\t\tDefine Comment\n";
88 	if (markers & V4L2_JPEG_MARKER_APP)
89 		s += "\t\tDefine APP segment\n";
90 	return s;
91 }
92 
printjpegcomp(const struct v4l2_jpegcompression & jc)93 static void printjpegcomp(const struct v4l2_jpegcompression &jc)
94 {
95 	printf("JPEG compression:\n");
96 	printf("\tQuality: %d\n", jc.quality);
97 	if (jc.COM_len)
98 		printf("\tComment: '%s'\n", jc.COM_data);
99 	if (jc.APP_len)
100 		printf("\tAPP%x   : '%s'\n", jc.APPn, jc.APP_data);
101 	printf("\tMarkers: 0x%08x\n", jc.jpeg_markers);
102 	printf("%s", markers2s(jc.jpeg_markers).c_str());
103 }
104 
print_enccmd(const struct v4l2_encoder_cmd & cmd)105 static void print_enccmd(const struct v4l2_encoder_cmd &cmd)
106 {
107 	switch (cmd.cmd) {
108 	case V4L2_ENC_CMD_START:
109 		printf("\tstart\n");
110 		break;
111 	case V4L2_ENC_CMD_STOP:
112 		printf("\tstop%s\n",
113 			(cmd.flags & V4L2_ENC_CMD_STOP_AT_GOP_END) ? " at gop end" : "");
114 		break;
115 	case V4L2_ENC_CMD_PAUSE:
116 		printf("\tpause\n");
117 		break;
118 	case V4L2_ENC_CMD_RESUME:
119 		printf("\tresume\n");
120 		break;
121 	}
122 }
123 
print_deccmd(const struct v4l2_decoder_cmd & cmd)124 static void print_deccmd(const struct v4l2_decoder_cmd &cmd)
125 {
126 	int32_t speed;
127 
128 	switch (cmd.cmd) {
129 	case V4L2_DEC_CMD_START:
130 		speed = cmd.start.speed;
131 		if (speed == 0)
132 			speed = 1000;
133 		printf("\tstart%s%s, ",
134 			cmd.start.format == V4L2_DEC_START_FMT_GOP ? " (GOP aligned)" : "",
135 			(speed != 1000 &&
136 			 (cmd.flags & V4L2_DEC_CMD_START_MUTE_AUDIO)) ? " (mute audio)" : "");
137 		if (speed == 1 || speed == -1)
138 			printf("single step %s\n",
139 				speed == 1 ? "forward" : "backward");
140 		else
141 			printf("speed %.3fx\n", speed / 1000.0);
142 		break;
143 	case V4L2_DEC_CMD_STOP:
144 		printf("\tstop%s%s\n",
145 			(cmd.flags & V4L2_DEC_CMD_STOP_TO_BLACK) ? " to black" : "",
146 			(cmd.flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) ? " immediately" : "");
147 		break;
148 	case V4L2_DEC_CMD_PAUSE:
149 		printf("\tpause%s\n",
150 			(cmd.flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? " to black" : "");
151 		break;
152 	case V4L2_DEC_CMD_RESUME:
153 		printf("\tresume\n");
154 		break;
155 	}
156 }
157 
158 /* Used for both encoder and decoder commands since they are the same
159    at the moment. */
parse_cmd(const char * s)160 static int parse_cmd(const char *s)
161 {
162 	if (!strcmp(s, "start")) return V4L2_ENC_CMD_START;
163 	if (!strcmp(s, "stop")) return V4L2_ENC_CMD_STOP;
164 	if (!strcmp(s, "pause")) return V4L2_ENC_CMD_PAUSE;
165 	if (!strcmp(s, "resume")) return V4L2_ENC_CMD_RESUME;
166 	return 0;
167 }
168 
parse_encflags(const char * s)169 static int parse_encflags(const char *s)
170 {
171 	if (!strcmp(s, "stop_at_gop_end")) return V4L2_ENC_CMD_STOP_AT_GOP_END;
172 	return 0;
173 }
174 
parse_decflags(const char * s)175 static int parse_decflags(const char *s)
176 {
177 	if (!strcmp(s, "start_mute_audio")) return V4L2_DEC_CMD_START_MUTE_AUDIO;
178 	if (!strcmp(s, "pause_to_black")) return V4L2_DEC_CMD_PAUSE_TO_BLACK;
179 	if (!strcmp(s, "stop_to_black")) return V4L2_DEC_CMD_STOP_TO_BLACK;
180 	if (!strcmp(s, "stop_immediately")) return V4L2_DEC_CMD_STOP_IMMEDIATELY;
181 	return 0;
182 }
183 
misc_cmd(int ch,char * optarg)184 void misc_cmd(int ch, char *optarg)
185 {
186 	char *value, *subs;
187 
188 	switch (ch) {
189 	case OptSetParm:
190 		fps = strtod(optarg, NULL);
191 		break;
192 	case OptSetOutputParm:
193 		output_fps = strtod(optarg, NULL);
194 		break;
195 	case OptSetJpegComp:
196 		subs = optarg;
197 		while (*subs != '\0') {
198 			static const char *const subopts[] = {
199 				"app0", "app1", "app2", "app3",
200 				"app4", "app5", "app6", "app7",
201 				"app8", "app9", "appa", "appb",
202 				"appc", "appd", "appe", "appf",
203 				"quality",
204 				"markers",
205 				"comment",
206 				NULL
207 			};
208 			size_t len;
209 			int opt = parse_subopt(&subs, subopts, &value);
210 
211 			switch (opt) {
212 			case 16:
213 				jpegcomp.quality = strtol(value, 0L, 0);
214 				break;
215 			case 17:
216 				if (strstr(value, "dht"))
217 					jpegcomp.jpeg_markers |= V4L2_JPEG_MARKER_DHT;
218 				if (strstr(value, "dqt"))
219 					jpegcomp.jpeg_markers |= V4L2_JPEG_MARKER_DQT;
220 				if (strstr(value, "dri"))
221 					jpegcomp.jpeg_markers |= V4L2_JPEG_MARKER_DRI;
222 				break;
223 			case 18:
224 				len = strlen(value);
225 				if (len > sizeof(jpegcomp.COM_data) - 1)
226 					len = sizeof(jpegcomp.COM_data) - 1;
227 				jpegcomp.COM_len = len;
228 				memcpy(jpegcomp.COM_data, value, len);
229 				jpegcomp.COM_data[len] = '\0';
230 				break;
231 			default:
232 				if (opt < 0 || opt > 15) {
233 					misc_usage();
234 					std::exit(EXIT_FAILURE);
235 				}
236 				len = strlen(value);
237 				if (len > sizeof(jpegcomp.APP_data) - 1)
238 					len = sizeof(jpegcomp.APP_data) - 1;
239 				if (jpegcomp.APP_len) {
240 					fprintf(stderr, "Only one APP segment can be set\n");
241 					break;
242 				}
243 				jpegcomp.APP_len = len;
244 				memcpy(jpegcomp.APP_data, value, len);
245 				jpegcomp.APP_data[len] = '\0';
246 				jpegcomp.APPn = opt;
247 				break;
248 			}
249 		}
250 		break;
251 	case OptEncoderCmd:
252 	case OptTryEncoderCmd:
253 		subs = optarg;
254 		while (*subs != '\0') {
255 			static const char *const subopts[] = {
256 				"cmd",
257 				"flags",
258 				NULL
259 			};
260 
261 			switch (parse_subopt(&subs, subopts, &value)) {
262 			case 0:
263 				enc_cmd.cmd = parse_cmd(value);
264 				break;
265 			case 1:
266 				enc_cmd.flags = parse_encflags(value);
267 				break;
268 			default:
269 				misc_usage();
270 				std::exit(EXIT_FAILURE);
271 			}
272 		}
273 		break;
274 	case OptDecoderCmd:
275 	case OptTryDecoderCmd:
276 		subs = optarg;
277 		while (*subs != '\0') {
278 			static const char *const subopts[] = {
279 				"cmd",
280 				"flags",
281 				"stop_pts",
282 				"start_speed",
283 				"start_format",
284 				NULL
285 			};
286 
287 			switch (parse_subopt(&subs, subopts, &value)) {
288 			case 0:
289 				dec_cmd.cmd = parse_cmd(value);
290 				break;
291 			case 1:
292 				dec_cmd.flags = parse_decflags(value);
293 				break;
294 			case 2:
295 				dec_cmd.stop.pts = strtoull(value, 0, 0);
296 				break;
297 			case 3:
298 				dec_cmd.start.speed = strtol(value, 0, 0);
299 				break;
300 			case 4:
301 				if (!strcmp(value, "gop"))
302 					dec_cmd.start.format = V4L2_DEC_START_FMT_GOP;
303 				else if (!strcmp(value, "none"))
304 					dec_cmd.start.format = V4L2_DEC_START_FMT_NONE;
305 				break;
306 			default:
307 				misc_usage();
308 				std::exit(EXIT_FAILURE);
309 			}
310 		}
311 		break;
312 	}
313 }
314 
misc_set(cv4l_fd & _fd)315 void misc_set(cv4l_fd &_fd)
316 {
317 	int fd = _fd.g_fd();
318 
319 	if (options[OptSetParm]) {
320 		memset(&parm, 0, sizeof(parm));
321 		parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
322 		parm.parm.capture.timeperframe.numerator = 1000;
323 		parm.parm.capture.timeperframe.denominator =
324 			static_cast<uint32_t>(fps * parm.parm.capture.timeperframe.numerator);
325 
326 		if (doioctl(fd, VIDIOC_S_PARM, &parm) == 0) {
327 			struct v4l2_fract *tf = &parm.parm.capture.timeperframe;
328 
329 			if (!tf->denominator || !tf->numerator)
330 				printf("Invalid frame rate\n");
331 			else
332 				printf("Frame rate set to %.3f fps\n",
333 					1.0 * tf->denominator / tf->numerator);
334 		}
335 	}
336 
337 	if (options[OptSetOutputParm]) {
338 		memset(&parm, 0, sizeof(parm));
339 		parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
340 		parm.parm.output.timeperframe.numerator = 1000;
341 		parm.parm.output.timeperframe.denominator =
342 			static_cast<uint32_t>(output_fps * parm.parm.output.timeperframe.numerator);
343 
344 		if (doioctl(fd, VIDIOC_S_PARM, &parm) == 0) {
345 			struct v4l2_fract *tf = &parm.parm.output.timeperframe;
346 
347 			if (!tf->denominator || !tf->numerator)
348 				printf("Invalid frame rate\n");
349 			else
350 				printf("Frame rate set to %.3f fps\n",
351 					1.0 * tf->denominator / tf->numerator);
352 		}
353 	}
354 
355 	if (options[OptSetJpegComp]) {
356 		doioctl(fd, VIDIOC_S_JPEGCOMP, &jpegcomp);
357 	}
358 
359 	if (options[OptEncoderCmd])
360 		doioctl(fd, VIDIOC_ENCODER_CMD, &enc_cmd);
361 	if (options[OptTryEncoderCmd])
362 		if (doioctl(fd, VIDIOC_TRY_ENCODER_CMD, &enc_cmd) == 0)
363 			print_enccmd(enc_cmd);
364 	if (options[OptDecoderCmd])
365 		doioctl(fd, VIDIOC_DECODER_CMD, &dec_cmd);
366 	if (options[OptTryDecoderCmd])
367 		if (doioctl(fd, VIDIOC_TRY_DECODER_CMD, &dec_cmd) == 0)
368 			print_deccmd(dec_cmd);
369 }
370 
misc_get(cv4l_fd & _fd)371 void misc_get(cv4l_fd &_fd)
372 {
373 	int fd = _fd.g_fd();
374 
375 	if (options[OptGetJpegComp]) {
376 		struct v4l2_jpegcompression jc;
377 		if (doioctl(fd, VIDIOC_G_JPEGCOMP, &jc) == 0)
378 			printjpegcomp(jc);
379 	}
380 
381         if (options[OptGetParm]) {
382 		memset(&parm, 0, sizeof(parm));
383 		parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
384 		if (doioctl(fd, VIDIOC_G_PARM, &parm) == 0) {
385 			const struct v4l2_fract &tf = parm.parm.capture.timeperframe;
386 
387 			printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str());
388 			if (parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)
389 				printf("\tCapabilities     : timeperframe\n");
390 			if (parm.parm.capture.capturemode & V4L2_MODE_HIGHQUALITY)
391 				printf("\tCapture mode     : high quality\n");
392 			if (!tf.denominator || !tf.numerator)
393 				printf("\tFrames per second: invalid (%d/%d)\n",
394 						tf.denominator, tf.numerator);
395 			else
396 				printf("\tFrames per second: %.3f (%d/%d)\n",
397 						(1.0 * tf.denominator) / tf.numerator,
398 						tf.denominator, tf.numerator);
399 			printf("\tRead buffers     : %d\n", parm.parm.capture.readbuffers);
400 		}
401 	}
402 
403 	if (options[OptGetOutputParm]) {
404 		memset(&parm, 0, sizeof(parm));
405 		parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
406 		if (doioctl(fd, VIDIOC_G_PARM, &parm) == 0) {
407 			const struct v4l2_fract &tf = parm.parm.output.timeperframe;
408 
409 			printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str());
410 			if (parm.parm.output.capability & V4L2_CAP_TIMEPERFRAME)
411 				printf("\tCapabilities     : timeperframe\n");
412 			if (parm.parm.output.outputmode & V4L2_MODE_HIGHQUALITY)
413 				printf("\tOutput mode      : high quality\n");
414 			if (!tf.denominator || !tf.numerator)
415 				printf("\tFrames per second: invalid (%d/%d)\n",
416 						tf.denominator, tf.numerator);
417 			else
418 				printf("\tFrames per second: %.3f (%d/%d)\n",
419 						(1.0 * tf.denominator) / tf.numerator,
420 						tf.denominator, tf.numerator);
421 			printf("\tWrite buffers    : %d\n", parm.parm.output.writebuffers);
422 		}
423 	}
424 }
425