blob: 93c2d7fd02550bb68b1e31456c1c30f2ff274bbb [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
Steve Chodc8f92a2021-03-03 17:43:38 -080024static 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 {
Steve Chodc8f92a2021-03-03 17:43:38 -080030 void* start[VIDEO_MAX_PLANES];
31 size_t length[VIDEO_MAX_PLANES];
32 struct gbm_bo* bo;
Fritz Koenigcdba6532021-01-22 16:11:17 -080033};
34
35struct queue {
Steve Chodc8f92a2021-03-03 17:43:38 -080036 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 uint32_t framerate;
Fritz Koenigcdba6532021-01-22 16:11:17 -080048};
49
50struct encoder_cfg {
Steve Chodc8f92a2021-03-03 17:43:38 -080051 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;
Fritz Koenigcdba6532021-01-22 16:11:17 -080058};
59
60struct ivf_file_header {
Steve Chodc8f92a2021-03-03 17:43:38 -080061 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;
Fritz Koenigcdba6532021-01-22 16:11:17 -080071} __attribute__((packed));
72
73struct ivf_frame_header {
Steve Chodc8f92a2021-03-03 17:43:38 -080074 uint32_t size;
75 uint64_t timestamp;
Fritz Koenigcdba6532021-01-22 16:11:17 -080076} __attribute__((packed));
77
Steve Chodc8f92a2021-03-03 17:43:38 -080078void print_fourcc(uint32_t fourcc) {
79 printf("%c%c%c%c\n", fourcc & 0xff, fourcc >> 8 & 0xff, fourcc >> 16 & 0xff,
80 fourcc >> 24 & 0xff);
Fritz Koenigcdba6532021-01-22 16:11:17 -080081}
82
Steve Chodc8f92a2021-03-03 17:43:38 -080083int query_format(int v4lfd, enum v4l2_buf_type type, uint32_t fourcc) {
84 struct v4l2_fmtdesc fmtdesc;
85 memset(&fmtdesc, 0, sizeof(fmtdesc));
Fritz Koenigcdba6532021-01-22 16:11:17 -080086
Steve Chodc8f92a2021-03-03 17:43:38 -080087 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 }
Fritz Koenigcdba6532021-01-22 16:11:17 -080095
Steve Chodc8f92a2021-03-03 17:43:38 -080096 return 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -080097}
98
Steve Chodc8f92a2021-03-03 17:43:38 -080099void enumerate_menu(int v4lfd, uint32_t id, uint32_t min, uint32_t max) {
100 struct v4l2_querymenu querymenu;
101 memset(&querymenu, 0, sizeof(querymenu));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800102
Steve Chodc8f92a2021-03-03 17:43:38 -0800103 querymenu.id = id;
104 for (querymenu.index = min; querymenu.index <= max; querymenu.index++) {
105 if (0 == ioctl(v4lfd, VIDIOC_QUERYMENU, &querymenu))
106 fprintf(stderr, " %s\n", querymenu.name);
107 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800108}
109
Steve Chodc8f92a2021-03-03 17:43:38 -0800110int capabilities(int v4lfd,
111 uint32_t OUTPUT_format,
112 uint32_t CAPTURE_format,
113 int verbose_capabilities) {
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");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800119
Steve Chodc8f92a2021-03-03 17:43:38 -0800120 printf("driver=\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x\n", cap.driver,
121 cap.bus_info, cap.card, v4lfd);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800122
Steve Chodc8f92a2021-03-03 17:43:38 -0800123 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 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800128
Steve Chodc8f92a2021-03-03 17:43:38 -0800129 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
130 CAPTURE_format)) {
131 printf("Supported CAPTURE formats:\n");
132 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0);
133 ret = 1;
134 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800135
Steve Chodc8f92a2021-03-03 17:43:38 -0800136 if (verbose_capabilities) {
137 struct v4l2_query_ext_ctrl queryctrl;
138 memset(&queryctrl, 0, sizeof(queryctrl));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800139
Steve Chodc8f92a2021-03-03 17:43:38 -0800140 for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1;
141 queryctrl.id++) {
142 if (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
143 fprintf(
144 stderr, "control %s : %s\n", queryctrl.name,
145 queryctrl.flags & V4L2_CTRL_FLAG_DISABLED ? "disabled" : "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 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800153
Steve Chodc8f92a2021-03-03 17:43:38 -0800154 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;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800158
Steve Chodc8f92a2021-03-03 17:43:38 -0800159 fprintf(stderr, "control %s\n", queryctrl.name);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800160
Steve Chodc8f92a2021-03-03 17:43:38 -0800161 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 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800168
Steve Chodc8f92a2021-03-03 17:43:38 -0800169 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);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800173
Steve Chodc8f92a2021-03-03 17:43:38 -0800174 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
175 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
176 queryctrl.maximum);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800177
Steve Chodc8f92a2021-03-03 17:43:38 -0800178 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;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800185}
186
Steve Chodc8f92a2021-03-03 17:43:38 -0800187int queue_OUTPUT_buffer(struct queue* queue,
188 struct mmap_buffers* buffers,
189 uint32_t index) {
190 // compute frame timestamp
191 const float usec_per_frame = (1.0 / queue->framerate) * 1000000;
192 const uint64_t usec_time_stamp = usec_per_frame * queue->frame_cnt;
193 const uint64_t tv_sec = usec_time_stamp / 1000000;
Fritz Koenig62733512021-02-17 18:36:21 -0800194
Steve Chodc8f92a2021-03-03 17:43:38 -0800195 struct v4l2_buffer v4l2_buffer;
196 struct v4l2_plane planes[VIDEO_MAX_PLANES];
197 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
Fritz Koenig62733512021-02-17 18:36:21 -0800198
Steve Chodc8f92a2021-03-03 17:43:38 -0800199 v4l2_buffer.index = index;
200 v4l2_buffer.type = queue->type;
201 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
202 v4l2_buffer.length = queue->num_planes;
203 v4l2_buffer.timestamp.tv_sec = tv_sec;
204 v4l2_buffer.timestamp.tv_usec = usec_time_stamp - tv_sec;
205 v4l2_buffer.sequence = queue->frame_cnt;
206 v4l2_buffer.m.planes = planes;
207 for (uint32_t i = 0; i < queue->num_planes; ++i) {
208 v4l2_buffer.m.planes[i].length = buffers[index].length[i];
209 v4l2_buffer.m.planes[i].bytesused = buffers[index].length[i];
210 v4l2_buffer.m.planes[i].data_offset = 0;
211 }
Fritz Koenig62733512021-02-17 18:36:21 -0800212
Steve Chodc8f92a2021-03-03 17:43:38 -0800213 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
214 if (ret != 0) {
215 perror("VIDIOC_QBUF failed");
216 return -1;
217 }
Fritz Koenig62733512021-02-17 18:36:21 -0800218
Steve Chodc8f92a2021-03-03 17:43:38 -0800219 queue->frame_cnt++;
Fritz Koenig62733512021-02-17 18:36:21 -0800220
Steve Chodc8f92a2021-03-03 17:43:38 -0800221 return 0;
Fritz Koenig62733512021-02-17 18:36:21 -0800222}
223
Miguel Casas266b2e42021-02-05 22:05:58 -0500224// This function copies the contents pointed by |fp| tp |queue|s |index| buffer.
Steve Chodc8f92a2021-03-03 17:43:38 -0800225int submit_raw_frame_in_bulk(FILE* fp, struct queue* queue, uint32_t index) {
226 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
Steve Chodc8f92a2021-03-03 17:43:38 -0800232 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800233
Steve Chodc8f92a2021-03-03 17:43:38 -0800234 // read y plane first
235 size_t frame_size = queue->raw_width * queue->raw_height;
236 uint8_t* buffer = buffers[index].start[0];
Fritz Koenigcdba6532021-01-22 16:11:17 -0800237
Steve Chodc8f92a2021-03-03 17:43:38 -0800238 if (fread(buffer, frame_size, 1, fp) != 1) {
239 fprintf(stderr, "unable to read luma frame\n");
240 return -1;
241 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800242
Steve Chodc8f92a2021-03-03 17:43:38 -0800243 // now read uv
244 frame_size >>= 1;
245 if (queue->num_planes == 2)
246 buffer = buffers[index].start[1];
247 else
248 buffer += queue->encoded_width * queue->encoded_height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800249
Steve Chodc8f92a2021-03-03 17:43:38 -0800250 if (fread(buffer, frame_size, 1, fp) != 1) {
251 fprintf(stderr, "unable to read chroma frame\n");
252 return -1;
253 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800254
Steve Chodc8f92a2021-03-03 17:43:38 -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
Steve Chodc8f92a2021-03-03 17:43:38 -0800260int submit_raw_frame_row_by_row(FILE* fp,
261 uint32_t file_format,
262 struct queue* queue,
263 uint32_t index) {
264 assert(queue->num_planes == 1 || queue->num_planes == 2);
265 assert(queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') ||
266 queue->fourcc == v4l2_fourcc('N', 'V', '1', '2'));
267 assert(file_format == v4l2_fourcc('Y', 'V', '1', '2') ||
268 file_format == v4l2_fourcc('N', 'V', '1', '2'));
Miguel Casas8ed87472021-02-05 22:12:07 -0500269
Steve Chodc8f92a2021-03-03 17:43:38 -0800270 struct mmap_buffers* buffers = queue->buffers;
Miguel Casas8ed87472021-02-05 22:12:07 -0500271
Steve Chodc8f92a2021-03-03 17:43:38 -0800272 // Read Y plane first, row by row.
273 uint8_t* buffer = buffers[index].start[0];
274 for (int row = 0; row < queue->raw_height; ++row) {
275 if (fread(buffer, queue->raw_width, 1, fp) != 1) {
276 fprintf(stderr, "unable to read luma row\n");
277 return -1;
278 }
279 buffer += queue->encoded_width;
280 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500281
Steve Chodc8f92a2021-03-03 17:43:38 -0800282 if (queue->num_planes == 2)
283 buffer = buffers[index].start[1];
284 else
285 buffer =
286 buffers[index].start[0] + queue->encoded_width * queue->encoded_height;
Miguel Casas8ed87472021-02-05 22:12:07 -0500287
Steve Chodc8f92a2021-03-03 17:43:38 -0800288 // Now read the U and V planes.
289 if (queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') &&
290 file_format == v4l2_fourcc('Y', 'V', '1', '2')) {
291 printf("copying YV12 to YV12\n");
292 for (int row = 0; row < queue->raw_height / 4; ++row) {
293 if (fread(buffer, queue->raw_width, 1, fp) != 1) {
294 fprintf(stderr, "unable to read chroma row\n");
295 return -1;
296 }
297 buffer += queue->encoded_width;
298 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500299
Steve Chodc8f92a2021-03-03 17:43:38 -0800300 if (queue->num_planes == 2) {
301 buffer = buffers[index].start[1] +
302 queue->encoded_width * queue->encoded_height / 4;
303 } else {
304 buffer = buffers[index].start[0] +
305 5 * queue->encoded_width * queue->encoded_height / 4;
306 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500307
Steve Chodc8f92a2021-03-03 17:43:38 -0800308 for (int row = 0; row < queue->raw_height / 4; ++row) {
309 if (fread(buffer, queue->raw_width, 1, fp) != 1) {
310 fprintf(stderr, "unable to read chroma row\n");
311 return -1;
312 }
313 buffer += queue->encoded_width;
314 }
315 } else if (queue->fourcc == v4l2_fourcc('N', 'V', '1', '2') &&
316 file_format == v4l2_fourcc('Y', 'V', '1', '2') &&
317 queue->num_planes == 1) {
318 const int kNumPlanes = 2u;
319 for (int plane = 0; plane < kNumPlanes; ++plane) {
320 // Copy all chroma samples from |fp| one by one in even |buffer|
321 // positions, then rewind |buffer|, move it one position right and copy
322 // from |fp| into the odd |buffer| positions.
323 for (int row = 0; row < queue->raw_height / 4; ++row) {
324 for (int col = 0; col < queue->raw_width / 2; ++col) {
325 if (fread(buffer, 1 /*size */, 1 /*nmemb*/, fp) != 1) {
326 fprintf(stderr, "unable to read chroma byte\n");
327 return -1;
328 }
329 buffer += 2;
330 }
331 buffer += queue->encoded_width - queue->raw_width;
Miguel Casas8ed87472021-02-05 22:12:07 -0500332
Steve Chodc8f92a2021-03-03 17:43:38 -0800333 for (int col = 0; col < queue->raw_width / 2; ++col) {
334 if (fread(buffer, 1 /*size */, 1 /*nmemb*/, fp) != 1) {
335 fprintf(stderr, "unable to read chroma byte\n");
336 return -1;
337 }
338 buffer += 2;
339 }
340 buffer += queue->encoded_width - queue->raw_width;
341 }
342 // Rewind |buffer| to start writing the other Chroma samples.
343 buffer -= queue->encoded_width * queue->raw_height / 2;
344 buffer++;
345 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500346
Steve Chodc8f92a2021-03-03 17:43:38 -0800347 } else {
348 fprintf(stderr,
349 "combination of queue format, number of planes, and file format "
350 "unsupported\n");
351 return -1;
352 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500353
Steve Chodc8f92a2021-03-03 17:43:38 -0800354 return queue_OUTPUT_buffer(queue, buffers, index);
Miguel Casas8ed87472021-02-05 22:12:07 -0500355}
356
Miguel Casas266b2e42021-02-05 22:05:58 -0500357// This function copies the content of |fp| into the |index|th buffer of
358// |queue|. Depending on |file_format| and the |queue| format, and the raw and
359// encoded sizes of the latter, we might do a copy in bulk or need conversion.
Steve Chodc8f92a2021-03-03 17:43:38 -0800360int submit_raw_frame(FILE* fp,
361 uint32_t file_format,
362 struct queue* queue,
363 uint32_t index) {
364 if (queue->raw_width == queue->encoded_width &&
365 queue->fourcc == file_format &&
366 queue->fourcc == v4l2_fourcc('N', 'V', '1', '2')) {
367 return submit_raw_frame_in_bulk(fp, queue, index);
368 }
Miguel Casas266b2e42021-02-05 22:05:58 -0500369
Steve Chodc8f92a2021-03-03 17:43:38 -0800370 return submit_raw_frame_row_by_row(fp, file_format, queue, index);
Miguel Casas266b2e42021-02-05 22:05:58 -0500371}
372
Steve Chodc8f92a2021-03-03 17:43:38 -0800373void cleanup_queue(struct queue* queue) {
374 if (queue->cnt) {
375 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800376
Steve Chodc8f92a2021-03-03 17:43:38 -0800377 for (uint32_t i = 0; i < queue->cnt; i++)
378 for (uint32_t j = 0; j < queue->num_planes; j++) {
379 munmap(buffers[i].start[j], buffers[i].length[j]);
380 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800381
Steve Chodc8f92a2021-03-03 17:43:38 -0800382 free(queue->buffers);
383 queue->cnt = 0;
384 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800385}
386
Steve Chodc8f92a2021-03-03 17:43:38 -0800387int request_mmap_buffers(struct queue* queue,
388 struct v4l2_requestbuffers* reqbuf) {
389 const int v4lfd = queue->v4lfd;
390 const uint32_t buffer_alloc = reqbuf->count * sizeof(struct mmap_buffers);
391 struct mmap_buffers* buffers = (struct mmap_buffers*)malloc(buffer_alloc);
392 memset(buffers, 0, buffer_alloc);
393 queue->buffers = buffers;
394 queue->cnt = reqbuf->count;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800395
Steve Chodc8f92a2021-03-03 17:43:38 -0800396 int ret;
397 for (uint32_t i = 0; i < reqbuf->count; i++) {
398 struct v4l2_buffer buffer;
399 struct v4l2_plane planes[VIDEO_MAX_PLANES];
400 memset(&buffer, 0, sizeof(buffer));
401 buffer.type = reqbuf->type;
402 buffer.memory = V4L2_MEMORY_MMAP;
403 buffer.index = i;
404 buffer.length = queue->num_planes;
405 buffer.m.planes = planes;
406 ret = ioctl(v4lfd, VIDIOC_QUERYBUF, &buffer);
407 if (ret != 0) {
408 printf("VIDIOC_QUERYBUF failed: %d\n", ret);
409 break;
410 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800411
Steve Chodc8f92a2021-03-03 17:43:38 -0800412 for (uint32_t j = 0; j < queue->num_planes; j++) {
413 buffers[i].length[j] = buffer.m.planes[j].length;
414 buffers[i].start[j] =
415 mmap(NULL, buffer.m.planes[j].length, PROT_READ | PROT_WRITE,
416 MAP_SHARED, v4lfd, buffer.m.planes[j].m.mem_offset);
417 if (MAP_FAILED == buffers[i].start[j]) {
418 fprintf(stderr,
419 "failed to mmap buffer of length(%d) and offset(0x%x)\n",
420 buffer.m.planes[j].length, buffer.m.planes[j].m.mem_offset);
421 }
422 }
423 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800424
Steve Chodc8f92a2021-03-03 17:43:38 -0800425 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800426}
427
Steve Chodc8f92a2021-03-03 17:43:38 -0800428int queue_CAPTURE_buffer(struct queue* queue, uint32_t index) {
429 struct mmap_buffers* buffers = queue->buffers;
430 struct v4l2_buffer v4l2_buffer;
431 struct v4l2_plane planes[VIDEO_MAX_PLANES];
432 memset(&v4l2_buffer, 0, sizeof v4l2_buffer);
433 memset(&planes, 0, sizeof planes);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800434
Steve Chodc8f92a2021-03-03 17:43:38 -0800435 v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
436 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
437 v4l2_buffer.index = index;
438 v4l2_buffer.m.planes = planes;
439 v4l2_buffer.length = queue->num_planes;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800440
Steve Chodc8f92a2021-03-03 17:43:38 -0800441 v4l2_buffer.m.planes[0].length = buffers[index].length[0];
442 v4l2_buffer.m.planes[0].bytesused = buffers[index].length[0];
443 v4l2_buffer.m.planes[0].data_offset = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800444
Steve Chodc8f92a2021-03-03 17:43:38 -0800445 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
446 if (ret != 0) {
447 perror("VIDIOC_QBUF failed");
448 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800449
Steve Chodc8f92a2021-03-03 17:43:38 -0800450 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800451}
452
453// 4.5.2.5. Initialization
Steve Chodc8f92a2021-03-03 17:43:38 -0800454int Initialization(struct queue* OUTPUT_queue, struct queue* CAPTURE_queue) {
455 int ret = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800456
Steve Chodc8f92a2021-03-03 17:43:38 -0800457 // 1. Set the coded format on the CAPTURE queue via VIDIOC_S_FMT().
458 if (!ret) {
459 struct v4l2_format fmt;
460 memset(&fmt, 0, sizeof(fmt));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800461
Steve Chodc8f92a2021-03-03 17:43:38 -0800462 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
463 fmt.fmt.pix_mp.pixelformat = CAPTURE_queue->fourcc;
464 fmt.fmt.pix_mp.width = CAPTURE_queue->raw_width;
465 fmt.fmt.pix_mp.height = CAPTURE_queue->raw_height;
466 fmt.fmt.pix_mp.plane_fmt[0].sizeimage = kInputbufferMaxSize;
467 fmt.fmt.pix_mp.num_planes = 1;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800468
Steve Chodc8f92a2021-03-03 17:43:38 -0800469 int ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_S_FMT, &fmt);
470 if (ret != 0)
471 perror("VIDIOC_S_FMT failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800472
Steve Chodc8f92a2021-03-03 17:43:38 -0800473 CAPTURE_queue->encoded_width = fmt.fmt.pix_mp.width;
474 CAPTURE_queue->encoded_height = fmt.fmt.pix_mp.height;
475 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800476
Steve Chodc8f92a2021-03-03 17:43:38 -0800477 // 3. Set the raw source format on the OUTPUT queue via VIDIOC_S_FMT().
478 if (!ret) {
479 struct v4l2_format fmt;
480 memset(&fmt, 0, sizeof(fmt));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800481
Steve Chodc8f92a2021-03-03 17:43:38 -0800482 fmt.type = OUTPUT_queue->type;
483 fmt.fmt.pix_mp.pixelformat = OUTPUT_queue->fourcc;
484 fmt.fmt.pix_mp.width = OUTPUT_queue->raw_width;
485 fmt.fmt.pix_mp.height = OUTPUT_queue->raw_height;
486 fmt.fmt.pix_mp.num_planes = 1;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800487
Steve Chodc8f92a2021-03-03 17:43:38 -0800488 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_FMT, &fmt);
489 if (ret != 0)
490 perror("VIDIOC_S_FMT failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800491
Steve Chodc8f92a2021-03-03 17:43:38 -0800492 OUTPUT_queue->encoded_width = fmt.fmt.pix_mp.width;
493 OUTPUT_queue->encoded_height = fmt.fmt.pix_mp.height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800494
Steve Chodc8f92a2021-03-03 17:43:38 -0800495 OUTPUT_queue->num_planes = fmt.fmt.pix_mp.num_planes;
496 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800497
Steve Chodc8f92a2021-03-03 17:43:38 -0800498 // 4. Set the raw frame interval on the OUTPUT queue via VIDIOC_S_PARM()
499 if (!ret) {
500 struct v4l2_streamparm parms;
501 memset(&parms, 0, sizeof(parms));
502 parms.type = OUTPUT_queue->type;
503 // Note that we are provided "frames per second" but V4L2 expects "time per
504 // frame"; hence we provide the reciprocal of the framerate here.
505 parms.parm.output.timeperframe.numerator = 1;
506 parms.parm.output.timeperframe.denominator = OUTPUT_queue->framerate;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800507
Steve Chodc8f92a2021-03-03 17:43:38 -0800508 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_PARM, &parms);
509 if (ret != 0)
510 perror("VIDIOC_S_PARAM failed");
511 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800512
Steve Chodc8f92a2021-03-03 17:43:38 -0800513 // 6. Optional. Set the visible resolution for the stream metadata via
514 // VIDIOC_S_SELECTION() on the OUTPUT queue if it is desired to be
515 // different than the full OUTPUT resolution.
516 if (!ret) {
517 struct v4l2_selection selection_arg;
518 memset(&selection_arg, 0, sizeof(selection_arg));
519 selection_arg.type = OUTPUT_queue->type;
520 selection_arg.target = V4L2_SEL_TGT_CROP;
521 selection_arg.r.left = 0;
522 selection_arg.r.top = 0;
523 selection_arg.r.width = OUTPUT_queue->raw_width;
524 selection_arg.r.height = OUTPUT_queue->raw_height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800525
Steve Chodc8f92a2021-03-03 17:43:38 -0800526 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_SELECTION, &selection_arg);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800527
Steve Chodc8f92a2021-03-03 17:43:38 -0800528 if (ret != 0)
529 perror("VIDIOC_S_SELECTION failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800530
Steve Chodc8f92a2021-03-03 17:43:38 -0800531 // TODO(fritz) : check returned values are same as sent values
532 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800533
Steve Chodc8f92a2021-03-03 17:43:38 -0800534 // 7. Allocate buffers for both OUTPUT and CAPTURE via VIDIOC_REQBUFS().
535 // This may be performed in any order.
536 if (!ret) {
537 struct v4l2_requestbuffers reqbuf;
538 memset(&reqbuf, 0, sizeof(reqbuf));
539 reqbuf.count = kRequestBufferCount;
540 reqbuf.type = OUTPUT_queue->type;
541 reqbuf.memory = V4L2_MEMORY_MMAP;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800542
Steve Chodc8f92a2021-03-03 17:43:38 -0800543 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
544 if (ret != 0)
545 perror("VIDIOC_REQBUFS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800546
Steve Chodc8f92a2021-03-03 17:43:38 -0800547 printf(
548 "%d buffers requested, %d buffers for uncompressed frames returned\n",
549 kRequestBufferCount, reqbuf.count);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800550
Steve Chodc8f92a2021-03-03 17:43:38 -0800551 ret = request_mmap_buffers(OUTPUT_queue, &reqbuf);
552 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800553
Steve Chodc8f92a2021-03-03 17:43:38 -0800554 if (!ret) {
555 struct v4l2_requestbuffers reqbuf;
556 memset(&reqbuf, 0, sizeof(reqbuf));
557 reqbuf.count = kRequestBufferCount;
558 reqbuf.type = CAPTURE_queue->type;
559 reqbuf.memory = V4L2_MEMORY_MMAP;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800560
Steve Chodc8f92a2021-03-03 17:43:38 -0800561 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
562 if (ret != 0)
563 perror("VIDIOC_REQBUFS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800564
Steve Chodc8f92a2021-03-03 17:43:38 -0800565 printf("%d buffers requested, %d buffers for compressed frames returned\n",
566 kRequestBufferCount, reqbuf.count);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800567
Steve Chodc8f92a2021-03-03 17:43:38 -0800568 ret = request_mmap_buffers(CAPTURE_queue, &reqbuf);
569 for (uint32_t i = 0; i < reqbuf.count; i++) {
570 queue_CAPTURE_buffer(CAPTURE_queue, i);
571 }
572 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800573
Steve Chodc8f92a2021-03-03 17:43:38 -0800574 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800575}
576
Steve Chodc8f92a2021-03-03 17:43:38 -0800577int configure_h264(int v4lfd, struct encoder_cfg* cfg) {
578 int ret = 0;
579 const int kH264CtrlCnt = 4;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800580
Steve Chodc8f92a2021-03-03 17:43:38 -0800581 struct v4l2_ext_control ext_ctrl[kH264CtrlCnt];
582 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800583
Steve Chodc8f92a2021-03-03 17:43:38 -0800584 ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_H264_PROFILE;
585 ext_ctrl[0].value = cfg->h264_profile;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800586
Steve Chodc8f92a2021-03-03 17:43:38 -0800587 ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
588 ext_ctrl[1].value = cfg->h264_level;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800589
Steve Chodc8f92a2021-03-03 17:43:38 -0800590 ext_ctrl[2].id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE;
591 ext_ctrl[2].value = cfg->h264_entropy_mode;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800592
Steve Chodc8f92a2021-03-03 17:43:38 -0800593 ext_ctrl[3].id = V4L2_CID_MPEG_VIDEO_HEADER_MODE;
594 ext_ctrl[3].value = cfg->header_mode;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800595
Steve Chodc8f92a2021-03-03 17:43:38 -0800596 struct v4l2_ext_controls ext_ctrls;
597 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800598
Steve Chodc8f92a2021-03-03 17:43:38 -0800599 ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
600 ext_ctrls.count = kH264CtrlCnt;
601 ext_ctrls.controls = ext_ctrl;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800602
Steve Chodc8f92a2021-03-03 17:43:38 -0800603 ret = ioctl(v4lfd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800604
Steve Chodc8f92a2021-03-03 17:43:38 -0800605 if (ret != 0)
606 perror("VIDIOC_S_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800607
Steve Chodc8f92a2021-03-03 17:43:38 -0800608 for (uint32_t i = 0; i < kH264CtrlCnt; ++i)
609 ext_ctrl[i].value = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800610
Steve Chodc8f92a2021-03-03 17:43:38 -0800611 ret = ioctl(v4lfd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
612 if (ret != 0)
613 perror("VIDIOC_G_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800614
Steve Chodc8f92a2021-03-03 17:43:38 -0800615 if (ext_ctrl[0].value != cfg->h264_profile)
616 fprintf(stderr, "requested profile(%d) was not used, using (%d) instead.\n",
617 cfg->h264_profile, ext_ctrl[0].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800618
Steve Chodc8f92a2021-03-03 17:43:38 -0800619 if (ext_ctrl[1].value != cfg->h264_level)
620 fprintf(stderr, "requested level(%d) was not used, using (%d) instead.\n",
621 cfg->h264_level, ext_ctrl[1].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800622
Steve Chodc8f92a2021-03-03 17:43:38 -0800623 if (ext_ctrl[2].value != cfg->h264_entropy_mode)
624 fprintf(stderr,
625 "requested entropy mode(%d) was not used, using (%d) instead.\n",
626 cfg->h264_entropy_mode, ext_ctrl[2].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800627
Steve Chodc8f92a2021-03-03 17:43:38 -0800628 if (ext_ctrl[3].value != cfg->header_mode)
629 fprintf(stderr,
630 "requested entropy mode(%d) was not used, using (%d) instead.\n",
631 cfg->header_mode, ext_ctrl[3].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800632
Steve Chodc8f92a2021-03-03 17:43:38 -0800633 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800634}
635
Steve Chodc8f92a2021-03-03 17:43:38 -0800636int configure_common(int v4lfd, struct encoder_cfg* cfg) {
637 int ret = 0;
638 const int kCommonCtrlCnt = 5;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800639
Steve Chodc8f92a2021-03-03 17:43:38 -0800640 struct v4l2_ext_control ext_ctrl[kCommonCtrlCnt];
641 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800642
Steve Chodc8f92a2021-03-03 17:43:38 -0800643 ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_BITRATE;
644 ext_ctrl[0].value = cfg->bitrate;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800645
Steve Chodc8f92a2021-03-03 17:43:38 -0800646 ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK;
647 ext_ctrl[1].value = cfg->bitrate * 2;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800648
Steve Chodc8f92a2021-03-03 17:43:38 -0800649 ext_ctrl[2].id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
650 ext_ctrl[2].value = cfg->gop_size;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800651
Steve Chodc8f92a2021-03-03 17:43:38 -0800652 ext_ctrl[3].id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE;
653 ext_ctrl[3].value = 1;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800654
Steve Chodc8f92a2021-03-03 17:43:38 -0800655 ext_ctrl[4].id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE;
656 ext_ctrl[4].value = cfg->bitrate_mode;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800657
Steve Chodc8f92a2021-03-03 17:43:38 -0800658 struct v4l2_ext_controls ext_ctrls;
659 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800660
Steve Chodc8f92a2021-03-03 17:43:38 -0800661 ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
662 ext_ctrls.count = kCommonCtrlCnt;
663 ext_ctrls.controls = ext_ctrl;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800664
Steve Chodc8f92a2021-03-03 17:43:38 -0800665 ret = ioctl(v4lfd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800666
Steve Chodc8f92a2021-03-03 17:43:38 -0800667 if (ret != 0)
668 perror("VIDIOC_S_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800669
Steve Chodc8f92a2021-03-03 17:43:38 -0800670 for (uint32_t i = 0; i < kCommonCtrlCnt; ++i)
671 ext_ctrl[i].value = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800672
Steve Chodc8f92a2021-03-03 17:43:38 -0800673 ret = ioctl(v4lfd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
674 if (ret != 0)
675 perror("VIDIOC_G_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800676
Steve Chodc8f92a2021-03-03 17:43:38 -0800677 if (ext_ctrl[0].value != cfg->bitrate)
678 fprintf(
679 stderr,
680 "requested bitrate(%d) was outside of the limit, using (%d) instead.\n",
681 cfg->bitrate, ext_ctrl[0].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800682
Steve Chodc8f92a2021-03-03 17:43:38 -0800683 if (ext_ctrl[1].value != cfg->bitrate * 2)
684 fprintf(stderr,
685 "requested bitrate peak(%d) was outside of the limit, using (%d) "
686 "instead.\n",
687 cfg->bitrate * 2, ext_ctrl[1].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800688
Steve Chodc8f92a2021-03-03 17:43:38 -0800689 if (ext_ctrl[2].value != cfg->gop_size)
690 fprintf(stderr,
691 "requested gop size(%d) was not used, using (%d) instead.\n",
692 cfg->gop_size, ext_ctrl[2].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800693
Steve Chodc8f92a2021-03-03 17:43:38 -0800694 if (ext_ctrl[3].value != 1)
695 fprintf(
696 stderr,
697 "requested frame rate control (%d) was not used, using (%d) instead.\n",
698 1, ext_ctrl[3].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800699
Steve Chodc8f92a2021-03-03 17:43:38 -0800700 if (ext_ctrl[4].value != cfg->bitrate_mode)
701 fprintf(stderr,
702 "requested bitrate mode(%d) was not used, using (%d) instead.\n",
703 cfg->bitrate_mode, ext_ctrl[4].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800704
Steve Chodc8f92a2021-03-03 17:43:38 -0800705 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800706}
707
Steve Chodc8f92a2021-03-03 17:43:38 -0800708int dequeue_buffer(struct queue* queue,
709 uint32_t* index,
710 uint32_t* bytesused,
711 uint32_t* data_offset,
712 uint64_t* timestamp) {
713 struct v4l2_buffer v4l2_buffer;
714 struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
715 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
716 v4l2_buffer.type = queue->type;
717 v4l2_buffer.length = queue->num_planes;
718 v4l2_buffer.m.planes = planes;
719 v4l2_buffer.m.planes[0].bytesused = 0;
720 int ret = ioctl(queue->v4lfd, VIDIOC_DQBUF, &v4l2_buffer);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800721
Steve Chodc8f92a2021-03-03 17:43:38 -0800722 if (ret != 0 && errno != EAGAIN)
723 perror("VIDIOC_DQBUF failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800724
Steve Chodc8f92a2021-03-03 17:43:38 -0800725 *index = v4l2_buffer.index;
726 if (bytesused)
727 *bytesused = v4l2_buffer.m.planes[0].bytesused;
728 if (data_offset)
729 *data_offset = v4l2_buffer.m.planes[0].data_offset;
730 if (timestamp)
731 *timestamp = v4l2_buffer.timestamp.tv_usec;
732 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800733}
734
Steve Chodc8f92a2021-03-03 17:43:38 -0800735int encode(FILE* fp_input,
736 uint32_t file_format,
737 char* output_file_name,
738 struct queue* OUTPUT_queue,
739 struct queue* CAPTURE_queue,
740 uint32_t frames_to_decode) {
741 if (OUTPUT_queue->num_planes == 0 || OUTPUT_queue->num_planes > 2) {
742 fprintf(stderr, " unsupported number of planes: %d\n",
743 OUTPUT_queue->num_planes);
744 return -1;
745 }
746 fprintf(stderr, "encoding\n");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800747
Steve Chodc8f92a2021-03-03 17:43:38 -0800748 const int use_ivf = CAPTURE_queue->fourcc == v4l2_fourcc('V', 'P', '8', '0');
749 strcat(output_file_name, use_ivf ? ".ivf" : ".h264");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800750
Steve Chodc8f92a2021-03-03 17:43:38 -0800751 int ret = 0;
752 FILE* fp_output = fopen(output_file_name, "wb");
753 if (!fp_output) {
754 fprintf(stderr, "unable to write to file: %s\n", output_file_name);
755 ret = 1;
756 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800757
Steve Chodc8f92a2021-03-03 17:43:38 -0800758 // write header
759 if (use_ivf) {
760 struct ivf_file_header header;
761 header.signature = kIVFHeaderSignature;
762 header.version = 0;
763 header.header_length = sizeof(struct ivf_file_header);
764 header.fourcc = CAPTURE_queue->fourcc;
765 header.width = CAPTURE_queue->raw_width;
766 header.height = CAPTURE_queue->raw_height;
767 // hard coded 30fps
768 header.denominator = 30;
769 header.numerator = 1;
770 header.frame_cnt = frames_to_decode;
771 header.unused = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800772
Steve Chodc8f92a2021-03-03 17:43:38 -0800773 if (fwrite(&header, sizeof(struct ivf_file_header), 1, fp_output) != 1) {
774 fprintf(stderr, "unable to write ivf file header\n");
775 }
776 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800777
Steve Chodc8f92a2021-03-03 17:43:38 -0800778 struct timespec start, stop;
779 clock_gettime(CLOCK_REALTIME, &start);
780 if (!ret) {
781 // prime input by filling up the OUTPUT queue with raw frames
782 for (uint32_t i = 0; i < OUTPUT_queue->cnt; ++i) {
783 if (submit_raw_frame(fp_input, file_format, OUTPUT_queue, i)) {
784 fprintf(stderr, "unable to submit raw frame\n");
785 ret = 1;
786 }
787 }
788 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800789
Steve Chodc8f92a2021-03-03 17:43:38 -0800790 if (!ret) {
791 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_STREAMON, &OUTPUT_queue->type);
792 if (ret != 0)
793 perror("VIDIOC_STREAMON failed on OUTPUT");
794 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800795
Steve Chodc8f92a2021-03-03 17:43:38 -0800796 if (!ret) {
797 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_STREAMON, &CAPTURE_queue->type);
798 if (ret != 0)
799 perror("VIDIOC_STREAMON failed on CAPTURE");
800 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800801
Steve Chodc8f92a2021-03-03 17:43:38 -0800802 uint32_t cnt = OUTPUT_queue->cnt; // We pre-uploaded a few before.
803 if (!ret) {
804 while (cnt < frames_to_decode) {
805 // handle CAPTURE queue first
806 {
807 uint32_t index = 0;
808 uint32_t bytesused = 0;
809 uint32_t data_offset = 0;
810 uint64_t timestamp = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800811
Steve Chodc8f92a2021-03-03 17:43:38 -0800812 // first get the newly encoded frame
813 ret = dequeue_buffer(CAPTURE_queue, &index, &bytesused, &data_offset,
814 &timestamp);
815 if (ret != 0)
816 continue;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800817
Steve Chodc8f92a2021-03-03 17:43:38 -0800818 if (use_ivf) {
819 struct ivf_frame_header header;
820 header.size = bytesused - data_offset;
821 header.timestamp = timestamp;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800822
Steve Chodc8f92a2021-03-03 17:43:38 -0800823 if (fwrite(&header, sizeof(struct ivf_frame_header), 1, fp_output) !=
824 1) {
825 fprintf(stderr, "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);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800830
Steve Chodc8f92a2021-03-03 17:43:38 -0800831 // done with the buffer, queue it back up
832 queue_CAPTURE_buffer(CAPTURE_queue, index);
833 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800834
Steve Chodc8f92a2021-03-03 17:43:38 -0800835 // handle OUTPUT queue second
836 {
837 uint32_t index = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800838
Steve Chodc8f92a2021-03-03 17:43:38 -0800839 ret = dequeue_buffer(OUTPUT_queue, &index, 0, 0, 0);
840 if (ret != 0)
841 continue;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800842
Steve Chodc8f92a2021-03-03 17:43:38 -0800843 if (submit_raw_frame(fp_input, file_format, OUTPUT_queue, index))
844 break;
845 }
846 cnt++;
847 }
848 }
849 clock_gettime(CLOCK_REALTIME, &stop);
850 const double elapsed_ns =
851 (stop.tv_sec - start.tv_sec) * 1e9 + (stop.tv_nsec - start.tv_nsec);
852 const double fps = cnt * 1e9 / elapsed_ns;
853 printf("%d frames encoded in %fns (%ffps)\n", cnt, elapsed_ns, fps);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800854
Steve Chodc8f92a2021-03-03 17:43:38 -0800855 if (fp_output) {
856 fclose(fp_output);
857 }
858 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800859}
860
Steve Chodc8f92a2021-03-03 17:43:38 -0800861static void print_help(const char* argv0) {
862 printf("usage: %s [OPTIONS]\n", argv0);
863 printf(" -f, --file file to encode\n");
864 printf(" -i, --file_format pixel format of the file (yv12, nv12)\n");
865 printf(
866 " -o, --output output file name (will get a .h264 or .ivf suffix "
867 "added)\n");
868 printf(" -w, --width width of image\n");
869 printf(" -h, --height height of image\n");
870 printf(" -m, --max max number of frames to decode\n");
871 printf(" -r, --rate frames per second\n");
872 printf(" -b, --bitrate bits per second\n");
873 printf(" -g, --gop gop length\n");
874 printf(" -c, --codec codec\n");
875 printf(" -e, --end_usage rate control mode: VBR (default), CBR\n");
876 printf(" -v, --verbose verbose capabilities\n");
877 printf(" -q, --buffer_fmt OUTPUT queue format\n");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800878}
879
880static const struct option longopts[] = {
Steve Chodc8f92a2021-03-03 17:43:38 -0800881 {"file", required_argument, NULL, 'f'},
882 {"file_format", required_argument, NULL, 'i'},
883 {"output", required_argument, NULL, 'o'},
884 {"width", required_argument, NULL, 'w'},
885 {"height", required_argument, NULL, 'h'},
886 {"max", required_argument, NULL, 'm'},
887 {"fps", required_argument, NULL, 'r'},
888 {"bitrate", required_argument, NULL, 'b'},
889 {"gop", required_argument, NULL, 'g'},
890 {"codec", required_argument, NULL, 'c'},
891 {"end_usage", required_argument, NULL, 'e'},
892 {"verbose", no_argument, NULL, 'v'},
893 {"buffer_fmt", required_argument, NULL, 'q'},
894 {0, 0, 0, 0},
Fritz Koenigcdba6532021-01-22 16:11:17 -0800895};
896
Steve Chodc8f92a2021-03-03 17:43:38 -0800897int main(int argc, char* argv[]) {
898 uint32_t file_format = v4l2_fourcc('N', 'V', '1', '2');
899 uint32_t OUTPUT_format = v4l2_fourcc('N', 'V', '1', '2');
900 uint32_t CAPTURE_format = v4l2_fourcc('H', '2', '6', '4');
901 char* file_name = NULL;
902 char* output_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;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800909
Steve Chodc8f92a2021-03-03 17:43:38 -0800910 struct encoder_cfg cfg = {
911 .gop_size = 20,
912 .bitrate = 1000,
913 .h264_entropy_mode = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
914 .h264_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
915 .h264_profile = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
916 .header_mode = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
917 .bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR};
Fritz Koenigcdba6532021-01-22 16:11:17 -0800918
Steve Chodc8f92a2021-03-03 17:43:38 -0800919 while ((c = getopt_long(argc, argv, "f:i:o:w:h:m:r:b:g:c:e:vq:", longopts,
920 NULL)) != -1) {
921 switch (c) {
922 case 'f':
923 file_name = strdup(optarg);
924 break;
925 case 'i':
926 if (strlen(optarg) == 4) {
927 file_format = v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
928 toupper(optarg[2]), toupper(optarg[3]));
929 printf("using (%s) as the file format\n", optarg);
930 }
931 break;
932 case 'o':
933 output_file_name = strdup(optarg);
934 break;
935 case 'w':
936 width = atoi(optarg);
937 break;
938 case 'h':
939 height = atoi(optarg);
940 break;
941 case 'm':
942 frames_to_decode = atoi(optarg);
943 break;
944 case 'r':
945 framerate = atoi(optarg);
946 break;
947 case 'b':
948 cfg.bitrate = atoi(optarg);
949 break;
950 case 'g':
951 cfg.gop_size = atoi(optarg);
952 break;
953 case 'c':
954 if (strlen(optarg) == 4) {
955 CAPTURE_format = v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
956 toupper(optarg[2]), toupper(optarg[3]));
957 printf("using (%s) as the codec\n", optarg);
958 }
959 break;
960 case 'e':
961 if (strlen(optarg) == 3 && toupper(optarg[0]) == 'C' &&
962 toupper(optarg[1]) == 'B' && toupper(optarg[2]) == 'R') {
963 cfg.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
964 }
965 break;
966 case 'v':
967 verbose_capabilities = 1;
968 break;
969 case 'q':
970 if (strlen(optarg) == 4) {
971 OUTPUT_format = v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
972 toupper(optarg[2]), toupper(optarg[3]));
973 printf("using (%s) as the OUTPUT queue buffer format\n", optarg);
974 }
975 break;
976 default:
977 break;
978 }
979 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800980
Steve Chodc8f92a2021-03-03 17:43:38 -0800981 if (!file_name || !output_file_name || width == 0 || height == 0) {
982 fprintf(stderr, "Invalid parameters!\n");
983 print_help(argv[0]);
984 exit(1);
985 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800986
Steve Chodc8f92a2021-03-03 17:43:38 -0800987 FILE* fp = fopen(file_name, "rb");
988 if (!fp) {
989 fprintf(stderr, "%s: unable to open file.\n", file_name);
990 exit(1);
991 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800992
Steve Chodc8f92a2021-03-03 17:43:38 -0800993 if (!frames_to_decode) {
994 fseek(fp, 0, SEEK_END);
995 uint64_t length = ftell(fp);
996 uint32_t frame_size = (3 * width * height) >> 1;
997 frames_to_decode = length / frame_size;
998 fseek(fp, 0, SEEK_SET);
999 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001000
Steve Chodc8f92a2021-03-03 17:43:38 -08001001 fprintf(
1002 stderr, "encoding %d frames using %s bitrate control\n", frames_to_decode,
1003 (cfg.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) ? "CBR" : "VBR");
Fritz Koenigcdba6532021-01-22 16:11:17 -08001004
Steve Chodc8f92a2021-03-03 17:43:38 -08001005 int v4lfd = open(kEncodeDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC);
1006 if (v4lfd < 0) {
1007 fprintf(stderr, "Unable to open device file: %s\n", kEncodeDevice);
1008 exit(EXIT_FAILURE);
1009 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001010
Steve Chodc8f92a2021-03-03 17:43:38 -08001011 if (capabilities(v4lfd, OUTPUT_format, CAPTURE_format,
1012 verbose_capabilities) != 0) {
1013 fprintf(stderr, "Capabilities not present for encode.\n");
1014 exit(EXIT_FAILURE);
1015 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001016
Steve Chodc8f92a2021-03-03 17:43:38 -08001017 struct queue OUTPUT_queue = {.v4lfd = v4lfd,
1018 .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
1019 .fourcc = OUTPUT_format,
1020 .raw_width = width,
1021 .raw_height = height,
1022 .frame_cnt = 0,
1023 .num_planes = 1,
1024 .framerate = framerate};
Fritz Koenigcdba6532021-01-22 16:11:17 -08001025
Steve Chodc8f92a2021-03-03 17:43:38 -08001026 struct queue CAPTURE_queue = {.v4lfd = v4lfd,
1027 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
1028 .fourcc = CAPTURE_format,
1029 .raw_width = width,
1030 .raw_height = height,
1031 .num_planes = 1,
1032 .framerate = framerate};
Fritz Koenigcdba6532021-01-22 16:11:17 -08001033
Steve Chodc8f92a2021-03-03 17:43:38 -08001034 int ret = Initialization(&OUTPUT_queue, &CAPTURE_queue);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001035
Steve Chodc8f92a2021-03-03 17:43:38 -08001036 // not all configurations are supported, so we don't need to track
1037 // the return value
1038 if (!ret) {
1039 configure_common(v4lfd, &cfg);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001040
Steve Chodc8f92a2021-03-03 17:43:38 -08001041 if (v4l2_fourcc('H', '2', '6', '4') == CAPTURE_format)
1042 configure_h264(v4lfd, &cfg);
1043 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001044
Steve Chodc8f92a2021-03-03 17:43:38 -08001045 if (!ret)
1046 ret = encode(fp, file_format, output_file_name, &OUTPUT_queue,
1047 &CAPTURE_queue, frames_to_decode);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001048
Steve Chodc8f92a2021-03-03 17:43:38 -08001049 cleanup_queue(&OUTPUT_queue);
1050 cleanup_queue(&CAPTURE_queue);
1051 close(v4lfd);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001052
Steve Chodc8f92a2021-03-03 17:43:38 -08001053 return 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -08001054}