blob: 65e5c7ea8dc0ce490fd298fb54de30fac6599973 [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
Fritz Koenig526b5782021-03-11 16:13:22 -080051struct encoder_control {
52 uint32_t id;
53 uint32_t value;
54 uint32_t enabled;
55 char *name;
Fritz Koenigcdba6532021-01-22 16:11:17 -080056};
57
Fritz Koenig526b5782021-03-11 16:13:22 -080058#define ENCODER_CTL(ID, VALUE) \
59 {.id = ID, .value = VALUE, .enabled = 1, .name = #ID}
60
Fritz Koenigcdba6532021-01-22 16:11:17 -080061struct 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,
Fritz Koenig723a7c42021-03-11 19:41:13 -0800113 uint32_t CAPTURE_format) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800114 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 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800137}
138
Steve Chodc8f92a2021-03-03 17:43:38 -0800139int queue_OUTPUT_buffer(struct queue* queue,
140 struct mmap_buffers* buffers,
141 uint32_t index) {
142 // compute frame timestamp
143 const float usec_per_frame = (1.0 / queue->framerate) * 1000000;
144 const uint64_t usec_time_stamp = usec_per_frame * queue->frame_cnt;
145 const uint64_t tv_sec = usec_time_stamp / 1000000;
Fritz Koenig62733512021-02-17 18:36:21 -0800146
Steve Chodc8f92a2021-03-03 17:43:38 -0800147 struct v4l2_buffer v4l2_buffer;
148 struct v4l2_plane planes[VIDEO_MAX_PLANES];
149 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
Fritz Koenig62733512021-02-17 18:36:21 -0800150
Steve Chodc8f92a2021-03-03 17:43:38 -0800151 v4l2_buffer.index = index;
152 v4l2_buffer.type = queue->type;
153 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
154 v4l2_buffer.length = queue->num_planes;
155 v4l2_buffer.timestamp.tv_sec = tv_sec;
156 v4l2_buffer.timestamp.tv_usec = usec_time_stamp - tv_sec;
157 v4l2_buffer.sequence = queue->frame_cnt;
158 v4l2_buffer.m.planes = planes;
159 for (uint32_t i = 0; i < queue->num_planes; ++i) {
160 v4l2_buffer.m.planes[i].length = buffers[index].length[i];
161 v4l2_buffer.m.planes[i].bytesused = buffers[index].length[i];
162 v4l2_buffer.m.planes[i].data_offset = 0;
163 }
Fritz Koenig62733512021-02-17 18:36:21 -0800164
Steve Chodc8f92a2021-03-03 17:43:38 -0800165 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
166 if (ret != 0) {
167 perror("VIDIOC_QBUF failed");
168 return -1;
169 }
Fritz Koenig62733512021-02-17 18:36:21 -0800170
Steve Chodc8f92a2021-03-03 17:43:38 -0800171 queue->frame_cnt++;
Fritz Koenig62733512021-02-17 18:36:21 -0800172
Steve Chodc8f92a2021-03-03 17:43:38 -0800173 return 0;
Fritz Koenig62733512021-02-17 18:36:21 -0800174}
175
Miguel Casas266b2e42021-02-05 22:05:58 -0500176// This function copies the contents pointed by |fp| tp |queue|s |index| buffer.
Steve Chodc8f92a2021-03-03 17:43:38 -0800177int submit_raw_frame_in_bulk(FILE* fp, struct queue* queue, uint32_t index) {
178 assert(queue->num_planes == 1 || queue->num_planes == 2);
179 assert(queue->raw_width == queue->encoded_width);
180 // TODO: the code below assumes NV12 because the Chroma planes are copied in
181 // one call. Extend to YV12 if ever the need arises.
182 assert(queue->fourcc == v4l2_fourcc('N', 'V', '1', '2'));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800183
Steve Chodc8f92a2021-03-03 17:43:38 -0800184 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800185
Steve Chodc8f92a2021-03-03 17:43:38 -0800186 // read y plane first
187 size_t frame_size = queue->raw_width * queue->raw_height;
188 uint8_t* buffer = buffers[index].start[0];
Fritz Koenigcdba6532021-01-22 16:11:17 -0800189
Steve Chodc8f92a2021-03-03 17:43:38 -0800190 if (fread(buffer, frame_size, 1, fp) != 1) {
191 fprintf(stderr, "unable to read luma frame\n");
192 return -1;
193 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800194
Steve Chodc8f92a2021-03-03 17:43:38 -0800195 // now read uv
196 frame_size >>= 1;
197 if (queue->num_planes == 2)
198 buffer = buffers[index].start[1];
199 else
200 buffer += queue->encoded_width * queue->encoded_height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800201
Steve Chodc8f92a2021-03-03 17:43:38 -0800202 if (fread(buffer, frame_size, 1, fp) != 1) {
203 fprintf(stderr, "unable to read chroma frame\n");
204 return -1;
205 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800206
Steve Chodc8f92a2021-03-03 17:43:38 -0800207 return queue_OUTPUT_buffer(queue, buffers, index);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800208}
209
Miguel Casas8ed87472021-02-05 22:12:07 -0500210// This function copies the contents pointed by |fp| to |queue|s |index| buffer.
Steve Chodc8f92a2021-03-03 17:43:38 -0800211int submit_raw_frame_row_by_row(FILE* fp,
212 uint32_t file_format,
213 struct queue* queue,
214 uint32_t index) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500215 assert(queue->num_planes == 1 || queue->num_planes == 3);
Steve Chodc8f92a2021-03-03 17:43:38 -0800216 assert(queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') ||
Miguel Casas4fe876d2021-03-05 22:26:31 -0500217 queue->fourcc == v4l2_fourcc('Y', 'M', '1', '2') ||
Steve Chodc8f92a2021-03-03 17:43:38 -0800218 queue->fourcc == v4l2_fourcc('N', 'V', '1', '2'));
Miguel Casas4fe876d2021-03-05 22:26:31 -0500219 assert(file_format == v4l2_fourcc('Y', 'V', '1', '2'));
Miguel Casas8ed87472021-02-05 22:12:07 -0500220
Steve Chodc8f92a2021-03-03 17:43:38 -0800221 struct mmap_buffers* buffers = queue->buffers;
Miguel Casas8ed87472021-02-05 22:12:07 -0500222
Steve Chodc8f92a2021-03-03 17:43:38 -0800223 // Read Y plane first, row by row.
224 uint8_t* buffer = buffers[index].start[0];
225 for (int row = 0; row < queue->raw_height; ++row) {
226 if (fread(buffer, queue->raw_width, 1, fp) != 1) {
227 fprintf(stderr, "unable to read luma row\n");
228 return -1;
229 }
230 buffer += queue->encoded_width;
231 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500232
Steve Chodc8f92a2021-03-03 17:43:38 -0800233 // Now read the U and V planes.
Miguel Casas4fe876d2021-03-05 22:26:31 -0500234 if ((queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') ||
235 queue->fourcc == v4l2_fourcc('Y', 'M', '1', '2')) &&
Steve Chodc8f92a2021-03-03 17:43:38 -0800236 file_format == v4l2_fourcc('Y', 'V', '1', '2')) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500237 assert((queue->fourcc == v4l2_fourcc('Y', 'M', '1', '2') &&
238 queue->num_planes == 3) ||
239 (queue->fourcc == v4l2_fourcc('Y', 'V', '1', '2') &&
240 queue->num_planes == 1));
241
242 buffer = buffers[index].start[1];
243 const uint32_t stride_u = queue->strides[1];
244
245 for (int row = 0; row < queue->raw_height / 2; ++row) {
246 if (fread(buffer, queue->raw_width / 2, 1, fp) != 1) {
247 fprintf(stderr, "unable to read chroma v row\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800248 return -1;
249 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500250 buffer += stride_u;
Steve Chodc8f92a2021-03-03 17:43:38 -0800251 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500252
Miguel Casas4fe876d2021-03-05 22:26:31 -0500253 uint32_t stride_v = stride_u;
254 if (queue->num_planes == 3) {
255 buffer = buffers[index].start[2];
256 stride_v = queue->strides[2];
Steve Chodc8f92a2021-03-03 17:43:38 -0800257 } else {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500258 assert(queue->num_planes == 1);
Steve Chodc8f92a2021-03-03 17:43:38 -0800259 buffer = buffers[index].start[0] +
260 5 * queue->encoded_width * queue->encoded_height / 4;
261 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500262
Miguel Casas4fe876d2021-03-05 22:26:31 -0500263 for (int row = 0; row < queue->raw_height / 2; ++row) {
264 if (fread(buffer, queue->raw_width / 2, 1, fp) != 1) {
265 fprintf(stderr, "unable to read chroma u row\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800266 return -1;
267 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500268 buffer += stride_v;
Steve Chodc8f92a2021-03-03 17:43:38 -0800269 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500270
Steve Chodc8f92a2021-03-03 17:43:38 -0800271 } else if (queue->fourcc == v4l2_fourcc('N', 'V', '1', '2') &&
272 file_format == v4l2_fourcc('Y', 'V', '1', '2') &&
273 queue->num_planes == 1) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500274 assert(queue->num_planes == 1);
275
276 // Copy all chroma samples from |fp| one by one in even |buffer| positions,
277 // then on the second loop iteration, move |buffer| one position right and
278 // copy from |fp| into the odd |buffer| positions.
Steve Chodc8f92a2021-03-03 17:43:38 -0800279 const int kNumPlanes = 2u;
280 for (int plane = 0; plane < kNumPlanes; ++plane) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500281 buffer = buffers[index].start[0] +
282 queue->encoded_width * queue->encoded_height + plane;
283 const uint32_t row_padding = queue->encoded_width - queue->raw_width;
Steve Chodc8f92a2021-03-03 17:43:38 -0800284 for (int row = 0; row < queue->raw_height / 4; ++row) {
285 for (int col = 0; col < queue->raw_width / 2; ++col) {
286 if (fread(buffer, 1 /*size */, 1 /*nmemb*/, fp) != 1) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500287 fprintf(stderr, "unable to read chroma v byte\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800288 return -1;
289 }
290 buffer += 2;
291 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500292 buffer += row_padding;
Miguel Casas8ed87472021-02-05 22:12:07 -0500293
Steve Chodc8f92a2021-03-03 17:43:38 -0800294 for (int col = 0; col < queue->raw_width / 2; ++col) {
295 if (fread(buffer, 1 /*size */, 1 /*nmemb*/, fp) != 1) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500296 fprintf(stderr, "unable to read chroma u byte\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800297 return -1;
298 }
299 buffer += 2;
300 }
Miguel Casas4fe876d2021-03-05 22:26:31 -0500301 buffer += row_padding;
Steve Chodc8f92a2021-03-03 17:43:38 -0800302 }
Steve Chodc8f92a2021-03-03 17:43:38 -0800303 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500304
Steve Chodc8f92a2021-03-03 17:43:38 -0800305 } else {
306 fprintf(stderr,
307 "combination of queue format, number of planes, and file format "
308 "unsupported\n");
309 return -1;
310 }
Miguel Casas8ed87472021-02-05 22:12:07 -0500311
Steve Chodc8f92a2021-03-03 17:43:38 -0800312 return queue_OUTPUT_buffer(queue, buffers, index);
Miguel Casas8ed87472021-02-05 22:12:07 -0500313}
314
Miguel Casas266b2e42021-02-05 22:05:58 -0500315// This function copies the content of |fp| into the |index|th buffer of
316// |queue|. Depending on |file_format| and the |queue| format, and the raw and
317// encoded sizes of the latter, we might do a copy in bulk or need conversion.
Steve Chodc8f92a2021-03-03 17:43:38 -0800318int submit_raw_frame(FILE* fp,
319 uint32_t file_format,
320 struct queue* queue,
321 uint32_t index) {
322 if (queue->raw_width == queue->encoded_width &&
323 queue->fourcc == file_format &&
324 queue->fourcc == v4l2_fourcc('N', 'V', '1', '2')) {
325 return submit_raw_frame_in_bulk(fp, queue, index);
326 }
Miguel Casas266b2e42021-02-05 22:05:58 -0500327
Steve Chodc8f92a2021-03-03 17:43:38 -0800328 return submit_raw_frame_row_by_row(fp, file_format, queue, index);
Miguel Casas266b2e42021-02-05 22:05:58 -0500329}
330
Steve Chodc8f92a2021-03-03 17:43:38 -0800331void cleanup_queue(struct queue* queue) {
332 if (queue->cnt) {
333 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800334
Steve Chodc8f92a2021-03-03 17:43:38 -0800335 for (uint32_t i = 0; i < queue->cnt; i++)
336 for (uint32_t j = 0; j < queue->num_planes; j++) {
337 munmap(buffers[i].start[j], buffers[i].length[j]);
338 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800339
Steve Chodc8f92a2021-03-03 17:43:38 -0800340 free(queue->buffers);
341 queue->cnt = 0;
342 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800343}
344
Steve Chodc8f92a2021-03-03 17:43:38 -0800345int request_mmap_buffers(struct queue* queue,
346 struct v4l2_requestbuffers* reqbuf) {
347 const int v4lfd = queue->v4lfd;
348 const uint32_t buffer_alloc = reqbuf->count * sizeof(struct mmap_buffers);
349 struct mmap_buffers* buffers = (struct mmap_buffers*)malloc(buffer_alloc);
350 memset(buffers, 0, buffer_alloc);
351 queue->buffers = buffers;
352 queue->cnt = reqbuf->count;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800353
Steve Chodc8f92a2021-03-03 17:43:38 -0800354 int ret;
355 for (uint32_t i = 0; i < reqbuf->count; i++) {
356 struct v4l2_buffer buffer;
357 struct v4l2_plane planes[VIDEO_MAX_PLANES];
358 memset(&buffer, 0, sizeof(buffer));
359 buffer.type = reqbuf->type;
360 buffer.memory = V4L2_MEMORY_MMAP;
361 buffer.index = i;
362 buffer.length = queue->num_planes;
363 buffer.m.planes = planes;
364 ret = ioctl(v4lfd, VIDIOC_QUERYBUF, &buffer);
365 if (ret != 0) {
366 printf("VIDIOC_QUERYBUF failed: %d\n", ret);
367 break;
368 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800369
Steve Chodc8f92a2021-03-03 17:43:38 -0800370 for (uint32_t j = 0; j < queue->num_planes; j++) {
371 buffers[i].length[j] = buffer.m.planes[j].length;
372 buffers[i].start[j] =
373 mmap(NULL, buffer.m.planes[j].length, PROT_READ | PROT_WRITE,
374 MAP_SHARED, v4lfd, buffer.m.planes[j].m.mem_offset);
375 if (MAP_FAILED == buffers[i].start[j]) {
376 fprintf(stderr,
377 "failed to mmap buffer of length(%d) and offset(0x%x)\n",
378 buffer.m.planes[j].length, buffer.m.planes[j].m.mem_offset);
379 }
380 }
381 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800382
Steve Chodc8f92a2021-03-03 17:43:38 -0800383 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800384}
385
Steve Chodc8f92a2021-03-03 17:43:38 -0800386int queue_CAPTURE_buffer(struct queue* queue, uint32_t index) {
387 struct mmap_buffers* buffers = queue->buffers;
388 struct v4l2_buffer v4l2_buffer;
389 struct v4l2_plane planes[VIDEO_MAX_PLANES];
390 memset(&v4l2_buffer, 0, sizeof v4l2_buffer);
391 memset(&planes, 0, sizeof planes);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800392
Steve Chodc8f92a2021-03-03 17:43:38 -0800393 v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
394 v4l2_buffer.memory = V4L2_MEMORY_MMAP;
395 v4l2_buffer.index = index;
396 v4l2_buffer.m.planes = planes;
397 v4l2_buffer.length = queue->num_planes;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800398
Steve Chodc8f92a2021-03-03 17:43:38 -0800399 v4l2_buffer.m.planes[0].length = buffers[index].length[0];
400 v4l2_buffer.m.planes[0].bytesused = buffers[index].length[0];
401 v4l2_buffer.m.planes[0].data_offset = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800402
Steve Chodc8f92a2021-03-03 17:43:38 -0800403 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
404 if (ret != 0) {
405 perror("VIDIOC_QBUF failed");
406 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800407
Steve Chodc8f92a2021-03-03 17:43:38 -0800408 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800409}
410
411// 4.5.2.5. Initialization
Steve Chodc8f92a2021-03-03 17:43:38 -0800412int Initialization(struct queue* OUTPUT_queue, struct queue* CAPTURE_queue) {
413 int ret = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800414
Steve Chodc8f92a2021-03-03 17:43:38 -0800415 // 1. Set the coded format on the CAPTURE queue via VIDIOC_S_FMT().
416 if (!ret) {
417 struct v4l2_format fmt;
418 memset(&fmt, 0, sizeof(fmt));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800419
Steve Chodc8f92a2021-03-03 17:43:38 -0800420 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
421 fmt.fmt.pix_mp.pixelformat = CAPTURE_queue->fourcc;
422 fmt.fmt.pix_mp.width = CAPTURE_queue->raw_width;
423 fmt.fmt.pix_mp.height = CAPTURE_queue->raw_height;
424 fmt.fmt.pix_mp.plane_fmt[0].sizeimage = kInputbufferMaxSize;
425 fmt.fmt.pix_mp.num_planes = 1;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800426
Steve Chodc8f92a2021-03-03 17:43:38 -0800427 int ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_S_FMT, &fmt);
428 if (ret != 0)
429 perror("VIDIOC_S_FMT failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800430
Steve Chodc8f92a2021-03-03 17:43:38 -0800431 CAPTURE_queue->encoded_width = fmt.fmt.pix_mp.width;
432 CAPTURE_queue->encoded_height = fmt.fmt.pix_mp.height;
433 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800434
Steve Chodc8f92a2021-03-03 17:43:38 -0800435 // 3. Set the raw source format on the OUTPUT queue via VIDIOC_S_FMT().
436 if (!ret) {
437 struct v4l2_format fmt;
438 memset(&fmt, 0, sizeof(fmt));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800439
Steve Chodc8f92a2021-03-03 17:43:38 -0800440 fmt.type = OUTPUT_queue->type;
441 fmt.fmt.pix_mp.pixelformat = OUTPUT_queue->fourcc;
442 fmt.fmt.pix_mp.width = OUTPUT_queue->raw_width;
443 fmt.fmt.pix_mp.height = OUTPUT_queue->raw_height;
444 fmt.fmt.pix_mp.num_planes = 1;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800445
Steve Chodc8f92a2021-03-03 17:43:38 -0800446 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_FMT, &fmt);
447 if (ret != 0)
448 perror("VIDIOC_S_FMT failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800449
Steve Chodc8f92a2021-03-03 17:43:38 -0800450 OUTPUT_queue->encoded_width = fmt.fmt.pix_mp.width;
451 OUTPUT_queue->encoded_height = fmt.fmt.pix_mp.height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800452
Steve Chodc8f92a2021-03-03 17:43:38 -0800453 OUTPUT_queue->num_planes = fmt.fmt.pix_mp.num_planes;
Miguel Casas4fe876d2021-03-05 22:26:31 -0500454 assert(OUTPUT_queue->num_planes <= VIDEO_MAX_PLANES);
455 printf("OUTPUT_queue %d planes, (%dx%d)\n", OUTPUT_queue->num_planes,
456 OUTPUT_queue->encoded_width, OUTPUT_queue->encoded_height);
457 for (int plane = 0; plane < OUTPUT_queue->num_planes; ++plane) {
458 OUTPUT_queue->strides[plane] =
459 fmt.fmt.pix_mp.plane_fmt[plane].bytesperline;
460 printf(" plane %d, stride %d\n", plane, OUTPUT_queue->strides[plane]);
461 }
Steve Chodc8f92a2021-03-03 17:43:38 -0800462 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800463
Steve Chodc8f92a2021-03-03 17:43:38 -0800464 // 4. Set the raw frame interval on the OUTPUT queue via VIDIOC_S_PARM()
465 if (!ret) {
466 struct v4l2_streamparm parms;
467 memset(&parms, 0, sizeof(parms));
468 parms.type = OUTPUT_queue->type;
469 // Note that we are provided "frames per second" but V4L2 expects "time per
470 // frame"; hence we provide the reciprocal of the framerate here.
471 parms.parm.output.timeperframe.numerator = 1;
472 parms.parm.output.timeperframe.denominator = OUTPUT_queue->framerate;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800473
Steve Chodc8f92a2021-03-03 17:43:38 -0800474 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_PARM, &parms);
475 if (ret != 0)
476 perror("VIDIOC_S_PARAM failed");
477 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800478
Steve Chodc8f92a2021-03-03 17:43:38 -0800479 // 6. Optional. Set the visible resolution for the stream metadata via
480 // VIDIOC_S_SELECTION() on the OUTPUT queue if it is desired to be
481 // different than the full OUTPUT resolution.
482 if (!ret) {
483 struct v4l2_selection selection_arg;
484 memset(&selection_arg, 0, sizeof(selection_arg));
485 selection_arg.type = OUTPUT_queue->type;
486 selection_arg.target = V4L2_SEL_TGT_CROP;
487 selection_arg.r.left = 0;
488 selection_arg.r.top = 0;
489 selection_arg.r.width = OUTPUT_queue->raw_width;
490 selection_arg.r.height = OUTPUT_queue->raw_height;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800491
Steve Chodc8f92a2021-03-03 17:43:38 -0800492 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_SELECTION, &selection_arg);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800493
Steve Chodc8f92a2021-03-03 17:43:38 -0800494 if (ret != 0)
495 perror("VIDIOC_S_SELECTION failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800496
Steve Chodc8f92a2021-03-03 17:43:38 -0800497 // TODO(fritz) : check returned values are same as sent values
498 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800499
Steve Chodc8f92a2021-03-03 17:43:38 -0800500 // 7. Allocate buffers for both OUTPUT and CAPTURE via VIDIOC_REQBUFS().
501 // This may be performed in any order.
502 if (!ret) {
503 struct v4l2_requestbuffers reqbuf;
504 memset(&reqbuf, 0, sizeof(reqbuf));
505 reqbuf.count = kRequestBufferCount;
506 reqbuf.type = OUTPUT_queue->type;
507 reqbuf.memory = V4L2_MEMORY_MMAP;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800508
Steve Chodc8f92a2021-03-03 17:43:38 -0800509 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
510 if (ret != 0)
511 perror("VIDIOC_REQBUFS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800512
Steve Chodc8f92a2021-03-03 17:43:38 -0800513 printf(
514 "%d buffers requested, %d buffers for uncompressed frames returned\n",
515 kRequestBufferCount, reqbuf.count);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800516
Steve Chodc8f92a2021-03-03 17:43:38 -0800517 ret = request_mmap_buffers(OUTPUT_queue, &reqbuf);
518 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800519
Steve Chodc8f92a2021-03-03 17:43:38 -0800520 if (!ret) {
521 struct v4l2_requestbuffers reqbuf;
522 memset(&reqbuf, 0, sizeof(reqbuf));
523 reqbuf.count = kRequestBufferCount;
524 reqbuf.type = CAPTURE_queue->type;
525 reqbuf.memory = V4L2_MEMORY_MMAP;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800526
Steve Chodc8f92a2021-03-03 17:43:38 -0800527 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
528 if (ret != 0)
529 perror("VIDIOC_REQBUFS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800530
Steve Chodc8f92a2021-03-03 17:43:38 -0800531 printf("%d buffers requested, %d buffers for compressed frames returned\n",
532 kRequestBufferCount, reqbuf.count);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800533
Steve Chodc8f92a2021-03-03 17:43:38 -0800534 ret = request_mmap_buffers(CAPTURE_queue, &reqbuf);
535 for (uint32_t i = 0; i < reqbuf.count; i++) {
536 queue_CAPTURE_buffer(CAPTURE_queue, i);
537 }
538 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800539
Steve Chodc8f92a2021-03-03 17:43:38 -0800540 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800541}
542
Fritz Koenig526b5782021-03-11 16:13:22 -0800543void disable_unsupported_controls(int v4lfd,
544 struct encoder_control* encoder_ctrl) {
545 while (encoder_ctrl->id != 0) {
546 struct v4l2_query_ext_ctrl query_ext_ctrl;
547 memset(&query_ext_ctrl, 0, sizeof(query_ext_ctrl));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800548
Fritz Koenig526b5782021-03-11 16:13:22 -0800549 query_ext_ctrl.id = V4L2_CTRL_CLASS_MPEG | encoder_ctrl->id;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800550
Fritz Koenig526b5782021-03-11 16:13:22 -0800551 int ret = ioctl(v4lfd, VIDIOC_QUERY_EXT_CTRL, &query_ext_ctrl);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800552
Fritz Koenig526b5782021-03-11 16:13:22 -0800553 encoder_ctrl->enabled = (ret == 0);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800554
Fritz Koenig526b5782021-03-11 16:13:22 -0800555 if (ret != 0) {
556 fprintf(stderr,
557 "%s control does not exist on this platform\n" , encoder_ctrl->name);
558 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800559
Fritz Koenig526b5782021-03-11 16:13:22 -0800560 encoder_ctrl++;
561 };
Fritz Koenigcdba6532021-01-22 16:11:17 -0800562}
563
Fritz Koenig526b5782021-03-11 16:13:22 -0800564int enabled_control_count(const struct encoder_control* encoder_ctrl) {
565 int cnt = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800566
Fritz Koenig526b5782021-03-11 16:13:22 -0800567 for (; encoder_ctrl && encoder_ctrl->id != 0; ++encoder_ctrl)
568 cnt += encoder_ctrl->enabled;
569
570 return cnt;
571}
572
573void set_control_value(struct encoder_control* encoder_ctrl,
574 uint32_t id,
575 uint32_t value) {
576 for (; encoder_ctrl && encoder_ctrl->id != 0; ++encoder_ctrl) {
577 if (encoder_ctrl->id == id){
578 encoder_ctrl->value = value;
579 break;
580 }
581 }
582}
583
584int get_control_value(const struct encoder_control* encoder_ctrl, uint32_t id) {
585 for (; encoder_ctrl && encoder_ctrl->id != 0; ++encoder_ctrl) {
586 if (encoder_ctrl->id == id)
587 return encoder_ctrl->value;
588 };
589
590 return -1;
591}
592
593const char *get_control_name(const struct encoder_control* encoder_ctrl,
594 uint32_t id) {
595 for (; encoder_ctrl && encoder_ctrl->id != 0; ++encoder_ctrl) {
596 if (encoder_ctrl->id == id)
597 return encoder_ctrl->name;
598 };
599
600 return "unknown";
601}
602
603void fill_ext_controls(const struct encoder_control* encoder_ctrl,
604 struct v4l2_ext_control *ext_ctrl) {
605 int enabled_i = 0;
606 for (; encoder_ctrl && encoder_ctrl->id != 0; ++encoder_ctrl) {
607 if (encoder_ctrl->enabled) {
608 ext_ctrl[enabled_i].id = encoder_ctrl->id;
609 ext_ctrl[enabled_i].value = encoder_ctrl->value;
610 enabled_i++;
611 }
612 }
613}
614
615int set_ext_controls(int v4lfd, const struct encoder_control* encoder_ctrl) {
616 const int ctrl_cnt = enabled_control_count(encoder_ctrl);
617
618 struct v4l2_ext_control ext_ctrl[ctrl_cnt];
Steve Chodc8f92a2021-03-03 17:43:38 -0800619 memset(&ext_ctrl, 0, sizeof(ext_ctrl));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800620
Fritz Koenig526b5782021-03-11 16:13:22 -0800621 fill_ext_controls(encoder_ctrl, ext_ctrl);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800622
Steve Chodc8f92a2021-03-03 17:43:38 -0800623 struct v4l2_ext_controls ext_ctrls;
624 memset(&ext_ctrls, 0, sizeof(ext_ctrls));
Fritz Koenigcdba6532021-01-22 16:11:17 -0800625
Steve Chodc8f92a2021-03-03 17:43:38 -0800626 ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
Fritz Koenig526b5782021-03-11 16:13:22 -0800627 ext_ctrls.count = ctrl_cnt;
Steve Chodc8f92a2021-03-03 17:43:38 -0800628 ext_ctrls.controls = ext_ctrl;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800629
Fritz Koenig526b5782021-03-11 16:13:22 -0800630 const int set_ret = ioctl(v4lfd, VIDIOC_S_EXT_CTRLS, &ext_ctrls);
631 if (set_ret != 0)
Steve Chodc8f92a2021-03-03 17:43:38 -0800632 perror("VIDIOC_S_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800633
Fritz Koenig526b5782021-03-11 16:13:22 -0800634 for (uint32_t i = 0; i < ctrl_cnt; ++i)
Steve Chodc8f92a2021-03-03 17:43:38 -0800635 ext_ctrl[i].value = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800636
Fritz Koenig526b5782021-03-11 16:13:22 -0800637 const int get_ret = ioctl(v4lfd, VIDIOC_G_EXT_CTRLS, &ext_ctrls);
638 if (get_ret != 0)
Steve Chodc8f92a2021-03-03 17:43:38 -0800639 perror("VIDIOC_G_EXT_CTRLS failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800640
Fritz Koenigcdba6532021-01-22 16:11:17 -0800641
Fritz Koenig526b5782021-03-11 16:13:22 -0800642 for (int i = 0; i < ctrl_cnt; ++i) {
643 int ctrl_value = get_control_value(encoder_ctrl, ext_ctrl[i].id);
644 if (ext_ctrl[i].value != ctrl_value){
645 fprintf(stderr,
646 "control (%s) used a value of (%d) instead of the requested (%d)\n",
647 get_control_name(encoder_ctrl, ext_ctrl[i].id),
648 ext_ctrl[i].value,
649 ctrl_value);
650 }
651 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800652
Fritz Koenig526b5782021-03-11 16:13:22 -0800653 return set_ret | get_ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800654}
655
Steve Chodc8f92a2021-03-03 17:43:38 -0800656int dequeue_buffer(struct queue* queue,
657 uint32_t* index,
658 uint32_t* bytesused,
659 uint32_t* data_offset,
Fritz Koenig90820e22021-03-12 11:43:00 -0800660 uint64_t* timestamp,
661 uint32_t* flags) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800662 struct v4l2_buffer v4l2_buffer;
663 struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
664 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
665 v4l2_buffer.type = queue->type;
666 v4l2_buffer.length = queue->num_planes;
667 v4l2_buffer.m.planes = planes;
668 v4l2_buffer.m.planes[0].bytesused = 0;
669 int ret = ioctl(queue->v4lfd, VIDIOC_DQBUF, &v4l2_buffer);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800670
Steve Chodc8f92a2021-03-03 17:43:38 -0800671 if (ret != 0 && errno != EAGAIN)
672 perror("VIDIOC_DQBUF failed");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800673
Steve Chodc8f92a2021-03-03 17:43:38 -0800674 *index = v4l2_buffer.index;
675 if (bytesused)
676 *bytesused = v4l2_buffer.m.planes[0].bytesused;
677 if (data_offset)
678 *data_offset = v4l2_buffer.m.planes[0].data_offset;
679 if (timestamp)
680 *timestamp = v4l2_buffer.timestamp.tv_usec;
Fritz Koenig90820e22021-03-12 11:43:00 -0800681 if (flags)
682 *flags = v4l2_buffer.flags;
683
Steve Chodc8f92a2021-03-03 17:43:38 -0800684 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800685}
686
Fritz Koenig90820e22021-03-12 11:43:00 -0800687// 4.5.2.8. Drain
688void drain(struct queue* OUTPUT_queue,
689 struct queue* CAPTURE_queue) {
690
691 // 1. Begin the drain sequence by issuing VIDIOC_ENCODER_CMD().
692 struct v4l2_encoder_cmd cmd;
693 memset(&cmd, 0, sizeof(cmd));
694 cmd.cmd = V4L2_ENC_CMD_STOP;
695
696 // V4L2_ENC_CMD_STOP may not be supported, don't worry about result
697 ioctl(CAPTURE_queue->v4lfd, VIDIOC_ENCODER_CMD, &cmd);
698
699 // 2. Dequeue buffers
700 // The way the encode loop is set up, there shouldn't be any buffers
701 // left to dequeue.
702 {
703 uint32_t index = 0;
704 uint32_t bytesused = 0;
705 uint32_t flags = 0;
706
707 // check to make sure the queue is empty
708 dequeue_buffer(CAPTURE_queue, &index, &bytesused, 0, 0, &flags);
709
710 if (!(flags & V4L2_BUF_FLAG_LAST) && (bytesused != 0))
711 fprintf(stderr, "WARNING: CAPTURE queue did not clean up.\n");
712 }
713
714 // 3. Reset by issuing VIDIOC_STREAMOFF
715 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_STREAMOFF, &OUTPUT_queue->type);
716 if (ret != 0)
717 perror("VIDIOC_STREAMOFF failed on OUTPUT");
718
719 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_STREAMOFF, &CAPTURE_queue->type);
720 if (ret != 0)
721 perror("VIDIOC_STREAMOFF failed on CAPTURE");
722}
723
Steve Chodc8f92a2021-03-03 17:43:38 -0800724int encode(FILE* fp_input,
725 uint32_t file_format,
726 char* output_file_name,
727 struct queue* OUTPUT_queue,
728 struct queue* CAPTURE_queue,
Fritz Koenig90820e22021-03-12 11:43:00 -0800729 uint32_t frames_to_encode) {
Miguel Casas4fe876d2021-03-05 22:26:31 -0500730 if (OUTPUT_queue->num_planes == 0 || OUTPUT_queue->num_planes > 3) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800731 fprintf(stderr, " unsupported number of planes: %d\n",
732 OUTPUT_queue->num_planes);
733 return -1;
734 }
735 fprintf(stderr, "encoding\n");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800736
Steve Chodc8f92a2021-03-03 17:43:38 -0800737 const int use_ivf = CAPTURE_queue->fourcc == v4l2_fourcc('V', 'P', '8', '0');
738 strcat(output_file_name, use_ivf ? ".ivf" : ".h264");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800739
Steve Chodc8f92a2021-03-03 17:43:38 -0800740 int ret = 0;
741 FILE* fp_output = fopen(output_file_name, "wb");
742 if (!fp_output) {
743 fprintf(stderr, "unable to write to file: %s\n", output_file_name);
744 ret = 1;
745 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800746
Steve Chodc8f92a2021-03-03 17:43:38 -0800747 // write header
748 if (use_ivf) {
749 struct ivf_file_header header;
750 header.signature = kIVFHeaderSignature;
751 header.version = 0;
752 header.header_length = sizeof(struct ivf_file_header);
753 header.fourcc = CAPTURE_queue->fourcc;
754 header.width = CAPTURE_queue->raw_width;
755 header.height = CAPTURE_queue->raw_height;
756 // hard coded 30fps
757 header.denominator = 30;
758 header.numerator = 1;
Fritz Koenig90820e22021-03-12 11:43:00 -0800759 header.frame_cnt = frames_to_encode;
Steve Chodc8f92a2021-03-03 17:43:38 -0800760 header.unused = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800761
Steve Chodc8f92a2021-03-03 17:43:38 -0800762 if (fwrite(&header, sizeof(struct ivf_file_header), 1, fp_output) != 1) {
763 fprintf(stderr, "unable to write ivf file header\n");
764 }
765 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800766
Steve Chodc8f92a2021-03-03 17:43:38 -0800767 struct timespec start, stop;
768 clock_gettime(CLOCK_REALTIME, &start);
769 if (!ret) {
770 // prime input by filling up the OUTPUT queue with raw frames
771 for (uint32_t i = 0; i < OUTPUT_queue->cnt; ++i) {
772 if (submit_raw_frame(fp_input, file_format, OUTPUT_queue, i)) {
773 fprintf(stderr, "unable to submit raw frame\n");
774 ret = 1;
775 }
776 }
777 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800778
Steve Chodc8f92a2021-03-03 17:43:38 -0800779 if (!ret) {
780 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_STREAMON, &OUTPUT_queue->type);
781 if (ret != 0)
782 perror("VIDIOC_STREAMON failed on OUTPUT");
783 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800784
Steve Chodc8f92a2021-03-03 17:43:38 -0800785 if (!ret) {
786 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_STREAMON, &CAPTURE_queue->type);
787 if (ret != 0)
788 perror("VIDIOC_STREAMON failed on CAPTURE");
789 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800790
Steve Chodc8f92a2021-03-03 17:43:38 -0800791 uint32_t cnt = OUTPUT_queue->cnt; // We pre-uploaded a few before.
792 if (!ret) {
Fritz Koenig90820e22021-03-12 11:43:00 -0800793 while (cnt < frames_to_encode) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800794 // handle CAPTURE queue first
795 {
796 uint32_t index = 0;
797 uint32_t bytesused = 0;
798 uint32_t data_offset = 0;
799 uint64_t timestamp = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800800
Steve Chodc8f92a2021-03-03 17:43:38 -0800801 // first get the newly encoded frame
802 ret = dequeue_buffer(CAPTURE_queue, &index, &bytesused, &data_offset,
Fritz Koenig90820e22021-03-12 11:43:00 -0800803 &timestamp, 0);
Steve Chodc8f92a2021-03-03 17:43:38 -0800804 if (ret != 0)
805 continue;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800806
Steve Chodc8f92a2021-03-03 17:43:38 -0800807 if (use_ivf) {
808 struct ivf_frame_header header;
809 header.size = bytesused - data_offset;
810 header.timestamp = timestamp;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800811
Steve Chodc8f92a2021-03-03 17:43:38 -0800812 if (fwrite(&header, sizeof(struct ivf_frame_header), 1, fp_output) !=
813 1) {
814 fprintf(stderr, "unable to write ivf frame header\n");
815 }
816 }
817 fwrite(CAPTURE_queue->buffers[index].start[0] + data_offset,
818 bytesused - data_offset, 1, fp_output);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800819
Steve Chodc8f92a2021-03-03 17:43:38 -0800820 // done with the buffer, queue it back up
821 queue_CAPTURE_buffer(CAPTURE_queue, index);
822 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800823
Steve Chodc8f92a2021-03-03 17:43:38 -0800824 // handle OUTPUT queue second
825 {
826 uint32_t index = 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800827
Fritz Koenig90820e22021-03-12 11:43:00 -0800828 ret = dequeue_buffer(OUTPUT_queue, &index, 0, 0, 0, 0);
Steve Chodc8f92a2021-03-03 17:43:38 -0800829 if (ret != 0)
830 continue;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800831
Steve Chodc8f92a2021-03-03 17:43:38 -0800832 if (submit_raw_frame(fp_input, file_format, OUTPUT_queue, index))
833 break;
834 }
835 cnt++;
836 }
837 }
Fritz Koenig90820e22021-03-12 11:43:00 -0800838
839 drain(OUTPUT_queue, CAPTURE_queue);
840
Steve Chodc8f92a2021-03-03 17:43:38 -0800841 clock_gettime(CLOCK_REALTIME, &stop);
842 const double elapsed_ns =
843 (stop.tv_sec - start.tv_sec) * 1e9 + (stop.tv_nsec - start.tv_nsec);
844 const double fps = cnt * 1e9 / elapsed_ns;
845 printf("%d frames encoded in %fns (%ffps)\n", cnt, elapsed_ns, fps);
Fritz Koenigcdba6532021-01-22 16:11:17 -0800846
Steve Chodc8f92a2021-03-03 17:43:38 -0800847 if (fp_output) {
848 fclose(fp_output);
849 }
850 return ret;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800851}
852
Steve Chodc8f92a2021-03-03 17:43:38 -0800853static void print_help(const char* argv0) {
854 printf("usage: %s [OPTIONS]\n", argv0);
855 printf(" -f, --file file to encode\n");
856 printf(" -i, --file_format pixel format of the file (yv12, nv12)\n");
857 printf(
858 " -o, --output output file name (will get a .h264 or .ivf suffix "
859 "added)\n");
860 printf(" -w, --width width of image\n");
861 printf(" -h, --height height of image\n");
862 printf(" -m, --max max number of frames to decode\n");
863 printf(" -r, --rate frames per second\n");
864 printf(" -b, --bitrate bits per second\n");
865 printf(" -g, --gop gop length\n");
866 printf(" -c, --codec codec\n");
867 printf(" -e, --end_usage rate control mode: VBR (default), CBR\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800868 printf(" -q, --buffer_fmt OUTPUT queue format\n");
Fritz Koenigcdba6532021-01-22 16:11:17 -0800869}
870
871static const struct option longopts[] = {
Steve Chodc8f92a2021-03-03 17:43:38 -0800872 {"file", required_argument, NULL, 'f'},
873 {"file_format", required_argument, NULL, 'i'},
874 {"output", required_argument, NULL, 'o'},
875 {"width", required_argument, NULL, 'w'},
876 {"height", required_argument, NULL, 'h'},
877 {"max", required_argument, NULL, 'm'},
878 {"fps", required_argument, NULL, 'r'},
879 {"bitrate", required_argument, NULL, 'b'},
880 {"gop", required_argument, NULL, 'g'},
881 {"codec", required_argument, NULL, 'c'},
882 {"end_usage", required_argument, NULL, 'e'},
Steve Chodc8f92a2021-03-03 17:43:38 -0800883 {"buffer_fmt", required_argument, NULL, 'q'},
884 {0, 0, 0, 0},
Fritz Koenigcdba6532021-01-22 16:11:17 -0800885};
886
Steve Chodc8f92a2021-03-03 17:43:38 -0800887int main(int argc, char* argv[]) {
888 uint32_t file_format = v4l2_fourcc('N', 'V', '1', '2');
889 uint32_t OUTPUT_format = v4l2_fourcc('N', 'V', '1', '2');
890 uint32_t CAPTURE_format = v4l2_fourcc('H', '2', '6', '4');
891 char* file_name = NULL;
892 char* output_file_name = NULL;
893 uint32_t width = 0;
894 uint32_t height = 0;
Fritz Koenig90820e22021-03-12 11:43:00 -0800895 uint32_t frames_to_encode = 0;
Steve Chodc8f92a2021-03-03 17:43:38 -0800896 uint32_t framerate = 30;
Steve Chodc8f92a2021-03-03 17:43:38 -0800897 int c;
Fritz Koenigcdba6532021-01-22 16:11:17 -0800898
Fritz Koenig526b5782021-03-11 16:13:22 -0800899 struct encoder_control common_control_list[] = {
900 ENCODER_CTL(V4L2_CID_MPEG_VIDEO_BITRATE, 1000),
901 ENCODER_CTL(V4L2_CID_MPEG_VIDEO_GOP_SIZE, 20),
902 ENCODER_CTL(V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 1),
903 ENCODER_CTL(V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
904 V4L2_MPEG_VIDEO_BITRATE_MODE_VBR),
905 ENCODER_CTL(V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, 1500),
906 ENCODER_CTL(0, 0)
907 };
908
909 struct encoder_control h264_control_list[] = {
910 ENCODER_CTL(V4L2_CID_MPEG_VIDEO_H264_PROFILE,
911 V4L2_MPEG_VIDEO_H264_PROFILE_MAIN),
912 ENCODER_CTL(V4L2_CID_MPEG_VIDEO_H264_LEVEL,
913 V4L2_MPEG_VIDEO_H264_LEVEL_4_0),
914 ENCODER_CTL(V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
915 V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC),
916 ENCODER_CTL(V4L2_CID_MPEG_VIDEO_HEADER_MODE,
917 V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE),
918 ENCODER_CTL(0, 0)
919 };
Fritz Koenigcdba6532021-01-22 16:11:17 -0800920
Fritz Koenig723a7c42021-03-11 19:41:13 -0800921 while ((c = getopt_long(argc, argv, "f:i:o:w:h:m:r:b:g:c:e:q:", longopts,
Steve Chodc8f92a2021-03-03 17:43:38 -0800922 NULL)) != -1) {
923 switch (c) {
924 case 'f':
925 file_name = strdup(optarg);
926 break;
927 case 'i':
928 if (strlen(optarg) == 4) {
929 file_format = v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
930 toupper(optarg[2]), toupper(optarg[3]));
931 printf("using (%s) as the file format\n", optarg);
932 }
933 break;
934 case 'o':
935 output_file_name = strdup(optarg);
936 break;
937 case 'w':
938 width = atoi(optarg);
939 break;
940 case 'h':
941 height = atoi(optarg);
942 break;
943 case 'm':
Fritz Koenig90820e22021-03-12 11:43:00 -0800944 frames_to_encode = atoi(optarg);
Steve Chodc8f92a2021-03-03 17:43:38 -0800945 break;
946 case 'r':
947 framerate = atoi(optarg);
948 break;
949 case 'b':
Fritz Koenig526b5782021-03-11 16:13:22 -0800950 set_control_value(common_control_list,
951 V4L2_CID_MPEG_VIDEO_BITRATE,
952 atoi(optarg));
953 set_control_value(common_control_list,
954 V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
955 atoi(optarg) * 2);
Steve Chodc8f92a2021-03-03 17:43:38 -0800956 break;
957 case 'g':
Fritz Koenig526b5782021-03-11 16:13:22 -0800958 set_control_value(common_control_list,
959 V4L2_CID_MPEG_VIDEO_GOP_SIZE,
960 atoi(optarg));
Steve Chodc8f92a2021-03-03 17:43:38 -0800961 break;
962 case 'c':
963 if (strlen(optarg) == 4) {
964 CAPTURE_format = v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
965 toupper(optarg[2]), toupper(optarg[3]));
966 printf("using (%s) as the codec\n", optarg);
967 }
968 break;
969 case 'e':
970 if (strlen(optarg) == 3 && toupper(optarg[0]) == 'C' &&
971 toupper(optarg[1]) == 'B' && toupper(optarg[2]) == 'R') {
Fritz Koenig526b5782021-03-11 16:13:22 -0800972 set_control_value(common_control_list,
973 V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
974 V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
Steve Chodc8f92a2021-03-03 17:43:38 -0800975 }
976 break;
Steve Chodc8f92a2021-03-03 17:43:38 -0800977 case 'q':
978 if (strlen(optarg) == 4) {
979 OUTPUT_format = v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
980 toupper(optarg[2]), toupper(optarg[3]));
Miguel Casas4fe876d2021-03-05 22:26:31 -0500981 printf("using (%s) as the OUTPUT_queue buffer format\n", optarg);
Steve Chodc8f92a2021-03-03 17:43:38 -0800982 }
983 break;
984 default:
985 break;
986 }
987 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800988
Steve Chodc8f92a2021-03-03 17:43:38 -0800989 if (!file_name || !output_file_name || width == 0 || height == 0) {
990 fprintf(stderr, "Invalid parameters!\n");
991 print_help(argv[0]);
992 exit(1);
993 }
Fritz Koenigcdba6532021-01-22 16:11:17 -0800994
Steve Chodc8f92a2021-03-03 17:43:38 -0800995 FILE* fp = fopen(file_name, "rb");
996 if (!fp) {
997 fprintf(stderr, "%s: unable to open file.\n", file_name);
998 exit(1);
999 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001000
Fritz Koenig90820e22021-03-12 11:43:00 -08001001 if (!frames_to_encode) {
Steve Chodc8f92a2021-03-03 17:43:38 -08001002 fseek(fp, 0, SEEK_END);
1003 uint64_t length = ftell(fp);
1004 uint32_t frame_size = (3 * width * height) >> 1;
Fritz Koenig90820e22021-03-12 11:43:00 -08001005 frames_to_encode = length / frame_size;
Steve Chodc8f92a2021-03-03 17:43:38 -08001006 fseek(fp, 0, SEEK_SET);
1007 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001008
Fritz Koenig526b5782021-03-11 16:13:22 -08001009 const int bitrate_mode = get_control_value(common_control_list,
1010 V4L2_CID_MPEG_VIDEO_BITRATE_MODE);
Steve Chodc8f92a2021-03-03 17:43:38 -08001011 fprintf(
Fritz Koenig90820e22021-03-12 11:43:00 -08001012 stderr, "encoding %d frames using %s bitrate control\n", frames_to_encode,
Fritz Koenig526b5782021-03-11 16:13:22 -08001013 (bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) ? "CBR" : "VBR");
Fritz Koenigcdba6532021-01-22 16:11:17 -08001014
Steve Chodc8f92a2021-03-03 17:43:38 -08001015 int v4lfd = open(kEncodeDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC);
1016 if (v4lfd < 0) {
1017 fprintf(stderr, "Unable to open device file: %s\n", kEncodeDevice);
1018 exit(EXIT_FAILURE);
1019 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001020
Fritz Koenig723a7c42021-03-11 19:41:13 -08001021 if (capabilities(v4lfd, OUTPUT_format, CAPTURE_format) != 0) {
Steve Chodc8f92a2021-03-03 17:43:38 -08001022 fprintf(stderr, "Capabilities not present for encode.\n");
1023 exit(EXIT_FAILURE);
1024 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001025
Steve Chodc8f92a2021-03-03 17:43:38 -08001026 struct queue OUTPUT_queue = {.v4lfd = v4lfd,
1027 .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
1028 .fourcc = OUTPUT_format,
1029 .raw_width = width,
1030 .raw_height = height,
1031 .frame_cnt = 0,
1032 .num_planes = 1,
1033 .framerate = framerate};
Fritz Koenigcdba6532021-01-22 16:11:17 -08001034
Steve Chodc8f92a2021-03-03 17:43:38 -08001035 struct queue CAPTURE_queue = {.v4lfd = v4lfd,
1036 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
1037 .fourcc = CAPTURE_format,
1038 .raw_width = width,
1039 .raw_height = height,
1040 .num_planes = 1,
1041 .framerate = framerate};
Fritz Koenigcdba6532021-01-22 16:11:17 -08001042
Steve Chodc8f92a2021-03-03 17:43:38 -08001043 int ret = Initialization(&OUTPUT_queue, &CAPTURE_queue);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001044
Steve Chodc8f92a2021-03-03 17:43:38 -08001045 if (!ret) {
Fritz Koenig526b5782021-03-11 16:13:22 -08001046 // not all configurations are supported on all platforms
1047 disable_unsupported_controls(v4lfd, common_control_list);
1048 disable_unsupported_controls(v4lfd, h264_control_list);
1049 ret = set_ext_controls(v4lfd, common_control_list);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001050
Fritz Koenig526b5782021-03-11 16:13:22 -08001051 if (!ret && v4l2_fourcc('H', '2', '6', '4') == CAPTURE_format)
1052 ret = set_ext_controls(v4lfd, h264_control_list);
Steve Chodc8f92a2021-03-03 17:43:38 -08001053 }
Fritz Koenigcdba6532021-01-22 16:11:17 -08001054
Miguel Casas4fe876d2021-03-05 22:26:31 -05001055 if (!ret) {
Steve Chodc8f92a2021-03-03 17:43:38 -08001056 ret = encode(fp, file_format, output_file_name, &OUTPUT_queue,
Fritz Koenig90820e22021-03-12 11:43:00 -08001057 &CAPTURE_queue, frames_to_encode);
Miguel Casas4fe876d2021-03-05 22:26:31 -05001058 }
1059
Steve Chodc8f92a2021-03-03 17:43:38 -08001060 cleanup_queue(&OUTPUT_queue);
1061 cleanup_queue(&CAPTURE_queue);
1062 close(v4lfd);
Fritz Koenigcdba6532021-01-22 16:11:17 -08001063
Steve Chodc8f92a2021-03-03 17:43:38 -08001064 return 0;
Fritz Koenigcdba6532021-01-22 16:11:17 -08001065}