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