blob: 8986d22c1bf668848bf57d3961f814f69bf25055 [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
Miguel Casas266b2e42021-02-05 22:05:58 -05007#include <assert.h>
Fritz Koenigcdba6532021-01-22 16:11:17 -08008#include <ctype.h>
9#include <errno.h>
10#include <fcntl.h>
11#include <getopt.h>
12#include <linux/videodev2.h>
13#include <stdint.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/ioctl.h>
18#include <sys/mman.h>
19#include <sys/stat.h>
20#include <sys/types.h>
Miguel Casas0a13d7a2021-02-05 22:15:06 -050021#include <time.h>
Fritz Koenigcdba6532021-01-22 16:11:17 -080022#include <unistd.h>
23
24static const char *kEncodeDevice = "/dev/video-enc";
25static const int kInputbufferMaxSize = 4 * 1024 * 1024;
26static const int kRequestBufferCount = 8;
27static const uint32_t kIVFHeaderSignature = v4l2_fourcc('D', 'K', 'I', 'F');
28
29struct mmap_buffers {
30 void *start[VIDEO_MAX_PLANES];
31 size_t length[VIDEO_MAX_PLANES];
32 struct gbm_bo *bo;
33};
34
35struct queue {
36 int v4lfd;
37 enum v4l2_buf_type type;
38 uint32_t fourcc;
39 struct mmap_buffers *buffers;
40 uint32_t raw_width;
41 uint32_t raw_height;
42 uint32_t encoded_width;
43 uint32_t encoded_height;
44 uint32_t cnt;
45 uint32_t frame_cnt;
46 uint32_t num_planes;
47};
48
49struct encoder_cfg {
50 uint32_t gop_size;
51 uint32_t bitrate;
52 enum v4l2_mpeg_video_h264_entropy_mode h264_entropy_mode;
53 enum v4l2_mpeg_video_h264_level h264_level;
54 enum v4l2_mpeg_video_h264_profile h264_profile;
55 enum v4l2_mpeg_video_header_mode header_mode;
56 enum v4l2_mpeg_video_bitrate_mode bitrate_mode;
57};
58
59struct ivf_file_header {
60 uint32_t signature;
61 uint16_t version;
62 uint16_t header_length;
63 uint32_t fourcc;
64 uint16_t width;
65 uint16_t height;
66 uint32_t denominator;
67 uint32_t numerator;
68 uint32_t frame_cnt;
69 uint32_t unused;
70} __attribute__((packed));
71
72struct ivf_frame_header {
73 uint32_t size;
74 uint64_t timestamp;
75} __attribute__((packed));
76
77void print_fourcc(uint32_t fourcc)
78{
79 printf("%c%c%c%c\n", fourcc & 0xff, fourcc >> 8 & 0xff, fourcc >> 16 & 0xff,
80 fourcc >> 24 & 0xff);
81}
82
83int query_format(int v4lfd, enum v4l2_buf_type type, uint32_t fourcc)
84{
85 struct v4l2_fmtdesc fmtdesc;
86 memset(&fmtdesc, 0, sizeof(fmtdesc));
87
88 fmtdesc.type = type;
89 while (ioctl(v4lfd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
90 if (fourcc == 0)
91 print_fourcc(fmtdesc.pixelformat);
92 else if (fourcc == fmtdesc.pixelformat)
93 return 1;
94 fmtdesc.index++;
95 }
96
97 return 0;
98}
99
100void enumerate_menu(int v4lfd, uint32_t id, uint32_t min, uint32_t max)
101{
102 struct v4l2_querymenu querymenu;
103 memset(&querymenu, 0, sizeof(querymenu));
104
105 querymenu.id = id;
106 for (querymenu.index = min; querymenu.index <= max; querymenu.index++) {
107 if (0 == ioctl(v4lfd, VIDIOC_QUERYMENU, &querymenu))
108 fprintf(stderr, " %s\n", querymenu.name);
109 }
110}
111
112int capabilities(int v4lfd, uint32_t OUTPUT_format, uint32_t CAPTURE_format,
113 int verbose_capabilities)
114{
115 struct v4l2_capability cap;
116 memset(&cap, 0, sizeof(cap));
117 int ret = ioctl(v4lfd, VIDIOC_QUERYCAP, &cap);
118 if (ret != 0)
119 perror("VIDIOC_QUERYCAP failed");
120
121 printf("driver=\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x\n", cap.driver, cap.bus_info,
122 cap.card, v4lfd);
123
124 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, OUTPUT_format)) {
125 printf("Supported OUTPUT formats:\n");
126 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, 0);
127 ret = 1;
128 }
129
130 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, CAPTURE_format)) {
131 printf("Supported CAPTURE formats:\n");
132 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0);
133 ret = 1;
134 }
135
136 if (verbose_capabilities) {
137 struct v4l2_query_ext_ctrl queryctrl;
138 memset(&queryctrl, 0, sizeof(queryctrl));
139
140 for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1; queryctrl.id++) {
141 if (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
142 fprintf(stderr, "control %s : %s\n", queryctrl.name,
143 queryctrl.flags & V4L2_CTRL_FLAG_DISABLED ? "disabled"
144 : "enabled");
145 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
146 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
147 queryctrl.maximum);
148 } else if (errno == EINVAL) {
149 continue;
150 }
151 }
152
153 for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++) {
154 if (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
155 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
156 continue;
157
158 fprintf(stderr, "control %s\n", queryctrl.name);
159
160 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
161 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
162 queryctrl.maximum);
163 } else if (errno == EINVAL) {
164 break;
165 }
166 }
167
168 memset(&queryctrl, 0, sizeof(queryctrl));
169 queryctrl.id = V4L2_CTRL_CLASS_MPEG | V4L2_CTRL_FLAG_NEXT_CTRL;
170 while (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
171 fprintf(stderr, "control %s\n", queryctrl.name);
172
173 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
174 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
175 queryctrl.maximum);
176
177 if (V4L2_CTRL_ID2CLASS(queryctrl.id) != V4L2_CTRL_CLASS_MPEG)
178 break;
179 /* ... */
180 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
181 }
182 }
183 return ret;
184}
185
Miguel Casas266b2e42021-02-05 22:05:58 -0500186// This function copies the contents pointed by |fp| tp |queue|s |index| buffer.
187int submit_raw_frame_in_bulk(FILE *fp, struct queue *queue, uint32_t index)
Fritz Koenigcdba6532021-01-22 16:11:17 -0800188{
Miguel Casas266b2e42021-02-05 22:05:58 -0500189 assert(queue->num_planes == 1 || queue->num_planes == 2);
190 assert(queue->raw_width == queue->encoded_width);
191 // TODO: the code below assumes NV12 because the Chroma planes are copied in
192 // one call. Extend to YV12 if ever the need arises.
193 assert(queue->fourcc == v4l2_fourcc('N', 'V', '1', '2'));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800194
Miguel Casas266b2e42021-02-05 22:05:58 -0500195 struct mmap_buffers *buffers = queue->buffers;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800196
197 // read y plane first
198 size_t frame_size = queue->raw_width * queue->raw_height;
199 uint8_t *buffer = buffers[index].start[0];
200
201 if (fread(buffer, frame_size, 1, fp) != 1) {
202 fprintf(stderr, "unable to read luma frame\n");
203 return -1;
204 }
205
206 // now read uv
207 frame_size >>= 1;
208 if (queue->num_planes == 2)
209 buffer = buffers[index].start[1];
Fritz Koenigcdba6532021-01-22 16:11:17 -0800210 else
Miguel Casas266b2e42021-02-05 22:05:58 -0500211 buffer += queue->encoded_width * queue->encoded_height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800212
213 if (fread(buffer, frame_size, 1, fp) != 1) {
214 fprintf(stderr, "unable to read chroma frame\n");
215 return -1;
216 }
217
218 struct v4l2_buffer v4l2_buffer;
219 struct v4l2_plane planes[VIDEO_MAX_PLANES];
220 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
221
222 v4l2_buffer.index = index;
223 v4l2_buffer.type = queue->type;
224 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
225 v4l2_buffer.length = queue->num_planes;
226 v4l2_buffer.timestamp.tv_sec = 0;
227 v4l2_buffer.timestamp.tv_usec = queue->frame_cnt;
228 v4l2_buffer.m.planes = planes;
229 for (uint32_t i = 0; i < queue->num_planes; ++i) {
230 v4l2_buffer.m.planes[i].length = buffers[index].length[i];
231 v4l2_buffer.m.planes[i].bytesused = buffers[index].length[i];
232 v4l2_buffer.m.planes[i].data_offset = 0;
233 }
234
235 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
236 if (ret != 0) {
237 perror("VIDIOC_QBUF failed");
238 return -1;
239 }
240
241 queue->frame_cnt++;
242
243 return 0;
244}
245
Miguel Casas8ed87472021-02-05 22:12:07 -0500246// This function copies the contents pointed by |fp| to |queue|s |index| buffer.
247// It's assumed that
248int submit_raw_frame_row_by_row(FILE *fp, uint32_t file_format, struct queue *queue, uint32_t index)
249{
250 assert(queue->raw_width != queue->encoded_width);
251 assert(queue->num_planes == 1 || queue->num_planes == 2);
252 assert(queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') ||
253 queue->fourcc == v4l2_fourcc('N', 'V', '1', '2'));
254 assert(file_format == v4l2_fourcc('Y', 'V', '1', '2') ||
255 file_format == v4l2_fourcc('N', 'V', '1', '2'));
256
257 struct mmap_buffers *buffers = queue->buffers;
258
259 // Read Y plane first, row by row.
260 uint8_t *buffer = buffers[index].start[0];
261 for (int row = 0; row < queue->raw_height; ++row) {
262 if (fread(buffer, queue->raw_width, 1, fp) != 1) {
263 fprintf(stderr, "unable to read luma row\n");
264 return -1;
265 }
266 buffer += queue->encoded_width;
267 }
268
269 if (queue->num_planes == 2)
270 buffer = buffers[index].start[1];
271 else
272 buffer = buffers[index].start[0] + queue->encoded_width * queue->encoded_height;
273
274 // Now read the U and V planes.
275 if (queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') &&
276 file_format == v4l2_fourcc('Y', 'V', '1', '2')) {
277 printf("copying YV12 to YV12\n");
278 for (int row = 0; row < queue->raw_height / 4; ++row) {
279 if (fread(buffer, queue->raw_width, 1, fp) != 1) {
280 fprintf(stderr, "unable to read chroma row\n");
281 return -1;
282 }
283 buffer += queue->encoded_width;
284 }
285
286 if (queue->num_planes == 2) {
287 buffer = buffers[index].start[1] +
288 queue->encoded_width * queue->encoded_height / 4;
289 } else {
290 buffer = buffers[index].start[0] +
291 5 * queue->encoded_width * queue->encoded_height / 4;
292 }
293
294 for (int row = 0; row < queue->raw_height / 4; ++row) {
295 if (fread(buffer, queue->raw_width, 1, fp) != 1) {
296 fprintf(stderr, "unable to read chroma row\n");
297 return -1;
298 }
299 buffer += queue->encoded_width;
300 }
301 } else if (queue->fourcc == v4l2_fourcc('N', 'V', '1', '2') &&
302 file_format == v4l2_fourcc('Y', 'V', '1', '2') && queue->num_planes == 1) {
303 const int kNumPlanes = 2u;
304 for (int plane = 0; plane < kNumPlanes; ++plane) {
305 // Copy all chroma samples from |fp| one by one in even |buffer| positions,
306 // then rewind |buffer|, move it one position right and copy from |fp| into
307 // the odd |buffer| positions.
308 for (int row = 0; row < queue->raw_height / 4; ++row) {
309 for (int col = 0; col < queue->raw_width / 2; ++col) {
310 if (fread(buffer, 1 /*size */, 1 /*nmemb*/, fp) != 1) {
311 fprintf(stderr, "unable to read chroma byte\n");
312 return -1;
313 }
314 buffer += 2;
315 }
316 buffer += queue->encoded_width - queue->raw_width;
317
318 for (int col = 0; col < queue->raw_width / 2; ++col) {
319 if (fread(buffer, 1 /*size */, 1 /*nmemb*/, fp) != 1) {
320 fprintf(stderr, "unable to read chroma byte\n");
321 return -1;
322 }
323 buffer += 2;
324 }
325 buffer += queue->encoded_width - queue->raw_width;
326 }
327 // Rewind |buffer| to start writing the other Chroma samples.
328 buffer -= queue->encoded_width * queue->raw_height / 2;
329 buffer++; }
330
331 } else {
332 fprintf(
333 stderr,
334 "combination of queue format, number of planes, and file format unsupported\n");
335 return -1;
336 }
337
338 struct v4l2_buffer v4l2_buffer;
339 struct v4l2_plane planes[VIDEO_MAX_PLANES];
340 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
341
342 v4l2_buffer.index = index;
343 v4l2_buffer.type = queue->type;
344 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
345 v4l2_buffer.length = queue->num_planes;
346 v4l2_buffer.timestamp.tv_sec = 0;
347 v4l2_buffer.timestamp.tv_usec = queue->frame_cnt;
348 v4l2_buffer.m.planes = planes;
349 for (uint32_t i = 0; i < queue->num_planes; ++i) {
350 v4l2_buffer.m.planes[i].length = buffers[index].length[i];
351 v4l2_buffer.m.planes[i].bytesused = buffers[index].length[i];
352 v4l2_buffer.m.planes[i].data_offset = 0;
353 }
354
355 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
356 if (ret != 0) {
357 perror("VIDIOC_QBUF failed");
358 return -1;
359 }
360
361 queue->frame_cnt++;
362
363 return 0;
364}
365
Miguel Casas266b2e42021-02-05 22:05:58 -0500366// This function copies the content of |fp| into the |index|th buffer of
367// |queue|. Depending on |file_format| and the |queue| format, and the raw and
368// encoded sizes of the latter, we might do a copy in bulk or need conversion.
369int submit_raw_frame(FILE *fp, uint32_t file_format, struct queue *queue, uint32_t index)
370{
371 if (queue->raw_width == queue->encoded_width && queue->fourcc == file_format &&
372 queue->fourcc == v4l2_fourcc('N', 'V', '1', '2')) {
373 return submit_raw_frame_in_bulk(fp, queue, index);
374 }
375
Miguel Casas8ed87472021-02-05 22:12:07 -0500376 return submit_raw_frame_row_by_row(fp, file_format, queue, index);
Miguel Casas266b2e42021-02-05 22:05:58 -0500377}
378
Fritz Koenigcdba6532021-01-22 16:11:17 -0800379void cleanup_queue(struct queue *queue)
380{
381 if (queue->cnt) {
382 struct mmap_buffers *buffers = queue->buffers;
383
384 for (uint32_t i = 0; i < queue->cnt; i++)
385 for (uint32_t j = 0; j < queue->num_planes; j++) {
386 munmap(buffers[i].start[j], buffers[i].length[j]);
387 }
388
389 free(queue->buffers);
390 queue->cnt = 0;
391 }
392}
393
394int request_mmap_buffers(struct queue *queue, struct v4l2_requestbuffers *reqbuf)
395{
396 const int v4lfd = queue->v4lfd;
397 const uint32_t buffer_alloc = reqbuf->count * sizeof(struct mmap_buffers);
398 struct mmap_buffers *buffers = (struct mmap_buffers *)malloc(buffer_alloc);
399 memset(buffers, 0, buffer_alloc);
400 queue->buffers = buffers;
401 queue->cnt = reqbuf->count;
402
403 int ret;
404 for (uint32_t i = 0; i < reqbuf->count; i++) {
405 struct v4l2_buffer buffer;
406 struct v4l2_plane planes[VIDEO_MAX_PLANES];
407 memset(&buffer, 0, sizeof(buffer));
408 buffer.type = reqbuf->type;
409 buffer.memory = V4L2_MEMORY_MMAP;
410 buffer.index = i;
411 buffer.length = queue->num_planes;
412 buffer.m.planes = planes;
413 ret = ioctl(v4lfd, VIDIOC_QUERYBUF, &buffer);
414 if (ret != 0) {
415 printf("VIDIOC_QUERYBUF failed: %d\n", ret);
416 break;
417 }
418
419 for (uint32_t j = 0; j < queue->num_planes; j++) {
420 buffers[i].length[j] = buffer.m.planes[j].length;
421 buffers[i].start[j] =
422 mmap(NULL, buffer.m.planes[j].length, PROT_READ | PROT_WRITE,
423 MAP_SHARED, v4lfd, buffer.m.planes[j].m.mem_offset);
424 if (MAP_FAILED == buffers[i].start[j]) {
425 fprintf(stderr,
426 "failed to mmap buffer of length(%d) and offset(0x%x)\n",
427 buffer.m.planes[j].length, buffer.m.planes[j].m.mem_offset);
428 }
429 }
430 }
431
432 return ret;
433}
434
435int queue_CAPTURE_buffer(struct queue *queue, uint32_t index)
436{
437 struct mmap_buffers *buffers = queue->buffers;
438 struct v4l2_buffer v4l2_buffer;
439 struct v4l2_plane planes[VIDEO_MAX_PLANES];
440 memset(&v4l2_buffer, 0, sizeof v4l2_buffer);
441 memset(&planes, 0, sizeof planes);
442
443 v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
444 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
445 v4l2_buffer.index = index;
446 v4l2_buffer.m.planes = planes;
447 v4l2_buffer.length = queue->num_planes;
448
449 v4l2_buffer.m.planes[0].length = buffers[index].length[0];
450 v4l2_buffer.m.planes[0].bytesused = buffers[index].length[0];
451 v4l2_buffer.m.planes[0].data_offset = 0;
452
453 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
454 if (ret != 0) {
455 perror("VIDIOC_QBUF failed");
456 }
457
458 return ret;
459}
460
461// 4.5.2.5. Initialization
462int Initialization(struct queue *OUTPUT_queue, struct queue *CAPTURE_queue, uint32_t framerate)
463{
464 int ret = 0;
465
466 // 1. Set the coded format on the CAPTURE queue via VIDIOC_S_FMT().
467 if (!ret) {
468 struct v4l2_format fmt;
469 memset(&fmt, 0, sizeof(fmt));
470
471 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
472 fmt.fmt.pix_mp.pixelformat = CAPTURE_queue->fourcc;
473 fmt.fmt.pix_mp.width = CAPTURE_queue->raw_width;
474 fmt.fmt.pix_mp.height = CAPTURE_queue->raw_height;
475 fmt.fmt.pix_mp.plane_fmt[0].sizeimage = kInputbufferMaxSize;
476 fmt.fmt.pix_mp.num_planes = 1;
477
478 int ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_S_FMT, &fmt);
479 if (ret != 0)
480 perror("VIDIOC_S_FMT failed");
481
482 CAPTURE_queue->encoded_width = fmt.fmt.pix_mp.width;
483 CAPTURE_queue->encoded_height = fmt.fmt.pix_mp.height;
484 }
485
486 // 3. Set the raw source format on the OUTPUT queue via VIDIOC_S_FMT().
487 if (!ret) {
488 struct v4l2_format fmt;
489 memset(&fmt, 0, sizeof(fmt));
490
491 fmt.type = OUTPUT_queue->type;
492 fmt.fmt.pix_mp.pixelformat = OUTPUT_queue->fourcc;
493 fmt.fmt.pix_mp.width = OUTPUT_queue->raw_width;
494 fmt.fmt.pix_mp.height = OUTPUT_queue->raw_height;
495 fmt.fmt.pix_mp.num_planes = 1;
496
497 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_FMT, &fmt);
498 if (ret != 0)
499 perror("VIDIOC_S_FMT failed");
500
501 OUTPUT_queue->encoded_width = fmt.fmt.pix_mp.width;
502 OUTPUT_queue->encoded_height = fmt.fmt.pix_mp.height;
503
504 OUTPUT_queue->num_planes = fmt.fmt.pix_mp.num_planes;
505 }
506
507 // 4. Set the raw frame interval on the OUTPUT queue via VIDIOC_S_PARM()
508 if (!ret) {
509 struct v4l2_streamparm parms;
510 memset(&parms, 0, sizeof(parms));
511 parms.type = OUTPUT_queue->type;
512 // Note that we are provided "frames per second" but V4L2 expects "time per
513 // frame"; hence we provide the reciprocal of the framerate here.
514 parms.parm.output.timeperframe.numerator = 1;
515 parms.parm.output.timeperframe.denominator = framerate;
516
517 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_PARM, &parms);
518 if (ret != 0)
519 perror("VIDIOC_S_PARAM failed");
520 }
521
522 // 6. Optional. Set the visible resolution for the stream metadata via
523 // VIDIOC_S_SELECTION() on the OUTPUT queue if it is desired to be
524 // different than the full OUTPUT resolution.
525 if (!ret) {
526 struct v4l2_selection selection_arg;
527 memset(&selection_arg, 0, sizeof(selection_arg));
528 selection_arg.type = OUTPUT_queue->type;
529 selection_arg.target = V4L2_SEL_TGT_CROP;
530 selection_arg.r.left = 0;
531 selection_arg.r.top = 0;
532 selection_arg.r.width = OUTPUT_queue->raw_width;
533 selection_arg.r.height = OUTPUT_queue->raw_height;
534
535 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_SELECTION, &selection_arg);
536
537 if (ret != 0)
538 perror("VIDIOC_S_SELECTION failed");
539
540 // TODO(fritz) : check returned values are same as sent values
541 }
542
543 // 7. Allocate buffers for both OUTPUT and CAPTURE via VIDIOC_REQBUFS().
544 // This may be performed in any order.
545 if (!ret) {
546 struct v4l2_requestbuffers reqbuf;
547 memset(&reqbuf, 0, sizeof(reqbuf));
548 reqbuf.count = kRequestBufferCount;
549 reqbuf.type = OUTPUT_queue->type;
550 reqbuf.memory = V4L2_MEMORY_MMAP;
551
552 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
553 if (ret != 0)
554 perror("VIDIOC_REQBUFS failed");
555
556 printf("%d buffers requested, %d buffers for uncompressed frames returned\n",
557 kRequestBufferCount, reqbuf.count);
558
559 ret = request_mmap_buffers(OUTPUT_queue, &reqbuf);
560 }
561
562 if (!ret) {
563 struct v4l2_requestbuffers reqbuf;
564 memset(&reqbuf, 0, sizeof(reqbuf));
565 reqbuf.count = kRequestBufferCount;
566 reqbuf.type = CAPTURE_queue->type;
567 reqbuf.memory = V4L2_MEMORY_MMAP;
568
569 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
570 if (ret != 0)
571 perror("VIDIOC_REQBUFS failed");
572
573 printf("%d buffers requested, %d buffers for compressed frames returned\n",
574 kRequestBufferCount, reqbuf.count);
575
576 ret = request_mmap_buffers(CAPTURE_queue, &reqbuf);
577 for (uint32_t i = 0; i < reqbuf.count; i++) {
578 queue_CAPTURE_buffer(CAPTURE_queue, i);
579 }
580 }
581
582 return ret;
583}
584
585int configure_h264(int v4lfd, struct encoder_cfg *cfg)
586{
587 int ret = 0;
588 const int kH264CtrlCnt = 4;
589
590 struct v4l2_ext_control ext_ctrl[kH264CtrlCnt];
591 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
592
593 ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_H264_PROFILE;
594 ext_ctrl[0].value = cfg->h264_profile;
595
596 ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
597 ext_ctrl[1].value = cfg->h264_level;
598
599 ext_ctrl[2].id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE;
600 ext_ctrl[2].value = cfg->h264_entropy_mode;
601
602 ext_ctrl[3].id = V4L2_CID_MPEG_VIDEO_HEADER_MODE;
603 ext_ctrl[3].value = cfg->header_mode;
604
605 struct v4l2_ext_controls ext_ctrls;
606 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
607
608 ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
609 ext_ctrls.count = kH264CtrlCnt;
610 ext_ctrls.controls = ext_ctrl;
611
612 ret = ioctl(v4lfd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
613
614 if (ret != 0)
615 perror("VIDIOC_S_EXT_CTRLS failed");
616
617 for (uint32_t i = 0; i < kH264CtrlCnt; ++i)
618 ext_ctrl[i].value = 0;
619
620 ret = ioctl(v4lfd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
621 if (ret != 0)
622 perror("VIDIOC_G_EXT_CTRLS failed");
623
624 if (ext_ctrl[0].value != cfg->h264_profile)
625 fprintf(stderr, "requested profile(%d) was not used, using (%d) instead.\n",
626 cfg->h264_profile, ext_ctrl[0].value);
627
628 if (ext_ctrl[1].value != cfg->h264_level)
629 fprintf(stderr, "requested level(%d) was not used, using (%d) instead.\n",
630 cfg->h264_level, ext_ctrl[1].value);
631
632 if (ext_ctrl[2].value != cfg->h264_entropy_mode)
633 fprintf(stderr, "requested entropy mode(%d) was not used, using (%d) instead.\n",
634 cfg->h264_entropy_mode, ext_ctrl[2].value);
635
636 if (ext_ctrl[3].value != cfg->header_mode)
637 fprintf(stderr, "requested entropy mode(%d) was not used, using (%d) instead.\n",
638 cfg->header_mode, ext_ctrl[3].value);
639
640 return ret;
641}
642
643int configure_common(int v4lfd, struct encoder_cfg *cfg)
644{
645 int ret = 0;
646 const int kCommonCtrlCnt = 5;
647
648 struct v4l2_ext_control ext_ctrl[kCommonCtrlCnt];
649 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
650
651 ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_BITRATE;
652 ext_ctrl[0].value = cfg->bitrate;
653
654 ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK;
655 ext_ctrl[1].value = cfg->bitrate * 2;
656
657 ext_ctrl[2].id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
658 ext_ctrl[2].value = cfg->gop_size;
659
660 ext_ctrl[3].id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE;
661 ext_ctrl[3].value = 1;
662
663 ext_ctrl[4].id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE;
664 ext_ctrl[4].value = cfg->bitrate_mode;
665
666 struct v4l2_ext_controls ext_ctrls;
667 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
668
669 ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
670 ext_ctrls.count = kCommonCtrlCnt;
671 ext_ctrls.controls = ext_ctrl;
672
673 ret = ioctl(v4lfd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
674
675 if (ret != 0)
676 perror("VIDIOC_S_EXT_CTRLS failed");
677
678 for (uint32_t i = 0; i < kCommonCtrlCnt; ++i)
679 ext_ctrl[i].value = 0;
680
681 ret = ioctl(v4lfd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
682 if (ret != 0)
683 perror("VIDIOC_G_EXT_CTRLS failed");
684
685 if (ext_ctrl[0].value != cfg->bitrate)
686 fprintf(stderr,
687 "requested bitrate(%d) was outside of the limit, using (%d) instead.\n",
688 cfg->bitrate, ext_ctrl[0].value);
689
690 if (ext_ctrl[1].value != cfg->bitrate * 2)
691 fprintf(
692 stderr,
693 "requested bitrate peak(%d) was outside of the limit, using (%d) instead.\n",
694 cfg->bitrate * 2, ext_ctrl[1].value);
695
696 if (ext_ctrl[2].value != cfg->gop_size)
697 fprintf(stderr, "requested gop size(%d) was not used, using (%d) instead.\n",
698 cfg->gop_size, ext_ctrl[2].value);
699
700 if (ext_ctrl[3].value != 1)
701 fprintf(stderr,
702 "requested frame rate control (%d) was not used, using (%d) instead.\n", 1,
703 ext_ctrl[3].value);
704
705 if (ext_ctrl[4].value != cfg->bitrate_mode)
706 fprintf(stderr, "requested bitrate mode(%d) was not used, using (%d) instead.\n",
707 cfg->bitrate_mode, ext_ctrl[4].value);
708
709 return ret;
710}
711
712int dequeue_buffer(struct queue *queue, uint32_t *index, uint32_t *bytesused, uint32_t *data_offset,
713 uint64_t *timestamp)
714{
715 struct v4l2_buffer v4l2_buffer;
716 struct v4l2_plane planes[VIDEO_MAX_PLANES] = { 0 };
717 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
718 v4l2_buffer.type = queue->type;
719 v4l2_buffer.length = queue->num_planes;
720 v4l2_buffer.m.planes = planes;
721 v4l2_buffer.m.planes[0].bytesused = 0;
722 int ret = ioctl(queue->v4lfd, VIDIOC_DQBUF, &v4l2_buffer);
723
724 if (ret != 0 && errno != EAGAIN)
725 perror("VIDIOC_DQBUF failed");
726
727 *index = v4l2_buffer.index;
728 if (bytesused)
729 *bytesused = v4l2_buffer.m.planes[0].bytesused;
730 if (data_offset)
731 *data_offset = v4l2_buffer.m.planes[0].data_offset;
732 if (timestamp)
733 *timestamp = v4l2_buffer.timestamp.tv_usec;
734 return ret;
735}
736
Miguel Casas266b2e42021-02-05 22:05:58 -0500737int encode(FILE *fp_input, uint32_t file_format, struct queue *OUTPUT_queue, struct queue *CAPTURE_queue,
Fritz Koenigcdba6532021-01-22 16:11:17 -0800738 uint32_t frames_to_decode)
739{
740 int ret = 0;
741 char output_file_name[256];
742 int use_ivf = 0;
743
Miguel Casas266b2e42021-02-05 22:05:58 -0500744 if (OUTPUT_queue->num_planes == 0 || OUTPUT_queue->num_planes > 2) {
745 fprintf(stderr, " unsupported number of planes: %d\n", OUTPUT_queue->num_planes);
746 return -1;
747 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800748 fprintf(stderr, "encoding\n");
749
750 if (CAPTURE_queue->fourcc == v4l2_fourcc('V', 'P', '8', '0'))
751 use_ivf = 1;
752
753 sprintf(output_file_name, "output%s", use_ivf ? ".ivf" : ".h264");
754 FILE *fp_output = fopen(output_file_name, "wb");
755 if (!fp_output) {
756 fprintf(stderr, "unable to write to file: %s\n", output_file_name);
757 ret = 1;
758 }
759
760 // write header
761 if (use_ivf) {
762 struct ivf_file_header header;
763 header.signature = kIVFHeaderSignature;
764 header.version = 0;
765 header.header_length = sizeof(struct ivf_file_header);
766 header.fourcc = CAPTURE_queue->fourcc;
767 header.width = CAPTURE_queue->raw_width;
768 header.height = CAPTURE_queue->raw_height;
769 // hard coded 30fps
770 header.denominator = 30;
771 header.numerator = 1;
772 header.frame_cnt = frames_to_decode;
773 header.unused = 0;
774
775 if (fwrite(&header, sizeof(struct ivf_file_header), 1, fp_output) != 1) {
776 fprintf(stderr, "unable to write ivf file header\n");
777 }
778 }
779
Miguel Casas0a13d7a2021-02-05 22:15:06 -0500780 struct timespec start, stop;
781 clock_gettime(CLOCK_REALTIME, &start);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800782 if (!ret) {
783 // prime input by filling up the OUTPUT queue with raw frames
784 for (uint32_t i = 0; i < OUTPUT_queue->cnt; ++i) {
Miguel Casas266b2e42021-02-05 22:05:58 -0500785 if (submit_raw_frame(fp_input, file_format, OUTPUT_queue, i)) {
Fritz Koenigcdba6532021-01-22 16:11:17 -0800786 fprintf(stderr, "unable to submit raw frame\n");
787 ret = 1;
788 }
789 }
790 }
791
792 if (!ret) {
793 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_STREAMON, &OUTPUT_queue->type);
794 if (ret != 0)
795 perror("VIDIOC_STREAMON failed on OUTPUT");
796 }
797
798 if (!ret) {
799 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_STREAMON, &CAPTURE_queue->type);
800 if (ret != 0)
801 perror("VIDIOC_STREAMON failed on CAPTURE");
802 }
803
Miguel Casas0a13d7a2021-02-05 22:15:06 -0500804 uint32_t cnt = OUTPUT_queue->cnt; // We pre-uploaded a few before.
Fritz Koenigcdba6532021-01-22 16:11:17 -0800805 if (!ret) {
Fritz Koenigcdba6532021-01-22 16:11:17 -0800806 while (cnt < frames_to_decode) {
807 // handle CAPTURE queue first
808 {
809 uint32_t index = 0;
810 uint32_t bytesused = 0;
811 uint32_t data_offset = 0;
812 uint64_t timestamp = 0;
813
814 // first get the newly encoded frame
815 ret = dequeue_buffer(CAPTURE_queue, &index, &bytesused,
816 &data_offset, &timestamp);
817 if (ret != 0)
818 continue;
819
820 if (use_ivf) {
821 struct ivf_frame_header header;
822 header.size = bytesused - data_offset;
823 header.timestamp = timestamp;
824
825 if (fwrite(&header, sizeof(struct ivf_frame_header), 1,
826 fp_output) != 1) {
827 fprintf(stderr,
828 "unable to write ivf frame header\n");
829 }
830 }
831 fwrite(CAPTURE_queue->buffers[index].start[0] + data_offset,
832 bytesused - data_offset, 1, fp_output);
833
834 // done with the buffer, queue it back up
835 queue_CAPTURE_buffer(CAPTURE_queue, index);
836 }
837
838 // handle OUTPUT queue second
839 {
840 uint32_t index = 0;
841
842 ret = dequeue_buffer(OUTPUT_queue, &index, 0, 0, 0);
843 if (ret != 0)
844 continue;
845
Miguel Casas266b2e42021-02-05 22:05:58 -0500846 if (submit_raw_frame(fp_input, file_format, OUTPUT_queue, index))
Fritz Koenigcdba6532021-01-22 16:11:17 -0800847 break;
848 }
849 cnt++;
850 }
851 }
Miguel Casas0a13d7a2021-02-05 22:15:06 -0500852 clock_gettime(CLOCK_REALTIME, &stop);
853 const double elapsed_ns =
854 (stop.tv_sec - start.tv_sec) * 1e9 + (stop.tv_nsec - start.tv_nsec);
855 const double fps = cnt * 1e9 / elapsed_ns;
856 printf("%d frames encoded in %fns (%ffps)\n", cnt, elapsed_ns, fps);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800857
858 if (fp_output) {
859 fclose(fp_output);
860 }
861 return ret;
862}
863
864static void print_help(const char *argv0)
865{
866 printf("usage: %s [OPTIONS]\n", argv0);
Miguel Casas266b2e42021-02-05 22:05:58 -0500867 printf(" -f, --file file to encode\n");
868 printf(" -i, --file_format pixel format of the file (yv12, nv12)\n");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800869 printf(" -w, --width width of image\n");
870 printf(" -h, --height height of image\n");
871 printf(" -m, --max max number of frames to decode\n");
872 printf(" -r, --rate frames per second\n");
873 printf(" -b, --bitrate bits per second\n");
874 printf(" -g, --gop gop length\n");
875 printf(" -c, --codec codec\n");
Miguel Casase4c6efd2021-02-16 22:10:05 -0500876 printf(" -e, --end_usage rate control mode: VBR (default), CBR\n");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800877 printf(" -v, --verbose verbose capabilities\n");
Miguel Casas266b2e42021-02-05 22:05:58 -0500878 printf(" -q, --buffer_fmt OUTPUT queue format\n");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800879}
880
881static const struct option longopts[] = {
882 { "file", required_argument, NULL, 'f' },
Miguel Casas266b2e42021-02-05 22:05:58 -0500883 { "file_format", required_argument, NULL, 'i' },
Fritz Koenigcdba6532021-01-22 16:11:17 -0800884 { "width", required_argument, NULL, 'w' },
885 { "height", required_argument, NULL, 'h' },
886 { "max", required_argument, NULL, 'm' },
887 { "rate", required_argument, NULL, 'r' },
888 { "bitrate", required_argument, NULL, 'b' },
889 { "gop", required_argument, NULL, 'g' },
890 { "codec", required_argument, NULL, 'c' },
Miguel Casase4c6efd2021-02-16 22:10:05 -0500891 { "end_usage", required_argument, NULL, 'e' },
Fritz Koenigcdba6532021-01-22 16:11:17 -0800892 { "verbose", no_argument, NULL, 'v' },
Miguel Casas266b2e42021-02-05 22:05:58 -0500893 { "buffer_fmt", required_argument, NULL, 'q' },
Fritz Koenigcdba6532021-01-22 16:11:17 -0800894 { 0, 0, 0, 0 },
895};
896
897int main(int argc, char *argv[])
898{
Miguel Casas266b2e42021-02-05 22:05:58 -0500899 uint32_t file_format = v4l2_fourcc('N', 'V', '1', '2');
Fritz Koenigcdba6532021-01-22 16:11:17 -0800900 uint32_t OUTPUT_format = v4l2_fourcc('N', 'V', '1', '2');
901 uint32_t CAPTURE_format = v4l2_fourcc('H', '2', '6', '4');
902 char *file_name = NULL;
903 uint32_t width = 0;
904 uint32_t height = 0;
905 uint32_t frames_to_decode = 0;
906 uint32_t framerate = 30;
907 int verbose_capabilities = 0;
908 int c;
909
910 struct encoder_cfg cfg = { .gop_size = 20,
911 .bitrate = 1000,
912 .h264_entropy_mode = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
913 .h264_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
914 .h264_profile = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
915 .header_mode = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
916 .bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR };
917
Miguel Casase4c6efd2021-02-16 22:10:05 -0500918 while ((c = getopt_long(argc, argv, "f:i:w:h:m:r:b:g:c:e:vq:", longopts, NULL)) != -1) {
Fritz Koenigcdba6532021-01-22 16:11:17 -0800919 switch (c) {
920 case 'f':
921 file_name = strdup(optarg);
922 break;
Miguel Casas266b2e42021-02-05 22:05:58 -0500923 case 'i':
924 if (strlen(optarg) == 4) {
925 file_format =
926 v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
927 toupper(optarg[2]), toupper(optarg[3]));
928 printf("using (%s) as the file format\n", optarg);
929 }
930 break;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800931 case 'w':
932 width = atoi(optarg);
933 break;
934 case 'h':
935 height = atoi(optarg);
936 break;
937 case 'm':
938 frames_to_decode = atoi(optarg);
939 break;
940 case 'r':
941 framerate = atoi(optarg);
942 break;
943 case 'b':
944 cfg.bitrate = atoi(optarg);
945 break;
946 case 'g':
947 cfg.gop_size = atoi(optarg);
948 break;
949 case 'c':
950 if (strlen(optarg) == 4) {
951 CAPTURE_format =
952 v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
953 toupper(optarg[2]), toupper(optarg[3]));
954 printf("using (%s) as the codec\n", optarg);
955 }
956 break;
Miguel Casase4c6efd2021-02-16 22:10:05 -0500957 case 'e':
958 if (strlen(optarg) == 3 && toupper(optarg[0]) == 'C' &&
959 toupper(optarg[1]) == 'B' && toupper(optarg[2]) == 'R') {
960 cfg.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
961 }
962 break;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800963 case 'v':
964 verbose_capabilities = 1;
965 break;
Miguel Casas266b2e42021-02-05 22:05:58 -0500966 case 'q':
Fritz Koenigcdba6532021-01-22 16:11:17 -0800967 if (strlen(optarg) == 4) {
968 OUTPUT_format =
969 v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
970 toupper(optarg[2]), toupper(optarg[3]));
971 printf("using (%s) as the OUTPUT queue buffer format\n",
972 optarg);
973 }
974 break;
975 default:
976 break;
977 }
978 }
979
980 if (!file_name || width == 0 || height == 0) {
981 fprintf(stderr, "Invalid parameters!\n");
982 print_help(argv[0]);
983 exit(1);
984 }
985
986 FILE *fp = fopen(file_name, "rb");
987 if (!fp) {
988 fprintf(stderr, "%s: unable to open file.\n", file_name);
989 exit(1);
990 }
991
992 if (!frames_to_decode) {
993 fseek(fp, 0, SEEK_END);
994 uint64_t length = ftell(fp);
995 uint32_t frame_size = (3 * width * height) >> 1;
996 frames_to_decode = length / frame_size;
997 fseek(fp, 0, SEEK_SET);
998 }
999
Miguel Casase4c6efd2021-02-16 22:10:05 -05001000 fprintf(stderr, "encoding %d frames using %s bitrate control\n",
1001 frames_to_decode,
1002 (cfg.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) ? "CBR" : "VBR");
Fritz Koenigcdba6532021-01-22 16:11:17 -08001003
1004 int v4lfd = open(kEncodeDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC);
1005 if (v4lfd < 0) {
1006 fprintf(stderr, "Unable to open device file: %s\n", kEncodeDevice);
1007 exit(EXIT_FAILURE);
1008 }
1009
1010 if (capabilities(v4lfd, OUTPUT_format, CAPTURE_format, verbose_capabilities) != 0) {
1011 fprintf(stderr, "Capabilities not present for encode.\n");
1012 exit(EXIT_FAILURE);
1013 }
1014
1015 struct queue OUTPUT_queue = { .v4lfd = v4lfd,
1016 .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
1017 .fourcc = OUTPUT_format,
1018 .raw_width = width,
1019 .raw_height = height,
1020 .frame_cnt = 0,
1021 .num_planes = 1 };
1022
1023 struct queue CAPTURE_queue = { .v4lfd = v4lfd,
1024 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
1025 .fourcc = CAPTURE_format,
1026 .raw_width = width,
1027 .raw_height = height,
1028 .num_planes = 1 };
1029
1030 int ret = Initialization(&OUTPUT_queue, &CAPTURE_queue, framerate);
1031
1032 // not all configurations are supported, so we don't need to track
1033 // the return value
1034 if (!ret) {
1035 configure_common(v4lfd, &cfg);
1036
1037 if (v4l2_fourcc('H', '2', '6', '4') == CAPTURE_format)
1038 configure_h264(v4lfd, &cfg);
1039 }
1040
1041 if (!ret)
Miguel Casas266b2e42021-02-05 22:05:58 -05001042 ret = encode(fp, file_format, &OUTPUT_queue, &CAPTURE_queue, frames_to_decode);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001043
1044 cleanup_queue(&OUTPUT_queue);
1045 cleanup_queue(&CAPTURE_queue);
1046 close(v4lfd);
1047
1048 return 0;
1049}