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