blob: e275bc9edbba15ee2b4987d63561c5636a33c9a6 [file] [log] [blame]
Fritz Koenigcdba6532021-01-22 16:11:17 -08001/*
2 * Copyright 2021 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6// https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-encoder.html
7#include <ctype.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <getopt.h>
11#include <linux/videodev2.h>
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/ioctl.h>
17#include <sys/mman.h>
18#include <sys/stat.h>
19#include <sys/types.h>
20#include <unistd.h>
21
22static const char *kEncodeDevice = "/dev/video-enc";
23static const int kInputbufferMaxSize = 4 * 1024 * 1024;
24static const int kRequestBufferCount = 8;
25static const uint32_t kIVFHeaderSignature = v4l2_fourcc('D', 'K', 'I', 'F');
26
27struct mmap_buffers {
28 void *start[VIDEO_MAX_PLANES];
29 size_t length[VIDEO_MAX_PLANES];
30 struct gbm_bo *bo;
31};
32
33struct queue {
34 int v4lfd;
35 enum v4l2_buf_type type;
36 uint32_t fourcc;
37 struct mmap_buffers *buffers;
38 uint32_t raw_width;
39 uint32_t raw_height;
40 uint32_t encoded_width;
41 uint32_t encoded_height;
42 uint32_t cnt;
43 uint32_t frame_cnt;
44 uint32_t num_planes;
45};
46
47struct encoder_cfg {
48 uint32_t gop_size;
49 uint32_t bitrate;
50 enum v4l2_mpeg_video_h264_entropy_mode h264_entropy_mode;
51 enum v4l2_mpeg_video_h264_level h264_level;
52 enum v4l2_mpeg_video_h264_profile h264_profile;
53 enum v4l2_mpeg_video_header_mode header_mode;
54 enum v4l2_mpeg_video_bitrate_mode bitrate_mode;
55};
56
57struct ivf_file_header {
58 uint32_t signature;
59 uint16_t version;
60 uint16_t header_length;
61 uint32_t fourcc;
62 uint16_t width;
63 uint16_t height;
64 uint32_t denominator;
65 uint32_t numerator;
66 uint32_t frame_cnt;
67 uint32_t unused;
68} __attribute__((packed));
69
70struct ivf_frame_header {
71 uint32_t size;
72 uint64_t timestamp;
73} __attribute__((packed));
74
75void print_fourcc(uint32_t fourcc)
76{
77 printf("%c%c%c%c\n", fourcc & 0xff, fourcc >> 8 & 0xff, fourcc >> 16 & 0xff,
78 fourcc >> 24 & 0xff);
79}
80
81int query_format(int v4lfd, enum v4l2_buf_type type, uint32_t fourcc)
82{
83 struct v4l2_fmtdesc fmtdesc;
84 memset(&fmtdesc, 0, sizeof(fmtdesc));
85
86 fmtdesc.type = type;
87 while (ioctl(v4lfd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
88 if (fourcc == 0)
89 print_fourcc(fmtdesc.pixelformat);
90 else if (fourcc == fmtdesc.pixelformat)
91 return 1;
92 fmtdesc.index++;
93 }
94
95 return 0;
96}
97
98void enumerate_menu(int v4lfd, uint32_t id, uint32_t min, uint32_t max)
99{
100 struct v4l2_querymenu querymenu;
101 memset(&querymenu, 0, sizeof(querymenu));
102
103 querymenu.id = id;
104 for (querymenu.index = min; querymenu.index <= max; querymenu.index++) {
105 if (0 == ioctl(v4lfd, VIDIOC_QUERYMENU, &querymenu))
106 fprintf(stderr, " %s\n", querymenu.name);
107 }
108}
109
110int capabilities(int v4lfd, uint32_t OUTPUT_format, uint32_t CAPTURE_format,
111 int verbose_capabilities)
112{
113 struct v4l2_capability cap;
114 memset(&cap, 0, sizeof(cap));
115 int ret = ioctl(v4lfd, VIDIOC_QUERYCAP, &cap);
116 if (ret != 0)
117 perror("VIDIOC_QUERYCAP failed");
118
119 printf("driver=\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x\n", cap.driver, cap.bus_info,
120 cap.card, v4lfd);
121
122 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, OUTPUT_format)) {
123 printf("Supported OUTPUT formats:\n");
124 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, 0);
125 ret = 1;
126 }
127
128 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, CAPTURE_format)) {
129 printf("Supported CAPTURE formats:\n");
130 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0);
131 ret = 1;
132 }
133
134 if (verbose_capabilities) {
135 struct v4l2_query_ext_ctrl queryctrl;
136 memset(&queryctrl, 0, sizeof(queryctrl));
137
138 for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++) {
139 if (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
140 fprintf(stderr, "control %s : %s\n", queryctrl.name,
141 queryctrl.flags & V4L2_CTRL_FLAG_DISABLED ? "disabled"
142 : "enabled");
143 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
144 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
145 queryctrl.maximum);
146 } else if (errno == EINVAL) {
147 continue;
148 }
149 }
150
151 for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++) {
152 if (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
153 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
154 continue;
155
156 fprintf(stderr, "control %s\n", queryctrl.name);
157
158 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
159 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
160 queryctrl.maximum);
161 } else if (errno == EINVAL) {
162 break;
163 }
164 }
165
166 memset(&queryctrl, 0, sizeof(queryctrl));
167 queryctrl.id = V4L2_CTRL_CLASS_MPEG | V4L2_CTRL_FLAG_NEXT_CTRL;
168 while (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
169 fprintf(stderr, "control %s\n", queryctrl.name);
170
171 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
172 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
173 queryctrl.maximum);
174
175 if (V4L2_CTRL_ID2CLASS(queryctrl.id) != V4L2_CTRL_CLASS_MPEG)
176 break;
177 /* ... */
178 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
179 }
180 }
181 return ret;
182}
183
184int submit_raw_frame(FILE *fp, struct queue *queue, uint32_t index)
185{
186 struct mmap_buffers *buffers = queue->buffers;
187
188 // read y plane first
189 if (queue->raw_width != queue->encoded_width) {
190 // This means that the frame will need to be copied over line by line
191 fprintf(stderr, "unable to load raw frame! %d != %d\n", queue->raw_width,
192 queue->encoded_width);
193 return -1;
194 }
195
196 // read y plane first
197 size_t frame_size = queue->raw_width * queue->raw_height;
198 uint8_t *buffer = buffers[index].start[0];
199
200 if (fread(buffer, frame_size, 1, fp) != 1) {
201 fprintf(stderr, "unable to read luma frame\n");
202 return -1;
203 }
204
205 // now read uv
206 frame_size >>= 1;
207 if (queue->num_planes == 2)
208 buffer = buffers[index].start[1];
209 else if (queue->num_planes == 1)
210 buffer += queue->encoded_width * queue->encoded_height;
211 else
212 return -1;
213
214 if (fread(buffer, frame_size, 1, fp) != 1) {
215 fprintf(stderr, "unable to read chroma frame\n");
216 return -1;
217 }
218
219 struct v4l2_buffer v4l2_buffer;
220 struct v4l2_plane planes[VIDEO_MAX_PLANES];
221 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
222
223 v4l2_buffer.index = index;
224 v4l2_buffer.type = queue->type;
225 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
226 v4l2_buffer.length = queue->num_planes;
227 v4l2_buffer.timestamp.tv_sec = 0;
228 v4l2_buffer.timestamp.tv_usec = queue->frame_cnt;
229 v4l2_buffer.m.planes = planes;
230 for (uint32_t i = 0; i < queue->num_planes; ++i) {
231 v4l2_buffer.m.planes[i].length = buffers[index].length[i];
232 v4l2_buffer.m.planes[i].bytesused = buffers[index].length[i];
233 v4l2_buffer.m.planes[i].data_offset = 0;
234 }
235
236 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
237 if (ret != 0) {
238 perror("VIDIOC_QBUF failed");
239 return -1;
240 }
241
242 queue->frame_cnt++;
243
244 return 0;
245}
246
247void cleanup_queue(struct queue *queue)
248{
249 if (queue->cnt) {
250 struct mmap_buffers *buffers = queue->buffers;
251
252 for (uint32_t i = 0; i < queue->cnt; i++)
253 for (uint32_t j = 0; j < queue->num_planes; j++) {
254 munmap(buffers[i].start[j], buffers[i].length[j]);
255 }
256
257 free(queue->buffers);
258 queue->cnt = 0;
259 }
260}
261
262int request_mmap_buffers(struct queue *queue, struct v4l2_requestbuffers *reqbuf)
263{
264 const int v4lfd = queue->v4lfd;
265 const uint32_t buffer_alloc = reqbuf->count * sizeof(struct mmap_buffers);
266 struct mmap_buffers *buffers = (struct mmap_buffers *)malloc(buffer_alloc);
267 memset(buffers, 0, buffer_alloc);
268 queue->buffers = buffers;
269 queue->cnt = reqbuf->count;
270
271 int ret;
272 for (uint32_t i = 0; i < reqbuf->count; i++) {
273 struct v4l2_buffer buffer;
274 struct v4l2_plane planes[VIDEO_MAX_PLANES];
275 memset(&buffer, 0, sizeof(buffer));
276 buffer.type = reqbuf->type;
277 buffer.memory = V4L2_MEMORY_MMAP;
278 buffer.index = i;
279 buffer.length = queue->num_planes;
280 buffer.m.planes = planes;
281 ret = ioctl(v4lfd, VIDIOC_QUERYBUF, &buffer);
282 if (ret != 0) {
283 printf("VIDIOC_QUERYBUF failed: %d\n", ret);
284 break;
285 }
286
287 for (uint32_t j = 0; j < queue->num_planes; j++) {
288 buffers[i].length[j] = buffer.m.planes[j].length;
289 buffers[i].start[j] =
290 mmap(NULL, buffer.m.planes[j].length, PROT_READ | PROT_WRITE,
291 MAP_SHARED, v4lfd, buffer.m.planes[j].m.mem_offset);
292 if (MAP_FAILED == buffers[i].start[j]) {
293 fprintf(stderr,
294 "failed to mmap buffer of length(%d) and offset(0x%x)\n",
295 buffer.m.planes[j].length, buffer.m.planes[j].m.mem_offset);
296 }
297 }
298 }
299
300 return ret;
301}
302
303int queue_CAPTURE_buffer(struct queue *queue, uint32_t index)
304{
305 struct mmap_buffers *buffers = queue->buffers;
306 struct v4l2_buffer v4l2_buffer;
307 struct v4l2_plane planes[VIDEO_MAX_PLANES];
308 memset(&v4l2_buffer, 0, sizeof v4l2_buffer);
309 memset(&planes, 0, sizeof planes);
310
311 v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
312 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
313 v4l2_buffer.index = index;
314 v4l2_buffer.m.planes = planes;
315 v4l2_buffer.length = queue->num_planes;
316
317 v4l2_buffer.m.planes[0].length = buffers[index].length[0];
318 v4l2_buffer.m.planes[0].bytesused = buffers[index].length[0];
319 v4l2_buffer.m.planes[0].data_offset = 0;
320
321 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
322 if (ret != 0) {
323 perror("VIDIOC_QBUF failed");
324 }
325
326 return ret;
327}
328
329// 4.5.2.5. Initialization
330int Initialization(struct queue *OUTPUT_queue, struct queue *CAPTURE_queue, uint32_t framerate)
331{
332 int ret = 0;
333
334 // 1. Set the coded format on the CAPTURE queue via VIDIOC_S_FMT().
335 if (!ret) {
336 struct v4l2_format fmt;
337 memset(&fmt, 0, sizeof(fmt));
338
339 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
340 fmt.fmt.pix_mp.pixelformat = CAPTURE_queue->fourcc;
341 fmt.fmt.pix_mp.width = CAPTURE_queue->raw_width;
342 fmt.fmt.pix_mp.height = CAPTURE_queue->raw_height;
343 fmt.fmt.pix_mp.plane_fmt[0].sizeimage = kInputbufferMaxSize;
344 fmt.fmt.pix_mp.num_planes = 1;
345
346 int ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_S_FMT, &fmt);
347 if (ret != 0)
348 perror("VIDIOC_S_FMT failed");
349
350 CAPTURE_queue->encoded_width = fmt.fmt.pix_mp.width;
351 CAPTURE_queue->encoded_height = fmt.fmt.pix_mp.height;
352 }
353
354 // 3. Set the raw source format on the OUTPUT queue via VIDIOC_S_FMT().
355 if (!ret) {
356 struct v4l2_format fmt;
357 memset(&fmt, 0, sizeof(fmt));
358
359 fmt.type = OUTPUT_queue->type;
360 fmt.fmt.pix_mp.pixelformat = OUTPUT_queue->fourcc;
361 fmt.fmt.pix_mp.width = OUTPUT_queue->raw_width;
362 fmt.fmt.pix_mp.height = OUTPUT_queue->raw_height;
363 fmt.fmt.pix_mp.num_planes = 1;
364
365 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_FMT, &fmt);
366 if (ret != 0)
367 perror("VIDIOC_S_FMT failed");
368
369 OUTPUT_queue->encoded_width = fmt.fmt.pix_mp.width;
370 OUTPUT_queue->encoded_height = fmt.fmt.pix_mp.height;
371
372 OUTPUT_queue->num_planes = fmt.fmt.pix_mp.num_planes;
373 }
374
375 // 4. Set the raw frame interval on the OUTPUT queue via VIDIOC_S_PARM()
376 if (!ret) {
377 struct v4l2_streamparm parms;
378 memset(&parms, 0, sizeof(parms));
379 parms.type = OUTPUT_queue->type;
380 // Note that we are provided "frames per second" but V4L2 expects "time per
381 // frame"; hence we provide the reciprocal of the framerate here.
382 parms.parm.output.timeperframe.numerator = 1;
383 parms.parm.output.timeperframe.denominator = framerate;
384
385 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_PARM, &parms);
386 if (ret != 0)
387 perror("VIDIOC_S_PARAM failed");
388 }
389
390 // 6. Optional. Set the visible resolution for the stream metadata via
391 // VIDIOC_S_SELECTION() on the OUTPUT queue if it is desired to be
392 // different than the full OUTPUT resolution.
393 if (!ret) {
394 struct v4l2_selection selection_arg;
395 memset(&selection_arg, 0, sizeof(selection_arg));
396 selection_arg.type = OUTPUT_queue->type;
397 selection_arg.target = V4L2_SEL_TGT_CROP;
398 selection_arg.r.left = 0;
399 selection_arg.r.top = 0;
400 selection_arg.r.width = OUTPUT_queue->raw_width;
401 selection_arg.r.height = OUTPUT_queue->raw_height;
402
403 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_SELECTION, &selection_arg);
404
405 if (ret != 0)
406 perror("VIDIOC_S_SELECTION failed");
407
408 // TODO(fritz) : check returned values are same as sent values
409 }
410
411 // 7. Allocate buffers for both OUTPUT and CAPTURE via VIDIOC_REQBUFS().
412 // This may be performed in any order.
413 if (!ret) {
414 struct v4l2_requestbuffers reqbuf;
415 memset(&reqbuf, 0, sizeof(reqbuf));
416 reqbuf.count = kRequestBufferCount;
417 reqbuf.type = OUTPUT_queue->type;
418 reqbuf.memory = V4L2_MEMORY_MMAP;
419
420 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
421 if (ret != 0)
422 perror("VIDIOC_REQBUFS failed");
423
424 printf("%d buffers requested, %d buffers for uncompressed frames returned\n",
425 kRequestBufferCount, reqbuf.count);
426
427 ret = request_mmap_buffers(OUTPUT_queue, &reqbuf);
428 }
429
430 if (!ret) {
431 struct v4l2_requestbuffers reqbuf;
432 memset(&reqbuf, 0, sizeof(reqbuf));
433 reqbuf.count = kRequestBufferCount;
434 reqbuf.type = CAPTURE_queue->type;
435 reqbuf.memory = V4L2_MEMORY_MMAP;
436
437 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
438 if (ret != 0)
439 perror("VIDIOC_REQBUFS failed");
440
441 printf("%d buffers requested, %d buffers for compressed frames returned\n",
442 kRequestBufferCount, reqbuf.count);
443
444 ret = request_mmap_buffers(CAPTURE_queue, &reqbuf);
445 for (uint32_t i = 0; i < reqbuf.count; i++) {
446 queue_CAPTURE_buffer(CAPTURE_queue, i);
447 }
448 }
449
450 return ret;
451}
452
453int configure_h264(int v4lfd, struct encoder_cfg *cfg)
454{
455 int ret = 0;
456 const int kH264CtrlCnt = 4;
457
458 struct v4l2_ext_control ext_ctrl[kH264CtrlCnt];
459 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
460
461 ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_H264_PROFILE;
462 ext_ctrl[0].value = cfg->h264_profile;
463
464 ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
465 ext_ctrl[1].value = cfg->h264_level;
466
467 ext_ctrl[2].id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE;
468 ext_ctrl[2].value = cfg->h264_entropy_mode;
469
470 ext_ctrl[3].id = V4L2_CID_MPEG_VIDEO_HEADER_MODE;
471 ext_ctrl[3].value = cfg->header_mode;
472
473 struct v4l2_ext_controls ext_ctrls;
474 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
475
476 ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
477 ext_ctrls.count = kH264CtrlCnt;
478 ext_ctrls.controls = ext_ctrl;
479
480 ret = ioctl(v4lfd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
481
482 if (ret != 0)
483 perror("VIDIOC_S_EXT_CTRLS failed");
484
485 for (uint32_t i = 0; i < kH264CtrlCnt; ++i)
486 ext_ctrl[i].value = 0;
487
488 ret = ioctl(v4lfd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
489 if (ret != 0)
490 perror("VIDIOC_G_EXT_CTRLS failed");
491
492 if (ext_ctrl[0].value != cfg->h264_profile)
493 fprintf(stderr, "requested profile(%d) was not used, using (%d) instead.\n",
494 cfg->h264_profile, ext_ctrl[0].value);
495
496 if (ext_ctrl[1].value != cfg->h264_level)
497 fprintf(stderr, "requested level(%d) was not used, using (%d) instead.\n",
498 cfg->h264_level, ext_ctrl[1].value);
499
500 if (ext_ctrl[2].value != cfg->h264_entropy_mode)
501 fprintf(stderr, "requested entropy mode(%d) was not used, using (%d) instead.\n",
502 cfg->h264_entropy_mode, ext_ctrl[2].value);
503
504 if (ext_ctrl[3].value != cfg->header_mode)
505 fprintf(stderr, "requested entropy mode(%d) was not used, using (%d) instead.\n",
506 cfg->header_mode, ext_ctrl[3].value);
507
508 return ret;
509}
510
511int configure_common(int v4lfd, struct encoder_cfg *cfg)
512{
513 int ret = 0;
514 const int kCommonCtrlCnt = 5;
515
516 struct v4l2_ext_control ext_ctrl[kCommonCtrlCnt];
517 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
518
519 ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_BITRATE;
520 ext_ctrl[0].value = cfg->bitrate;
521
522 ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK;
523 ext_ctrl[1].value = cfg->bitrate * 2;
524
525 ext_ctrl[2].id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
526 ext_ctrl[2].value = cfg->gop_size;
527
528 ext_ctrl[3].id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE;
529 ext_ctrl[3].value = 1;
530
531 ext_ctrl[4].id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE;
532 ext_ctrl[4].value = cfg->bitrate_mode;
533
534 struct v4l2_ext_controls ext_ctrls;
535 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
536
537 ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
538 ext_ctrls.count = kCommonCtrlCnt;
539 ext_ctrls.controls = ext_ctrl;
540
541 ret = ioctl(v4lfd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
542
543 if (ret != 0)
544 perror("VIDIOC_S_EXT_CTRLS failed");
545
546 for (uint32_t i = 0; i < kCommonCtrlCnt; ++i)
547 ext_ctrl[i].value = 0;
548
549 ret = ioctl(v4lfd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
550 if (ret != 0)
551 perror("VIDIOC_G_EXT_CTRLS failed");
552
553 if (ext_ctrl[0].value != cfg->bitrate)
554 fprintf(stderr,
555 "requested bitrate(%d) was outside of the limit, using (%d) instead.\n",
556 cfg->bitrate, ext_ctrl[0].value);
557
558 if (ext_ctrl[1].value != cfg->bitrate * 2)
559 fprintf(
560 stderr,
561 "requested bitrate peak(%d) was outside of the limit, using (%d) instead.\n",
562 cfg->bitrate * 2, ext_ctrl[1].value);
563
564 if (ext_ctrl[2].value != cfg->gop_size)
565 fprintf(stderr, "requested gop size(%d) was not used, using (%d) instead.\n",
566 cfg->gop_size, ext_ctrl[2].value);
567
568 if (ext_ctrl[3].value != 1)
569 fprintf(stderr,
570 "requested frame rate control (%d) was not used, using (%d) instead.\n", 1,
571 ext_ctrl[3].value);
572
573 if (ext_ctrl[4].value != cfg->bitrate_mode)
574 fprintf(stderr, "requested bitrate mode(%d) was not used, using (%d) instead.\n",
575 cfg->bitrate_mode, ext_ctrl[4].value);
576
577 return ret;
578}
579
580int dequeue_buffer(struct queue *queue, uint32_t *index, uint32_t *bytesused, uint32_t *data_offset,
581 uint64_t *timestamp)
582{
583 struct v4l2_buffer v4l2_buffer;
584 struct v4l2_plane planes[VIDEO_MAX_PLANES] = { 0 };
585 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
586 v4l2_buffer.type = queue->type;
587 v4l2_buffer.length = queue->num_planes;
588 v4l2_buffer.m.planes = planes;
589 v4l2_buffer.m.planes[0].bytesused = 0;
590 int ret = ioctl(queue->v4lfd, VIDIOC_DQBUF, &v4l2_buffer);
591
592 if (ret != 0 && errno != EAGAIN)
593 perror("VIDIOC_DQBUF failed");
594
595 *index = v4l2_buffer.index;
596 if (bytesused)
597 *bytesused = v4l2_buffer.m.planes[0].bytesused;
598 if (data_offset)
599 *data_offset = v4l2_buffer.m.planes[0].data_offset;
600 if (timestamp)
601 *timestamp = v4l2_buffer.timestamp.tv_usec;
602 return ret;
603}
604
605int encode(FILE *fp_input, struct queue *OUTPUT_queue, struct queue *CAPTURE_queue,
606 uint32_t frames_to_decode)
607{
608 int ret = 0;
609 char output_file_name[256];
610 int use_ivf = 0;
611
612 fprintf(stderr, "encoding\n");
613
614 if (CAPTURE_queue->fourcc == v4l2_fourcc('V', 'P', '8', '0'))
615 use_ivf = 1;
616
617 sprintf(output_file_name, "output%s", use_ivf ? ".ivf" : ".h264");
618 FILE *fp_output = fopen(output_file_name, "wb");
619 if (!fp_output) {
620 fprintf(stderr, "unable to write to file: %s\n", output_file_name);
621 ret = 1;
622 }
623
624 // write header
625 if (use_ivf) {
626 struct ivf_file_header header;
627 header.signature = kIVFHeaderSignature;
628 header.version = 0;
629 header.header_length = sizeof(struct ivf_file_header);
630 header.fourcc = CAPTURE_queue->fourcc;
631 header.width = CAPTURE_queue->raw_width;
632 header.height = CAPTURE_queue->raw_height;
633 // hard coded 30fps
634 header.denominator = 30;
635 header.numerator = 1;
636 header.frame_cnt = frames_to_decode;
637 header.unused = 0;
638
639 if (fwrite(&header, sizeof(struct ivf_file_header), 1, fp_output) != 1) {
640 fprintf(stderr, "unable to write ivf file header\n");
641 }
642 }
643
644 if (!ret) {
645 // prime input by filling up the OUTPUT queue with raw frames
646 for (uint32_t i = 0; i < OUTPUT_queue->cnt; ++i) {
647 if (submit_raw_frame(fp_input, OUTPUT_queue, i)) {
648 fprintf(stderr, "unable to submit raw frame\n");
649 ret = 1;
650 }
651 }
652 }
653
654 if (!ret) {
655 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_STREAMON, &OUTPUT_queue->type);
656 if (ret != 0)
657 perror("VIDIOC_STREAMON failed on OUTPUT");
658 }
659
660 if (!ret) {
661 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_STREAMON, &CAPTURE_queue->type);
662 if (ret != 0)
663 perror("VIDIOC_STREAMON failed on CAPTURE");
664 }
665
666 if (!ret) {
667 uint32_t cnt = 0;
668 while (cnt < frames_to_decode) {
669 // handle CAPTURE queue first
670 {
671 uint32_t index = 0;
672 uint32_t bytesused = 0;
673 uint32_t data_offset = 0;
674 uint64_t timestamp = 0;
675
676 // first get the newly encoded frame
677 ret = dequeue_buffer(CAPTURE_queue, &index, &bytesused,
678 &data_offset, &timestamp);
679 if (ret != 0)
680 continue;
681
682 if (use_ivf) {
683 struct ivf_frame_header header;
684 header.size = bytesused - data_offset;
685 header.timestamp = timestamp;
686
687 if (fwrite(&header, sizeof(struct ivf_frame_header), 1,
688 fp_output) != 1) {
689 fprintf(stderr,
690 "unable to write ivf frame header\n");
691 }
692 }
693 fwrite(CAPTURE_queue->buffers[index].start[0] + data_offset,
694 bytesused - data_offset, 1, fp_output);
695
696 // done with the buffer, queue it back up
697 queue_CAPTURE_buffer(CAPTURE_queue, index);
698 }
699
700 // handle OUTPUT queue second
701 {
702 uint32_t index = 0;
703
704 ret = dequeue_buffer(OUTPUT_queue, &index, 0, 0, 0);
705 if (ret != 0)
706 continue;
707
708 if (submit_raw_frame(fp_input, OUTPUT_queue, index))
709 break;
710 }
711 cnt++;
712 }
713 }
714
715 if (fp_output) {
716 fclose(fp_output);
717 }
718 return ret;
719}
720
721static void print_help(const char *argv0)
722{
723 printf("usage: %s [OPTIONS]\n", argv0);
724 printf(" -f, --file nv12 file to encode\n");
725 printf(" -w, --width width of image\n");
726 printf(" -h, --height height of image\n");
727 printf(" -m, --max max number of frames to decode\n");
728 printf(" -r, --rate frames per second\n");
729 printf(" -b, --bitrate bits per second\n");
730 printf(" -g, --gop gop length\n");
731 printf(" -c, --codec codec\n");
732 printf(" -v, --verbose verbose capabilities\n");
733 printf(" -i, --buffer_fmt OUTPUT queue format\n");
734}
735
736static const struct option longopts[] = {
737 { "file", required_argument, NULL, 'f' },
738 { "width", required_argument, NULL, 'w' },
739 { "height", required_argument, NULL, 'h' },
740 { "max", required_argument, NULL, 'm' },
741 { "rate", required_argument, NULL, 'r' },
742 { "bitrate", required_argument, NULL, 'b' },
743 { "gop", required_argument, NULL, 'g' },
744 { "codec", required_argument, NULL, 'c' },
745 { "verbose", no_argument, NULL, 'v' },
746 { "buffer_fmt", no_argument, NULL, 'i' },
747 { 0, 0, 0, 0 },
748};
749
750int main(int argc, char *argv[])
751{
752 uint32_t OUTPUT_format = v4l2_fourcc('N', 'V', '1', '2');
753 uint32_t CAPTURE_format = v4l2_fourcc('H', '2', '6', '4');
754 char *file_name = NULL;
755 uint32_t width = 0;
756 uint32_t height = 0;
757 uint32_t frames_to_decode = 0;
758 uint32_t framerate = 30;
759 int verbose_capabilities = 0;
760 int c;
761
762 struct encoder_cfg cfg = { .gop_size = 20,
763 .bitrate = 1000,
764 .h264_entropy_mode = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
765 .h264_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
766 .h264_profile = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
767 .header_mode = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
768 .bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR };
769
770 while ((c = getopt_long(argc, argv, "f:w:h:m:r:b:g:c:vzo:", longopts, NULL)) != -1) {
771 switch (c) {
772 case 'f':
773 file_name = strdup(optarg);
774 break;
775 case 'w':
776 width = atoi(optarg);
777 break;
778 case 'h':
779 height = atoi(optarg);
780 break;
781 case 'm':
782 frames_to_decode = atoi(optarg);
783 break;
784 case 'r':
785 framerate = atoi(optarg);
786 break;
787 case 'b':
788 cfg.bitrate = atoi(optarg);
789 break;
790 case 'g':
791 cfg.gop_size = atoi(optarg);
792 break;
793 case 'c':
794 if (strlen(optarg) == 4) {
795 CAPTURE_format =
796 v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
797 toupper(optarg[2]), toupper(optarg[3]));
798 printf("using (%s) as the codec\n", optarg);
799 }
800 break;
801 case 'v':
802 verbose_capabilities = 1;
803 break;
804 case 'o':
805 if (strlen(optarg) == 4) {
806 OUTPUT_format =
807 v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
808 toupper(optarg[2]), toupper(optarg[3]));
809 printf("using (%s) as the OUTPUT queue buffer format\n",
810 optarg);
811 }
812 break;
813 default:
814 break;
815 }
816 }
817
818 if (!file_name || width == 0 || height == 0) {
819 fprintf(stderr, "Invalid parameters!\n");
820 print_help(argv[0]);
821 exit(1);
822 }
823
824 FILE *fp = fopen(file_name, "rb");
825 if (!fp) {
826 fprintf(stderr, "%s: unable to open file.\n", file_name);
827 exit(1);
828 }
829
830 if (!frames_to_decode) {
831 fseek(fp, 0, SEEK_END);
832 uint64_t length = ftell(fp);
833 uint32_t frame_size = (3 * width * height) >> 1;
834 frames_to_decode = length / frame_size;
835 fseek(fp, 0, SEEK_SET);
836 }
837
838 fprintf(stderr, "encoding %d frames\n", frames_to_decode);
839
840 int v4lfd = open(kEncodeDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC);
841 if (v4lfd < 0) {
842 fprintf(stderr, "Unable to open device file: %s\n", kEncodeDevice);
843 exit(EXIT_FAILURE);
844 }
845
846 if (capabilities(v4lfd, OUTPUT_format, CAPTURE_format, verbose_capabilities) != 0) {
847 fprintf(stderr, "Capabilities not present for encode.\n");
848 exit(EXIT_FAILURE);
849 }
850
851 struct queue OUTPUT_queue = { .v4lfd = v4lfd,
852 .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
853 .fourcc = OUTPUT_format,
854 .raw_width = width,
855 .raw_height = height,
856 .frame_cnt = 0,
857 .num_planes = 1 };
858
859 struct queue CAPTURE_queue = { .v4lfd = v4lfd,
860 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
861 .fourcc = CAPTURE_format,
862 .raw_width = width,
863 .raw_height = height,
864 .num_planes = 1 };
865
866 int ret = Initialization(&OUTPUT_queue, &CAPTURE_queue, framerate);
867
868 // not all configurations are supported, so we don't need to track
869 // the return value
870 if (!ret) {
871 configure_common(v4lfd, &cfg);
872
873 if (v4l2_fourcc('H', '2', '6', '4') == CAPTURE_format)
874 configure_h264(v4lfd, &cfg);
875 }
876
877 if (!ret)
878 ret = encode(fp, &OUTPUT_queue, &CAPTURE_queue, frames_to_decode);
879
880 cleanup_queue(&OUTPUT_queue);
881 cleanup_queue(&CAPTURE_queue);
882 close(v4lfd);
883
884 return 0;
885}