blob: a9227e18f97b57f6683880bf78dbff7845ae950e [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;
Miguel Casas4fe876d2021-03-05 22:26:31 -050048 uint32_t strides[VIDEO_MAX_PLANES];
Fritz Koenigcdba6532021-01-22 16:11:17 -080049};
50
51struct encoder_cfg {
Steve Chodc8f92a2021-03-03 17:43:38 -080052 uint32_t gop_size;
53 uint32_t bitrate;
54 enum v4l2_mpeg_video_h264_entropy_mode h264_entropy_mode;
55 enum v4l2_mpeg_video_h264_level h264_level;
56 enum v4l2_mpeg_video_h264_profile h264_profile;
57 enum v4l2_mpeg_video_header_mode header_mode;
58 enum v4l2_mpeg_video_bitrate_mode bitrate_mode;
Fritz Koenigcdba6532021-01-22 16:11:17 -080059};
60
61struct ivf_file_header {
Steve Chodc8f92a2021-03-03 17:43:38 -080062 uint32_t signature;
63 uint16_t version;
64 uint16_t header_length;
65 uint32_t fourcc;
66 uint16_t width;
67 uint16_t height;
68 uint32_t denominator;
69 uint32_t numerator;
70 uint32_t frame_cnt;
71 uint32_t unused;
Fritz Koenigcdba6532021-01-22 16:11:17 -080072} __attribute__((packed));
73
74struct ivf_frame_header {
Steve Chodc8f92a2021-03-03 17:43:38 -080075 uint32_t size;
76 uint64_t timestamp;
Fritz Koenigcdba6532021-01-22 16:11:17 -080077} __attribute__((packed));
78
Steve Chodc8f92a2021-03-03 17:43:38 -080079void print_fourcc(uint32_t fourcc) {
80 printf("%c%c%c%c\n", fourcc & 0xff, fourcc >> 8 & 0xff, fourcc >> 16 & 0xff,
81 fourcc >> 24 & 0xff);
Fritz Koenigcdba6532021-01-22 16:11:17 -080082}
83
Steve Chodc8f92a2021-03-03 17:43:38 -080084int query_format(int v4lfd, enum v4l2_buf_type type, uint32_t fourcc) {
85 struct v4l2_fmtdesc fmtdesc;
86 memset(&fmtdesc, 0, sizeof(fmtdesc));
Fritz Koenigcdba6532021-01-22 16:11:17 -080087
Steve Chodc8f92a2021-03-03 17:43:38 -080088 fmtdesc.type = type;
89 while (ioctl(v4lfd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
90 if (fourcc == 0)
91 print_fourcc(fmtdesc.pixelformat);
92 else if (fourcc == fmtdesc.pixelformat)
93 return 1;
94 fmtdesc.index++;
95 }
Fritz Koenigcdba6532021-01-22 16:11:17 -080096
Steve Chodc8f92a2021-03-03 17:43:38 -080097 return 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -080098}
99
Steve Chodc8f92a2021-03-03 17:43:38 -0800100void enumerate_menu(int v4lfd, uint32_t id, uint32_t min, uint32_t max) {
101 struct v4l2_querymenu querymenu;
102 memset(&querymenu, 0, sizeof(querymenu));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800103
Steve Chodc8f92a2021-03-03 17:43:38 -0800104 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 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800109}
110
Steve Chodc8f92a2021-03-03 17:43:38 -0800111int capabilities(int v4lfd,
112 uint32_t OUTPUT_format,
113 uint32_t CAPTURE_format,
114 int verbose_capabilities) {
115 struct v4l2_capability cap;
116 memset(&cap, 0, sizeof(cap));
117 int ret = ioctl(v4lfd, VIDIOC_QUERYCAP, &cap);
118 if (ret != 0)
119 perror("VIDIOC_QUERYCAP failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800120
Steve Chodc8f92a2021-03-03 17:43:38 -0800121 printf("driver=\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x\n", cap.driver,
122 cap.bus_info, cap.card, v4lfd);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800123
Steve Chodc8f92a2021-03-03 17:43:38 -0800124 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, OUTPUT_format)) {
125 printf("Supported OUTPUT formats:\n");
126 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, 0);
127 ret = 1;
128 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800129
Steve Chodc8f92a2021-03-03 17:43:38 -0800130 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
131 CAPTURE_format)) {
132 printf("Supported CAPTURE formats:\n");
133 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0);
134 ret = 1;
135 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800136
Steve Chodc8f92a2021-03-03 17:43:38 -0800137 if (verbose_capabilities) {
138 struct v4l2_query_ext_ctrl queryctrl;
139 memset(&queryctrl, 0, sizeof(queryctrl));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800140
Steve Chodc8f92a2021-03-03 17:43:38 -0800141 for (queryctrl.id = V4L2_CID_BASE; queryctrl.id < V4L2_CID_LASTP1;
142 queryctrl.id++) {
143 if (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
144 fprintf(
145 stderr, "control %s : %s\n", queryctrl.name,
146 queryctrl.flags & V4L2_CTRL_FLAG_DISABLED ? "disabled" : "enabled");
147 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
148 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
149 queryctrl.maximum);
150 } else if (errno == EINVAL) {
151 continue;
152 }
153 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800154
Steve Chodc8f92a2021-03-03 17:43:38 -0800155 for (queryctrl.id = V4L2_CID_PRIVATE_BASE;; queryctrl.id++) {
156 if (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
157 if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
158 continue;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800159
Steve Chodc8f92a2021-03-03 17:43:38 -0800160 fprintf(stderr, "control %s\n", queryctrl.name);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800161
Steve Chodc8f92a2021-03-03 17:43:38 -0800162 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
163 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
164 queryctrl.maximum);
165 } else if (errno == EINVAL) {
166 break;
167 }
168 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800169
Steve Chodc8f92a2021-03-03 17:43:38 -0800170 memset(&queryctrl, 0, sizeof(queryctrl));
171 queryctrl.id = V4L2_CTRL_CLASS_MPEG | V4L2_CTRL_FLAG_NEXT_CTRL;
172 while (0 == ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &queryctrl)) {
173 fprintf(stderr, "control %s\n", queryctrl.name);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800174
Steve Chodc8f92a2021-03-03 17:43:38 -0800175 if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
176 enumerate_menu(v4lfd, queryctrl.id, queryctrl.minimum,
177 queryctrl.maximum);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800178
Steve Chodc8f92a2021-03-03 17:43:38 -0800179 if (V4L2_CTRL_ID2CLASS(queryctrl.id) != V4L2_CTRL_CLASS_MPEG)
180 break;
181 /* ... */
182 queryctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
183 }
184 }
185 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800186}
187
Steve Chodc8f92a2021-03-03 17:43:38 -0800188int queue_OUTPUT_buffer(struct queue* queue,
189 struct mmap_buffers* buffers,
190 uint32_t index) {
191 // compute frame timestamp
192 const float usec_per_frame = (1.0 / queue->framerate) * 1000000;
193 const uint64_t usec_time_stamp = usec_per_frame * queue->frame_cnt;
194 const uint64_t tv_sec = usec_time_stamp / 1000000;
Fritz Koenig62733512021-02-17 18:36:21 -0800195
Steve Chodc8f92a2021-03-03 17:43:38 -0800196 struct v4l2_buffer v4l2_buffer;
197 struct v4l2_plane planes[VIDEO_MAX_PLANES];
198 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
Fritz Koenig62733512021-02-17 18:36:21 -0800199
Steve Chodc8f92a2021-03-03 17:43:38 -0800200 v4l2_buffer.index = index;
201 v4l2_buffer.type = queue->type;
202 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
203 v4l2_buffer.length = queue->num_planes;
204 v4l2_buffer.timestamp.tv_sec = tv_sec;
205 v4l2_buffer.timestamp.tv_usec = usec_time_stamp - tv_sec;
206 v4l2_buffer.sequence = queue->frame_cnt;
207 v4l2_buffer.m.planes = planes;
208 for (uint32_t i = 0; i < queue->num_planes; ++i) {
209 v4l2_buffer.m.planes[i].length = buffers[index].length[i];
210 v4l2_buffer.m.planes[i].bytesused = buffers[index].length[i];
211 v4l2_buffer.m.planes[i].data_offset = 0;
212 }
Fritz Koenig62733512021-02-17 18:36:21 -0800213
Steve Chodc8f92a2021-03-03 17:43:38 -0800214 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
215 if (ret != 0) {
216 perror("VIDIOC_QBUF failed");
217 return -1;
218 }
Fritz Koenig62733512021-02-17 18:36:21 -0800219
Steve Chodc8f92a2021-03-03 17:43:38 -0800220 queue->frame_cnt++;
Fritz Koenig62733512021-02-17 18:36:21 -0800221
Steve Chodc8f92a2021-03-03 17:43:38 -0800222 return 0;
Fritz Koenig62733512021-02-17 18:36:21 -0800223}
224
Miguel Casas266b2e42021-02-05 22:05:58 -0500225// This function copies the contents pointed by |fp| tp |queue|s |index| buffer.
Steve Chodc8f92a2021-03-03 17:43:38 -0800226int submit_raw_frame_in_bulk(FILE* fp, struct queue* queue, uint32_t index) {
227 assert(queue->num_planes == 1 || queue->num_planes == 2);
228 assert(queue->raw_width == queue->encoded_width);
229 // TODO: the code below assumes NV12 because the Chroma planes are copied in
230 // one call. Extend to YV12 if ever the need arises.
231 assert(queue->fourcc == v4l2_fourcc('N', 'V', '1', '2'));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800232
Steve Chodc8f92a2021-03-03 17:43:38 -0800233 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800234
Steve Chodc8f92a2021-03-03 17:43:38 -0800235 // read y plane first
236 size_t frame_size = queue->raw_width * queue->raw_height;
237 uint8_t* buffer = buffers[index].start[0];
Fritz Koenigcdba6532021-01-22 16:11:17 -0800238
Steve Chodc8f92a2021-03-03 17:43:38 -0800239 if (fread(buffer, frame_size, 1, fp) != 1) {
240 fprintf(stderr, "unable to read luma frame\n");
241 return -1;
242 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800243
Steve Chodc8f92a2021-03-03 17:43:38 -0800244 // now read uv
245 frame_size >>= 1;
246 if (queue->num_planes == 2)
247 buffer = buffers[index].start[1];
248 else
249 buffer += queue->encoded_width * queue->encoded_height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800250
Steve Chodc8f92a2021-03-03 17:43:38 -0800251 if (fread(buffer, frame_size, 1, fp) != 1) {
252 fprintf(stderr, "unable to read chroma frame\n");
253 return -1;
254 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800255
Steve Chodc8f92a2021-03-03 17:43:38 -0800256 return queue_OUTPUT_buffer(queue, buffers, index);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800257}
258
Miguel Casas8ed87472021-02-05 22:12:07 -0500259// This function copies the contents pointed by |fp| to |queue|s |index| buffer.
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) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500264 assert(queue->num_planes == 1 || queue->num_planes == 3);
Steve Chodc8f92a2021-03-03 17:43:38 -0800265 assert(queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') ||
Miguel Casas4fe876d2021-03-05 22:26:31 -0500266 queue->fourcc == v4l2_fourcc('Y', 'M', '1', '2') ||
Steve Chodc8f92a2021-03-03 17:43:38 -0800267 queue->fourcc == v4l2_fourcc('N', 'V', '1', '2'));
Miguel Casas4fe876d2021-03-05 22:26:31 -0500268 assert(file_format == v4l2_fourcc('Y', '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 // Now read the U and V planes.
Miguel Casas4fe876d2021-03-05 22:26:31 -0500283 if ((queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') ||
284 queue->fourcc == v4l2_fourcc('Y', 'M', '1', '2')) &&
Steve Chodc8f92a2021-03-03 17:43:38 -0800285 file_format == v4l2_fourcc('Y', 'V', '1', '2')) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500286 assert((queue->fourcc == v4l2_fourcc('Y', 'M', '1', '2') &&
287 queue->num_planes == 3) ||
288 (queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') &&
289 queue->num_planes == 1));
290
291 buffer = buffers[index].start[1];
292 const uint32_t stride_u = queue->strides[1];
293
294 for (int row = 0; row < queue->raw_height / 2; ++row) {
295 if (fread(buffer, queue->raw_width / 2, 1, fp) != 1) {
296 fprintf(stderr, "unable to read chroma v row\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800297 return -1;
298 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500299 buffer += stride_u;
Steve Chodc8f92a2021-03-03 17:43:38 -0800300 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500301
Miguel Casas4fe876d2021-03-05 22:26:31 -0500302 uint32_t stride_v = stride_u;
303 if (queue->num_planes == 3) {
304 buffer = buffers[index].start[2];
305 stride_v = queue->strides[2];
Steve Chodc8f92a2021-03-03 17:43:38 -0800306 } else {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500307 assert(queue->num_planes == 1);
Steve Chodc8f92a2021-03-03 17:43:38 -0800308 buffer = buffers[index].start[0] +
309 5 * queue->encoded_width * queue->encoded_height / 4;
310 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500311
Miguel Casas4fe876d2021-03-05 22:26:31 -0500312 for (int row = 0; row < queue->raw_height / 2; ++row) {
313 if (fread(buffer, queue->raw_width / 2, 1, fp) != 1) {
314 fprintf(stderr, "unable to read chroma u row\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800315 return -1;
316 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500317 buffer += stride_v;
Steve Chodc8f92a2021-03-03 17:43:38 -0800318 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500319
Steve Chodc8f92a2021-03-03 17:43:38 -0800320 } else if (queue->fourcc == v4l2_fourcc('N', 'V', '1', '2') &&
321 file_format == v4l2_fourcc('Y', 'V', '1', '2') &&
322 queue->num_planes == 1) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500323 assert(queue->num_planes == 1);
324
325 // Copy all chroma samples from |fp| one by one in even |buffer| positions,
326 // then on the second loop iteration, move |buffer| one position right and
327 // copy from |fp| into the odd |buffer| positions.
Steve Chodc8f92a2021-03-03 17:43:38 -0800328 const int kNumPlanes = 2u;
329 for (int plane = 0; plane < kNumPlanes; ++plane) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500330 buffer = buffers[index].start[0] +
331 queue->encoded_width * queue->encoded_height + plane;
332 const uint32_t row_padding = queue->encoded_width - queue->raw_width;
Steve Chodc8f92a2021-03-03 17:43:38 -0800333 for (int row = 0; row < queue->raw_height / 4; ++row) {
334 for (int col = 0; col < queue->raw_width / 2; ++col) {
335 if (fread(buffer, 1 /*size */, 1 /*nmemb*/, fp) != 1) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500336 fprintf(stderr, "unable to read chroma v byte\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800337 return -1;
338 }
339 buffer += 2;
340 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500341 buffer += row_padding;
Miguel Casas8ed87472021-02-05 22:12:07 -0500342
Steve Chodc8f92a2021-03-03 17:43:38 -0800343 for (int col = 0; col < queue->raw_width / 2; ++col) {
344 if (fread(buffer, 1 /*size */, 1 /*nmemb*/, fp) != 1) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500345 fprintf(stderr, "unable to read chroma u byte\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800346 return -1;
347 }
348 buffer += 2;
349 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500350 buffer += row_padding;
Steve Chodc8f92a2021-03-03 17:43:38 -0800351 }
Steve Chodc8f92a2021-03-03 17:43:38 -0800352 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500353
Steve Chodc8f92a2021-03-03 17:43:38 -0800354 } else {
355 fprintf(stderr,
356 "combination of queue format, number of planes, and file format "
357 "unsupported\n");
358 return -1;
359 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500360
Steve Chodc8f92a2021-03-03 17:43:38 -0800361 return queue_OUTPUT_buffer(queue, buffers, index);
Miguel Casas8ed87472021-02-05 22:12:07 -0500362}
363
Miguel Casas266b2e42021-02-05 22:05:58 -0500364// This function copies the content of |fp| into the |index|th buffer of
365// |queue|. Depending on |file_format| and the |queue| format, and the raw and
366// encoded sizes of the latter, we might do a copy in bulk or need conversion.
Steve Chodc8f92a2021-03-03 17:43:38 -0800367int submit_raw_frame(FILE* fp,
368 uint32_t file_format,
369 struct queue* queue,
370 uint32_t index) {
371 if (queue->raw_width == queue->encoded_width &&
372 queue->fourcc == file_format &&
373 queue->fourcc == v4l2_fourcc('N', 'V', '1', '2')) {
374 return submit_raw_frame_in_bulk(fp, queue, index);
375 }
Miguel Casas266b2e42021-02-05 22:05:58 -0500376
Steve Chodc8f92a2021-03-03 17:43:38 -0800377 return submit_raw_frame_row_by_row(fp, file_format, queue, index);
Miguel Casas266b2e42021-02-05 22:05:58 -0500378}
379
Steve Chodc8f92a2021-03-03 17:43:38 -0800380void cleanup_queue(struct queue* queue) {
381 if (queue->cnt) {
382 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800383
Steve Chodc8f92a2021-03-03 17:43:38 -0800384 for (uint32_t i = 0; i < queue->cnt; i++)
385 for (uint32_t j = 0; j < queue->num_planes; j++) {
386 munmap(buffers[i].start[j], buffers[i].length[j]);
387 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800388
Steve Chodc8f92a2021-03-03 17:43:38 -0800389 free(queue->buffers);
390 queue->cnt = 0;
391 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800392}
393
Steve Chodc8f92a2021-03-03 17:43:38 -0800394int request_mmap_buffers(struct queue* queue,
395 struct v4l2_requestbuffers* reqbuf) {
396 const int v4lfd = queue->v4lfd;
397 const uint32_t buffer_alloc = reqbuf->count * sizeof(struct mmap_buffers);
398 struct mmap_buffers* buffers = (struct mmap_buffers*)malloc(buffer_alloc);
399 memset(buffers, 0, buffer_alloc);
400 queue->buffers = buffers;
401 queue->cnt = reqbuf->count;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800402
Steve Chodc8f92a2021-03-03 17:43:38 -0800403 int ret;
404 for (uint32_t i = 0; i < reqbuf->count; i++) {
405 struct v4l2_buffer buffer;
406 struct v4l2_plane planes[VIDEO_MAX_PLANES];
407 memset(&buffer, 0, sizeof(buffer));
408 buffer.type = reqbuf->type;
409 buffer.memory = V4L2_MEMORY_MMAP;
410 buffer.index = i;
411 buffer.length = queue->num_planes;
412 buffer.m.planes = planes;
413 ret = ioctl(v4lfd, VIDIOC_QUERYBUF, &buffer);
414 if (ret != 0) {
415 printf("VIDIOC_QUERYBUF failed: %d\n", ret);
416 break;
417 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800418
Steve Chodc8f92a2021-03-03 17:43:38 -0800419 for (uint32_t j = 0; j < queue->num_planes; j++) {
420 buffers[i].length[j] = buffer.m.planes[j].length;
421 buffers[i].start[j] =
422 mmap(NULL, buffer.m.planes[j].length, PROT_READ | PROT_WRITE,
423 MAP_SHARED, v4lfd, buffer.m.planes[j].m.mem_offset);
424 if (MAP_FAILED == buffers[i].start[j]) {
425 fprintf(stderr,
426 "failed to mmap buffer of length(%d) and offset(0x%x)\n",
427 buffer.m.planes[j].length, buffer.m.planes[j].m.mem_offset);
428 }
429 }
430 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800431
Steve Chodc8f92a2021-03-03 17:43:38 -0800432 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800433}
434
Steve Chodc8f92a2021-03-03 17:43:38 -0800435int queue_CAPTURE_buffer(struct queue* queue, uint32_t index) {
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);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800441
Steve Chodc8f92a2021-03-03 17:43:38 -0800442 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;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800447
Steve Chodc8f92a2021-03-03 17:43:38 -0800448 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;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800451
Steve Chodc8f92a2021-03-03 17:43:38 -0800452 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
453 if (ret != 0) {
454 perror("VIDIOC_QBUF failed");
455 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800456
Steve Chodc8f92a2021-03-03 17:43:38 -0800457 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800458}
459
460// 4.5.2.5. Initialization
Steve Chodc8f92a2021-03-03 17:43:38 -0800461int Initialization(struct queue* OUTPUT_queue, struct queue* CAPTURE_queue) {
462 int ret = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800463
Steve Chodc8f92a2021-03-03 17:43:38 -0800464 // 1. Set the coded format on the CAPTURE queue via VIDIOC_S_FMT().
465 if (!ret) {
466 struct v4l2_format fmt;
467 memset(&fmt, 0, sizeof(fmt));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800468
Steve Chodc8f92a2021-03-03 17:43:38 -0800469 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
470 fmt.fmt.pix_mp.pixelformat = CAPTURE_queue->fourcc;
471 fmt.fmt.pix_mp.width = CAPTURE_queue->raw_width;
472 fmt.fmt.pix_mp.height = CAPTURE_queue->raw_height;
473 fmt.fmt.pix_mp.plane_fmt[0].sizeimage = kInputbufferMaxSize;
474 fmt.fmt.pix_mp.num_planes = 1;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800475
Steve Chodc8f92a2021-03-03 17:43:38 -0800476 int ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_S_FMT, &fmt);
477 if (ret != 0)
478 perror("VIDIOC_S_FMT failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800479
Steve Chodc8f92a2021-03-03 17:43:38 -0800480 CAPTURE_queue->encoded_width = fmt.fmt.pix_mp.width;
481 CAPTURE_queue->encoded_height = fmt.fmt.pix_mp.height;
482 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800483
Steve Chodc8f92a2021-03-03 17:43:38 -0800484 // 3. Set the raw source format on the OUTPUT queue via VIDIOC_S_FMT().
485 if (!ret) {
486 struct v4l2_format fmt;
487 memset(&fmt, 0, sizeof(fmt));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800488
Steve Chodc8f92a2021-03-03 17:43:38 -0800489 fmt.type = OUTPUT_queue->type;
490 fmt.fmt.pix_mp.pixelformat = OUTPUT_queue->fourcc;
491 fmt.fmt.pix_mp.width = OUTPUT_queue->raw_width;
492 fmt.fmt.pix_mp.height = OUTPUT_queue->raw_height;
493 fmt.fmt.pix_mp.num_planes = 1;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800494
Steve Chodc8f92a2021-03-03 17:43:38 -0800495 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_FMT, &fmt);
496 if (ret != 0)
497 perror("VIDIOC_S_FMT failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800498
Steve Chodc8f92a2021-03-03 17:43:38 -0800499 OUTPUT_queue->encoded_width = fmt.fmt.pix_mp.width;
500 OUTPUT_queue->encoded_height = fmt.fmt.pix_mp.height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800501
Steve Chodc8f92a2021-03-03 17:43:38 -0800502 OUTPUT_queue->num_planes = fmt.fmt.pix_mp.num_planes;
Miguel Casas4fe876d2021-03-05 22:26:31 -0500503 assert(OUTPUT_queue->num_planes <= VIDEO_MAX_PLANES);
504 printf("OUTPUT_queue %d planes, (%dx%d)\n", OUTPUT_queue->num_planes,
505 OUTPUT_queue->encoded_width, OUTPUT_queue->encoded_height);
506 for (int plane = 0; plane < OUTPUT_queue->num_planes; ++plane) {
507 OUTPUT_queue->strides[plane] =
508 fmt.fmt.pix_mp.plane_fmt[plane].bytesperline;
509 printf(" plane %d, stride %d\n", plane, OUTPUT_queue->strides[plane]);
510 }
Steve Chodc8f92a2021-03-03 17:43:38 -0800511 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800512
Steve Chodc8f92a2021-03-03 17:43:38 -0800513 // 4. Set the raw frame interval on the OUTPUT queue via VIDIOC_S_PARM()
514 if (!ret) {
515 struct v4l2_streamparm parms;
516 memset(&parms, 0, sizeof(parms));
517 parms.type = OUTPUT_queue->type;
518 // Note that we are provided "frames per second" but V4L2 expects "time per
519 // frame"; hence we provide the reciprocal of the framerate here.
520 parms.parm.output.timeperframe.numerator = 1;
521 parms.parm.output.timeperframe.denominator = OUTPUT_queue->framerate;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800522
Steve Chodc8f92a2021-03-03 17:43:38 -0800523 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_PARM, &parms);
524 if (ret != 0)
525 perror("VIDIOC_S_PARAM failed");
526 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800527
Steve Chodc8f92a2021-03-03 17:43:38 -0800528 // 6. Optional. Set the visible resolution for the stream metadata via
529 // VIDIOC_S_SELECTION() on the OUTPUT queue if it is desired to be
530 // different than the full OUTPUT resolution.
531 if (!ret) {
532 struct v4l2_selection selection_arg;
533 memset(&selection_arg, 0, sizeof(selection_arg));
534 selection_arg.type = OUTPUT_queue->type;
535 selection_arg.target = V4L2_SEL_TGT_CROP;
536 selection_arg.r.left = 0;
537 selection_arg.r.top = 0;
538 selection_arg.r.width = OUTPUT_queue->raw_width;
539 selection_arg.r.height = OUTPUT_queue->raw_height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800540
Steve Chodc8f92a2021-03-03 17:43:38 -0800541 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_SELECTION, &selection_arg);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800542
Steve Chodc8f92a2021-03-03 17:43:38 -0800543 if (ret != 0)
544 perror("VIDIOC_S_SELECTION failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800545
Steve Chodc8f92a2021-03-03 17:43:38 -0800546 // TODO(fritz) : check returned values are same as sent values
547 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800548
Steve Chodc8f92a2021-03-03 17:43:38 -0800549 // 7. Allocate buffers for both OUTPUT and CAPTURE via VIDIOC_REQBUFS().
550 // This may be performed in any order.
551 if (!ret) {
552 struct v4l2_requestbuffers reqbuf;
553 memset(&reqbuf, 0, sizeof(reqbuf));
554 reqbuf.count = kRequestBufferCount;
555 reqbuf.type = OUTPUT_queue->type;
556 reqbuf.memory = V4L2_MEMORY_MMAP;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800557
Steve Chodc8f92a2021-03-03 17:43:38 -0800558 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
559 if (ret != 0)
560 perror("VIDIOC_REQBUFS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800561
Steve Chodc8f92a2021-03-03 17:43:38 -0800562 printf(
563 "%d buffers requested, %d buffers for uncompressed frames returned\n",
564 kRequestBufferCount, reqbuf.count);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800565
Steve Chodc8f92a2021-03-03 17:43:38 -0800566 ret = request_mmap_buffers(OUTPUT_queue, &reqbuf);
567 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800568
Steve Chodc8f92a2021-03-03 17:43:38 -0800569 if (!ret) {
570 struct v4l2_requestbuffers reqbuf;
571 memset(&reqbuf, 0, sizeof(reqbuf));
572 reqbuf.count = kRequestBufferCount;
573 reqbuf.type = CAPTURE_queue->type;
574 reqbuf.memory = V4L2_MEMORY_MMAP;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800575
Steve Chodc8f92a2021-03-03 17:43:38 -0800576 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
577 if (ret != 0)
578 perror("VIDIOC_REQBUFS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800579
Steve Chodc8f92a2021-03-03 17:43:38 -0800580 printf("%d buffers requested, %d buffers for compressed frames returned\n",
581 kRequestBufferCount, reqbuf.count);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800582
Steve Chodc8f92a2021-03-03 17:43:38 -0800583 ret = request_mmap_buffers(CAPTURE_queue, &reqbuf);
584 for (uint32_t i = 0; i < reqbuf.count; i++) {
585 queue_CAPTURE_buffer(CAPTURE_queue, i);
586 }
587 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800588
Steve Chodc8f92a2021-03-03 17:43:38 -0800589 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800590}
591
Steve Chodc8f92a2021-03-03 17:43:38 -0800592int configure_h264(int v4lfd, struct encoder_cfg* cfg) {
593 int ret = 0;
594 const int kH264CtrlCnt = 4;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800595
Steve Chodc8f92a2021-03-03 17:43:38 -0800596 struct v4l2_ext_control ext_ctrl[kH264CtrlCnt];
597 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800598
Steve Chodc8f92a2021-03-03 17:43:38 -0800599 ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_H264_PROFILE;
600 ext_ctrl[0].value = cfg->h264_profile;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800601
Steve Chodc8f92a2021-03-03 17:43:38 -0800602 ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
603 ext_ctrl[1].value = cfg->h264_level;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800604
Steve Chodc8f92a2021-03-03 17:43:38 -0800605 ext_ctrl[2].id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE;
606 ext_ctrl[2].value = cfg->h264_entropy_mode;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800607
Steve Chodc8f92a2021-03-03 17:43:38 -0800608 ext_ctrl[3].id = V4L2_CID_MPEG_VIDEO_HEADER_MODE;
609 ext_ctrl[3].value = cfg->header_mode;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800610
Steve Chodc8f92a2021-03-03 17:43:38 -0800611 struct v4l2_ext_controls ext_ctrls;
612 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800613
Steve Chodc8f92a2021-03-03 17:43:38 -0800614 ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
615 ext_ctrls.count = kH264CtrlCnt;
616 ext_ctrls.controls = ext_ctrl;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800617
Steve Chodc8f92a2021-03-03 17:43:38 -0800618 ret = ioctl(v4lfd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800619
Steve Chodc8f92a2021-03-03 17:43:38 -0800620 if (ret != 0)
621 perror("VIDIOC_S_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800622
Steve Chodc8f92a2021-03-03 17:43:38 -0800623 for (uint32_t i = 0; i < kH264CtrlCnt; ++i)
624 ext_ctrl[i].value = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800625
Steve Chodc8f92a2021-03-03 17:43:38 -0800626 ret = ioctl(v4lfd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
627 if (ret != 0)
628 perror("VIDIOC_G_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800629
Steve Chodc8f92a2021-03-03 17:43:38 -0800630 if (ext_ctrl[0].value != cfg->h264_profile)
631 fprintf(stderr, "requested profile(%d) was not used, using (%d) instead.\n",
632 cfg->h264_profile, ext_ctrl[0].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800633
Steve Chodc8f92a2021-03-03 17:43:38 -0800634 if (ext_ctrl[1].value != cfg->h264_level)
635 fprintf(stderr, "requested level(%d) was not used, using (%d) instead.\n",
636 cfg->h264_level, ext_ctrl[1].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800637
Steve Chodc8f92a2021-03-03 17:43:38 -0800638 if (ext_ctrl[2].value != cfg->h264_entropy_mode)
639 fprintf(stderr,
640 "requested entropy mode(%d) was not used, using (%d) instead.\n",
641 cfg->h264_entropy_mode, ext_ctrl[2].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800642
Steve Chodc8f92a2021-03-03 17:43:38 -0800643 if (ext_ctrl[3].value != cfg->header_mode)
644 fprintf(stderr,
645 "requested entropy mode(%d) was not used, using (%d) instead.\n",
646 cfg->header_mode, ext_ctrl[3].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800647
Steve Chodc8f92a2021-03-03 17:43:38 -0800648 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800649}
650
Steve Chodc8f92a2021-03-03 17:43:38 -0800651int configure_common(int v4lfd, struct encoder_cfg* cfg) {
652 int ret = 0;
653 const int kCommonCtrlCnt = 5;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800654
Steve Chodc8f92a2021-03-03 17:43:38 -0800655 struct v4l2_ext_control ext_ctrl[kCommonCtrlCnt];
656 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800657
Steve Chodc8f92a2021-03-03 17:43:38 -0800658 ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_BITRATE;
659 ext_ctrl[0].value = cfg->bitrate;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800660
Steve Chodc8f92a2021-03-03 17:43:38 -0800661 ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK;
662 ext_ctrl[1].value = cfg->bitrate * 2;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800663
Steve Chodc8f92a2021-03-03 17:43:38 -0800664 ext_ctrl[2].id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
665 ext_ctrl[2].value = cfg->gop_size;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800666
Steve Chodc8f92a2021-03-03 17:43:38 -0800667 ext_ctrl[3].id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE;
668 ext_ctrl[3].value = 1;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800669
Steve Chodc8f92a2021-03-03 17:43:38 -0800670 ext_ctrl[4].id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE;
671 ext_ctrl[4].value = cfg->bitrate_mode;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800672
Steve Chodc8f92a2021-03-03 17:43:38 -0800673 struct v4l2_ext_controls ext_ctrls;
674 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800675
Steve Chodc8f92a2021-03-03 17:43:38 -0800676 ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
677 ext_ctrls.count = kCommonCtrlCnt;
678 ext_ctrls.controls = ext_ctrl;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800679
Steve Chodc8f92a2021-03-03 17:43:38 -0800680 ret = ioctl(v4lfd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800681
Steve Chodc8f92a2021-03-03 17:43:38 -0800682 if (ret != 0)
683 perror("VIDIOC_S_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800684
Steve Chodc8f92a2021-03-03 17:43:38 -0800685 for (uint32_t i = 0; i < kCommonCtrlCnt; ++i)
686 ext_ctrl[i].value = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800687
Steve Chodc8f92a2021-03-03 17:43:38 -0800688 ret = ioctl(v4lfd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
689 if (ret != 0)
690 perror("VIDIOC_G_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800691
Steve Chodc8f92a2021-03-03 17:43:38 -0800692 if (ext_ctrl[0].value != cfg->bitrate)
693 fprintf(
694 stderr,
695 "requested bitrate(%d) was outside of the limit, using (%d) instead.\n",
696 cfg->bitrate, ext_ctrl[0].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800697
Steve Chodc8f92a2021-03-03 17:43:38 -0800698 if (ext_ctrl[1].value != cfg->bitrate * 2)
699 fprintf(stderr,
700 "requested bitrate peak(%d) was outside of the limit, using (%d) "
701 "instead.\n",
702 cfg->bitrate * 2, ext_ctrl[1].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800703
Steve Chodc8f92a2021-03-03 17:43:38 -0800704 if (ext_ctrl[2].value != cfg->gop_size)
705 fprintf(stderr,
706 "requested gop size(%d) was not used, using (%d) instead.\n",
707 cfg->gop_size, ext_ctrl[2].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800708
Steve Chodc8f92a2021-03-03 17:43:38 -0800709 if (ext_ctrl[3].value != 1)
710 fprintf(
711 stderr,
712 "requested frame rate control (%d) was not used, using (%d) instead.\n",
713 1, ext_ctrl[3].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800714
Steve Chodc8f92a2021-03-03 17:43:38 -0800715 if (ext_ctrl[4].value != cfg->bitrate_mode)
716 fprintf(stderr,
717 "requested bitrate mode(%d) was not used, using (%d) instead.\n",
718 cfg->bitrate_mode, ext_ctrl[4].value);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800719
Steve Chodc8f92a2021-03-03 17:43:38 -0800720 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800721}
722
Steve Chodc8f92a2021-03-03 17:43:38 -0800723int dequeue_buffer(struct queue* queue,
724 uint32_t* index,
725 uint32_t* bytesused,
726 uint32_t* data_offset,
727 uint64_t* timestamp) {
728 struct v4l2_buffer v4l2_buffer;
729 struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
730 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
731 v4l2_buffer.type = queue->type;
732 v4l2_buffer.length = queue->num_planes;
733 v4l2_buffer.m.planes = planes;
734 v4l2_buffer.m.planes[0].bytesused = 0;
735 int ret = ioctl(queue->v4lfd, VIDIOC_DQBUF, &v4l2_buffer);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800736
Steve Chodc8f92a2021-03-03 17:43:38 -0800737 if (ret != 0 && errno != EAGAIN)
738 perror("VIDIOC_DQBUF failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800739
Steve Chodc8f92a2021-03-03 17:43:38 -0800740 *index = v4l2_buffer.index;
741 if (bytesused)
742 *bytesused = v4l2_buffer.m.planes[0].bytesused;
743 if (data_offset)
744 *data_offset = v4l2_buffer.m.planes[0].data_offset;
745 if (timestamp)
746 *timestamp = v4l2_buffer.timestamp.tv_usec;
747 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800748}
749
Steve Chodc8f92a2021-03-03 17:43:38 -0800750int encode(FILE* fp_input,
751 uint32_t file_format,
752 char* output_file_name,
753 struct queue* OUTPUT_queue,
754 struct queue* CAPTURE_queue,
755 uint32_t frames_to_decode) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500756 if (OUTPUT_queue->num_planes == 0 || OUTPUT_queue->num_planes > 3) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800757 fprintf(stderr, " unsupported number of planes: %d\n",
758 OUTPUT_queue->num_planes);
759 return -1;
760 }
761 fprintf(stderr, "encoding\n");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800762
Steve Chodc8f92a2021-03-03 17:43:38 -0800763 const int use_ivf = CAPTURE_queue->fourcc == v4l2_fourcc('V', 'P', '8', '0');
764 strcat(output_file_name, use_ivf ? ".ivf" : ".h264");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800765
Steve Chodc8f92a2021-03-03 17:43:38 -0800766 int ret = 0;
767 FILE* fp_output = fopen(output_file_name, "wb");
768 if (!fp_output) {
769 fprintf(stderr, "unable to write to file: %s\n", output_file_name);
770 ret = 1;
771 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800772
Steve Chodc8f92a2021-03-03 17:43:38 -0800773 // write header
774 if (use_ivf) {
775 struct ivf_file_header header;
776 header.signature = kIVFHeaderSignature;
777 header.version = 0;
778 header.header_length = sizeof(struct ivf_file_header);
779 header.fourcc = CAPTURE_queue->fourcc;
780 header.width = CAPTURE_queue->raw_width;
781 header.height = CAPTURE_queue->raw_height;
782 // hard coded 30fps
783 header.denominator = 30;
784 header.numerator = 1;
785 header.frame_cnt = frames_to_decode;
786 header.unused = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800787
Steve Chodc8f92a2021-03-03 17:43:38 -0800788 if (fwrite(&header, sizeof(struct ivf_file_header), 1, fp_output) != 1) {
789 fprintf(stderr, "unable to write ivf file header\n");
790 }
791 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800792
Steve Chodc8f92a2021-03-03 17:43:38 -0800793 struct timespec start, stop;
794 clock_gettime(CLOCK_REALTIME, &start);
795 if (!ret) {
796 // prime input by filling up the OUTPUT queue with raw frames
797 for (uint32_t i = 0; i < OUTPUT_queue->cnt; ++i) {
798 if (submit_raw_frame(fp_input, file_format, OUTPUT_queue, i)) {
799 fprintf(stderr, "unable to submit raw frame\n");
800 ret = 1;
801 }
802 }
803 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800804
Steve Chodc8f92a2021-03-03 17:43:38 -0800805 if (!ret) {
806 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_STREAMON, &OUTPUT_queue->type);
807 if (ret != 0)
808 perror("VIDIOC_STREAMON failed on OUTPUT");
809 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800810
Steve Chodc8f92a2021-03-03 17:43:38 -0800811 if (!ret) {
812 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_STREAMON, &CAPTURE_queue->type);
813 if (ret != 0)
814 perror("VIDIOC_STREAMON failed on CAPTURE");
815 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800816
Steve Chodc8f92a2021-03-03 17:43:38 -0800817 uint32_t cnt = OUTPUT_queue->cnt; // We pre-uploaded a few before.
818 if (!ret) {
819 while (cnt < frames_to_decode) {
820 // handle CAPTURE queue first
821 {
822 uint32_t index = 0;
823 uint32_t bytesused = 0;
824 uint32_t data_offset = 0;
825 uint64_t timestamp = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800826
Steve Chodc8f92a2021-03-03 17:43:38 -0800827 // first get the newly encoded frame
828 ret = dequeue_buffer(CAPTURE_queue, &index, &bytesused, &data_offset,
829 &timestamp);
830 if (ret != 0)
831 continue;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800832
Steve Chodc8f92a2021-03-03 17:43:38 -0800833 if (use_ivf) {
834 struct ivf_frame_header header;
835 header.size = bytesused - data_offset;
836 header.timestamp = timestamp;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800837
Steve Chodc8f92a2021-03-03 17:43:38 -0800838 if (fwrite(&header, sizeof(struct ivf_frame_header), 1, fp_output) !=
839 1) {
840 fprintf(stderr, "unable to write ivf frame header\n");
841 }
842 }
843 fwrite(CAPTURE_queue->buffers[index].start[0] + data_offset,
844 bytesused - data_offset, 1, fp_output);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800845
Steve Chodc8f92a2021-03-03 17:43:38 -0800846 // done with the buffer, queue it back up
847 queue_CAPTURE_buffer(CAPTURE_queue, index);
848 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800849
Steve Chodc8f92a2021-03-03 17:43:38 -0800850 // handle OUTPUT queue second
851 {
852 uint32_t index = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800853
Steve Chodc8f92a2021-03-03 17:43:38 -0800854 ret = dequeue_buffer(OUTPUT_queue, &index, 0, 0, 0);
855 if (ret != 0)
856 continue;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800857
Steve Chodc8f92a2021-03-03 17:43:38 -0800858 if (submit_raw_frame(fp_input, file_format, OUTPUT_queue, index))
859 break;
860 }
861 cnt++;
862 }
863 }
864 clock_gettime(CLOCK_REALTIME, &stop);
865 const double elapsed_ns =
866 (stop.tv_sec - start.tv_sec) * 1e9 + (stop.tv_nsec - start.tv_nsec);
867 const double fps = cnt * 1e9 / elapsed_ns;
868 printf("%d frames encoded in %fns (%ffps)\n", cnt, elapsed_ns, fps);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800869
Steve Chodc8f92a2021-03-03 17:43:38 -0800870 if (fp_output) {
871 fclose(fp_output);
872 }
873 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800874}
875
Steve Chodc8f92a2021-03-03 17:43:38 -0800876static void print_help(const char* argv0) {
877 printf("usage: %s [OPTIONS]\n", argv0);
878 printf(" -f, --file file to encode\n");
879 printf(" -i, --file_format pixel format of the file (yv12, nv12)\n");
880 printf(
881 " -o, --output output file name (will get a .h264 or .ivf suffix "
882 "added)\n");
883 printf(" -w, --width width of image\n");
884 printf(" -h, --height height of image\n");
885 printf(" -m, --max max number of frames to decode\n");
886 printf(" -r, --rate frames per second\n");
887 printf(" -b, --bitrate bits per second\n");
888 printf(" -g, --gop gop length\n");
889 printf(" -c, --codec codec\n");
890 printf(" -e, --end_usage rate control mode: VBR (default), CBR\n");
891 printf(" -v, --verbose verbose capabilities\n");
892 printf(" -q, --buffer_fmt OUTPUT queue format\n");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800893}
894
895static const struct option longopts[] = {
Steve Chodc8f92a2021-03-03 17:43:38 -0800896 {"file", required_argument, NULL, 'f'},
897 {"file_format", required_argument, NULL, 'i'},
898 {"output", required_argument, NULL, 'o'},
899 {"width", required_argument, NULL, 'w'},
900 {"height", required_argument, NULL, 'h'},
901 {"max", required_argument, NULL, 'm'},
902 {"fps", required_argument, NULL, 'r'},
903 {"bitrate", required_argument, NULL, 'b'},
904 {"gop", required_argument, NULL, 'g'},
905 {"codec", required_argument, NULL, 'c'},
906 {"end_usage", required_argument, NULL, 'e'},
907 {"verbose", no_argument, NULL, 'v'},
908 {"buffer_fmt", required_argument, NULL, 'q'},
909 {0, 0, 0, 0},
Fritz Koenigcdba6532021-01-22 16:11:17 -0800910};
911
Steve Chodc8f92a2021-03-03 17:43:38 -0800912int main(int argc, char* argv[]) {
913 uint32_t file_format = v4l2_fourcc('N', 'V', '1', '2');
914 uint32_t OUTPUT_format = v4l2_fourcc('N', 'V', '1', '2');
915 uint32_t CAPTURE_format = v4l2_fourcc('H', '2', '6', '4');
916 char* file_name = NULL;
917 char* output_file_name = NULL;
918 uint32_t width = 0;
919 uint32_t height = 0;
920 uint32_t frames_to_decode = 0;
921 uint32_t framerate = 30;
922 int verbose_capabilities = 0;
923 int c;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800924
Steve Chodc8f92a2021-03-03 17:43:38 -0800925 struct encoder_cfg cfg = {
926 .gop_size = 20,
927 .bitrate = 1000,
928 .h264_entropy_mode = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
929 .h264_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
930 .h264_profile = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
931 .header_mode = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
932 .bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR};
Fritz Koenigcdba6532021-01-22 16:11:17 -0800933
Steve Chodc8f92a2021-03-03 17:43:38 -0800934 while ((c = getopt_long(argc, argv, "f:i:o:w:h:m:r:b:g:c:e:vq:", longopts,
935 NULL)) != -1) {
936 switch (c) {
937 case 'f':
938 file_name = strdup(optarg);
939 break;
940 case 'i':
941 if (strlen(optarg) == 4) {
942 file_format = v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
943 toupper(optarg[2]), toupper(optarg[3]));
944 printf("using (%s) as the file format\n", optarg);
945 }
946 break;
947 case 'o':
948 output_file_name = strdup(optarg);
949 break;
950 case 'w':
951 width = atoi(optarg);
952 break;
953 case 'h':
954 height = atoi(optarg);
955 break;
956 case 'm':
957 frames_to_decode = atoi(optarg);
958 break;
959 case 'r':
960 framerate = atoi(optarg);
961 break;
962 case 'b':
963 cfg.bitrate = atoi(optarg);
964 break;
965 case 'g':
966 cfg.gop_size = atoi(optarg);
967 break;
968 case 'c':
969 if (strlen(optarg) == 4) {
970 CAPTURE_format = v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
971 toupper(optarg[2]), toupper(optarg[3]));
972 printf("using (%s) as the codec\n", optarg);
973 }
974 break;
975 case 'e':
976 if (strlen(optarg) == 3 && toupper(optarg[0]) == 'C' &&
977 toupper(optarg[1]) == 'B' && toupper(optarg[2]) == 'R') {
978 cfg.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
979 }
980 break;
981 case 'v':
982 verbose_capabilities = 1;
983 break;
984 case 'q':
985 if (strlen(optarg) == 4) {
986 OUTPUT_format = v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
987 toupper(optarg[2]), toupper(optarg[3]));
Miguel Casas4fe876d2021-03-05 22:26:31 -0500988 printf("using (%s) as the OUTPUT_queue buffer format\n", optarg);
Steve Chodc8f92a2021-03-03 17:43:38 -0800989 }
990 break;
991 default:
992 break;
993 }
994 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800995
Steve Chodc8f92a2021-03-03 17:43:38 -0800996 if (!file_name || !output_file_name || width == 0 || height == 0) {
997 fprintf(stderr, "Invalid parameters!\n");
998 print_help(argv[0]);
999 exit(1);
1000 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001001
Steve Chodc8f92a2021-03-03 17:43:38 -08001002 FILE* fp = fopen(file_name, "rb");
1003 if (!fp) {
1004 fprintf(stderr, "%s: unable to open file.\n", file_name);
1005 exit(1);
1006 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001007
Steve Chodc8f92a2021-03-03 17:43:38 -08001008 if (!frames_to_decode) {
1009 fseek(fp, 0, SEEK_END);
1010 uint64_t length = ftell(fp);
1011 uint32_t frame_size = (3 * width * height) >> 1;
1012 frames_to_decode = length / frame_size;
1013 fseek(fp, 0, SEEK_SET);
1014 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001015
Steve Chodc8f92a2021-03-03 17:43:38 -08001016 fprintf(
1017 stderr, "encoding %d frames using %s bitrate control\n", frames_to_decode,
1018 (cfg.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) ? "CBR" : "VBR");
Fritz Koenigcdba6532021-01-22 16:11:17 -08001019
Steve Chodc8f92a2021-03-03 17:43:38 -08001020 int v4lfd = open(kEncodeDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC);
1021 if (v4lfd < 0) {
1022 fprintf(stderr, "Unable to open device file: %s\n", kEncodeDevice);
1023 exit(EXIT_FAILURE);
1024 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001025
Steve Chodc8f92a2021-03-03 17:43:38 -08001026 if (capabilities(v4lfd, OUTPUT_format, CAPTURE_format,
1027 verbose_capabilities) != 0) {
1028 fprintf(stderr, "Capabilities not present for encode.\n");
1029 exit(EXIT_FAILURE);
1030 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001031
Steve Chodc8f92a2021-03-03 17:43:38 -08001032 struct queue OUTPUT_queue = {.v4lfd = v4lfd,
1033 .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
1034 .fourcc = OUTPUT_format,
1035 .raw_width = width,
1036 .raw_height = height,
1037 .frame_cnt = 0,
1038 .num_planes = 1,
1039 .framerate = framerate};
Fritz Koenigcdba6532021-01-22 16:11:17 -08001040
Steve Chodc8f92a2021-03-03 17:43:38 -08001041 struct queue CAPTURE_queue = {.v4lfd = v4lfd,
1042 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
1043 .fourcc = CAPTURE_format,
1044 .raw_width = width,
1045 .raw_height = height,
1046 .num_planes = 1,
1047 .framerate = framerate};
Fritz Koenigcdba6532021-01-22 16:11:17 -08001048
Steve Chodc8f92a2021-03-03 17:43:38 -08001049 int ret = Initialization(&OUTPUT_queue, &CAPTURE_queue);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001050
Steve Chodc8f92a2021-03-03 17:43:38 -08001051 // not all configurations are supported, so we don't need to track
1052 // the return value
1053 if (!ret) {
1054 configure_common(v4lfd, &cfg);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001055
Steve Chodc8f92a2021-03-03 17:43:38 -08001056 if (v4l2_fourcc('H', '2', '6', '4') == CAPTURE_format)
1057 configure_h264(v4lfd, &cfg);
1058 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001059
Miguel Casas4fe876d2021-03-05 22:26:31 -05001060 if (!ret) {
Steve Chodc8f92a2021-03-03 17:43:38 -08001061 ret = encode(fp, file_format, output_file_name, &OUTPUT_queue,
1062 &CAPTURE_queue, frames_to_decode);
Miguel Casas4fe876d2021-03-05 22:26:31 -05001063 }
1064
1065 // On kukui (MTK8183), removing this sleep() causes a hard crash.
1066 // TODO(b/182200028): Investigate why and remove it.
1067 sleep(1);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001068
Steve Chodc8f92a2021-03-03 17:43:38 -08001069 cleanup_queue(&OUTPUT_queue);
1070 cleanup_queue(&CAPTURE_queue);
1071 close(v4lfd);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001072
Steve Chodc8f92a2021-03-03 17:43:38 -08001073 return 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -08001074}