blob: 3f5388733856883704c7e254945b442f943db2fb [file] [log] [blame]
Fritz Koenig7d0e8412021-02-05 15:40:08 -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
7// As per https://www.kernel.org/doc/html/v5.4/media/uapi/v4l/dev-decoder.html
8#include <ctype.h>
9#include <errno.h>
10#include <fcntl.h>
11#include <getopt.h>
12#include <limits.h>
13#include <linux/videodev2.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <sys/ioctl.h>
19#include <sys/mman.h>
20#include <unistd.h>
21
22#include "bs_drm.h"
23
Steve Chodc8f92a2021-03-03 17:43:38 -080024static const char* kDecodeDevice = "/dev/video-dec0";
Fritz Koenig7d0e8412021-02-05 15:40:08 -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 Koenig7d0e8412021-02-05 15:40:08 -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 image_width;
41 uint32_t image_height;
42 uint32_t cnt;
43 uint32_t num_planes;
44 uint32_t memory;
Fritz Koenig7d0e8412021-02-05 15:40:08 -080045};
46
47struct ivf_file_header {
Steve Chodc8f92a2021-03-03 17:43:38 -080048 uint32_t signature;
49 uint16_t version;
50 uint16_t header_length;
51 uint32_t fourcc;
52 uint16_t width;
53 uint16_t height;
54 uint32_t denominator;
55 uint32_t numerator;
56 uint32_t frame_cnt;
57 uint32_t unused;
Fritz Koenig7d0e8412021-02-05 15:40:08 -080058} __attribute__((packed));
59
60struct ivf_frame_header {
Steve Chodc8f92a2021-03-03 17:43:38 -080061 uint32_t size;
62 uint64_t timestamp;
Fritz Koenig7d0e8412021-02-05 15:40:08 -080063} __attribute__((packed));
64
65struct compressed_file {
Steve Chodc8f92a2021-03-03 17:43:38 -080066 FILE* fp;
67 struct ivf_file_header header;
68 uint32_t submitted_frames;
Fritz Koenig7d0e8412021-02-05 15:40:08 -080069};
70
Steve Chodc8f92a2021-03-03 17:43:38 -080071void print_fourcc(uint32_t fourcc) {
72 printf("%c%c%c%c\n", fourcc & 0xff, fourcc >> 8 & 0xff, fourcc >> 16 & 0xff,
73 fourcc >> 24 & 0xff);
Fritz Koenig7d0e8412021-02-05 15:40:08 -080074}
75
Steve Chodc8f92a2021-03-03 17:43:38 -080076struct compressed_file open_file(const char* file_name) {
77 struct compressed_file file = {0};
Fritz Koenig7d0e8412021-02-05 15:40:08 -080078
Steve Chodc8f92a2021-03-03 17:43:38 -080079 FILE* fp = fopen(file_name, "rb");
80 if (fp) {
81 if (fread(&file.header, sizeof(struct ivf_file_header), 1, fp) != 1) {
82 fclose(fp);
83 fprintf(stderr, "unable to read ivf file header\n");
84 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -080085
Steve Chodc8f92a2021-03-03 17:43:38 -080086 if (file.header.signature != kIVFHeaderSignature) {
87 fclose(fp);
88 fprintf(stderr, "Incorrect header signature : 0x%0x != 0x%0x\n",
89 file.header.signature, kIVFHeaderSignature);
90 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -080091
Steve Chodc8f92a2021-03-03 17:43:38 -080092 file.fp = fp;
93 print_fourcc(file.header.fourcc);
94 printf("ivf file header: %d x %d\n", file.header.width, file.header.height);
95 } else {
96 fprintf(stderr, "unable to open file: %s\n", file_name);
97 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -080098
Steve Chodc8f92a2021-03-03 17:43:38 -080099 return file;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800100}
101
Steve Chodc8f92a2021-03-03 17:43:38 -0800102int query_format(int v4lfd, enum v4l2_buf_type type, uint32_t fourcc) {
103 struct v4l2_fmtdesc fmtdesc;
104 memset(&fmtdesc, 0, sizeof(fmtdesc));
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800105
Steve Chodc8f92a2021-03-03 17:43:38 -0800106 fmtdesc.type = type;
107 while (ioctl(v4lfd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
108 if (fourcc == 0)
109 print_fourcc(fmtdesc.pixelformat);
110 else if (fourcc == fmtdesc.pixelformat)
111 return 1;
112 fmtdesc.index++;
113 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800114
Steve Chodc8f92a2021-03-03 17:43:38 -0800115 return 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800116}
117
Steve Chodc8f92a2021-03-03 17:43:38 -0800118int capabilities(int v4lfd,
119 uint32_t compressed_format,
120 uint32_t uncompressed_format) {
121 struct v4l2_capability cap;
122 memset(&cap, 0, sizeof(cap));
123 int ret = ioctl(v4lfd, VIDIOC_QUERYCAP, &cap);
124 if (ret != 0)
125 perror("VIDIOC_QUERYCAP failed");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800126
Steve Chodc8f92a2021-03-03 17:43:38 -0800127 printf("driver=\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x\n", cap.driver,
128 cap.bus_info, cap.card, v4lfd);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800129
Steve Chodc8f92a2021-03-03 17:43:38 -0800130 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
131 compressed_format)) {
132 printf("Supported compressed formats:\n");
133 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, 0);
134 ret = 1;
135 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800136
Steve Chodc8f92a2021-03-03 17:43:38 -0800137 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
138 uncompressed_format)) {
139 printf("Supported uncompressed formats:\n");
140 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0);
141 ret = 1;
142 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800143
Steve Chodc8f92a2021-03-03 17:43:38 -0800144 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800145}
146
Steve Chodc8f92a2021-03-03 17:43:38 -0800147int request_mmap_buffers(struct queue* queue,
148 struct v4l2_requestbuffers* reqbuf) {
149 const int v4lfd = queue->v4lfd;
150 const uint32_t buffer_alloc = reqbuf->count * sizeof(struct mmap_buffers);
151 struct mmap_buffers* buffers = (struct mmap_buffers*)malloc(buffer_alloc);
152 memset(buffers, 0, buffer_alloc);
153 queue->buffers = buffers;
154 queue->cnt = reqbuf->count;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800155
Steve Chodc8f92a2021-03-03 17:43:38 -0800156 int ret;
157 for (uint32_t i = 0; i < reqbuf->count; i++) {
158 struct v4l2_buffer buffer;
159 struct v4l2_plane planes[VIDEO_MAX_PLANES];
160 memset(&buffer, 0, sizeof(buffer));
161 buffer.type = reqbuf->type;
162 buffer.memory = queue->memory;
163 buffer.index = i;
164 buffer.length = queue->num_planes;
165 buffer.m.planes = planes;
166 ret = ioctl(v4lfd, VIDIOC_QUERYBUF, &buffer);
167 if (ret != 0) {
168 printf("VIDIOC_QUERYBUF failed: %d\n", ret);
169 break;
170 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800171
Steve Chodc8f92a2021-03-03 17:43:38 -0800172 for (uint32_t j = 0; j < queue->num_planes; j++) {
173 buffers[i].length[j] = buffer.m.planes[j].length;
174 buffers[i].start[j] =
175 mmap(NULL, buffer.m.planes[j].length, PROT_READ | PROT_WRITE,
176 MAP_SHARED, v4lfd, buffer.m.planes[j].m.mem_offset);
177 if (MAP_FAILED == buffers[i].start[j]) {
178 fprintf(stderr,
179 "failed to mmap buffer of length(%d) and offset(0x%x)\n",
180 buffer.m.planes[j].length, buffer.m.planes[j].m.mem_offset);
181 }
182 }
183 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800184
Steve Chodc8f92a2021-03-03 17:43:38 -0800185 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800186}
187
188// this is the input queue that will take compressed data
189// 4.5.1.5
Steve Chodc8f92a2021-03-03 17:43:38 -0800190int setup_OUTPUT(struct queue* OUTPUT_queue) {
191 int ret = 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800192
Steve Chodc8f92a2021-03-03 17:43:38 -0800193 // 1. Set the coded format on OUTPUT via VIDIOC_S_FMT()
194 if (!ret) {
195 struct v4l2_format fmt;
196 memset(&fmt, 0, sizeof(fmt));
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800197
Steve Chodc8f92a2021-03-03 17:43:38 -0800198 fmt.type = OUTPUT_queue->type;
199 fmt.fmt.pix_mp.pixelformat = OUTPUT_queue->fourcc;
200 fmt.fmt.pix_mp.plane_fmt[0].sizeimage = kInputbufferMaxSize;
201 fmt.fmt.pix_mp.num_planes = 1;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800202
Steve Chodc8f92a2021-03-03 17:43:38 -0800203 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_FMT, &fmt);
204 if (ret != 0)
205 perror("VIDIOC_S_FMT failed");
206 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800207
Steve Chodc8f92a2021-03-03 17:43:38 -0800208 // 2. Allocate source (bytestream) buffers via VIDIOC_REQBUFS() on OUTPUT.
209 if (!ret) {
210 struct v4l2_requestbuffers reqbuf;
211 memset(&reqbuf, 0, sizeof(reqbuf));
212 reqbuf.count = kRequestBufferCount;
213 reqbuf.type = OUTPUT_queue->type;
214 reqbuf.memory = OUTPUT_queue->memory;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800215
Steve Chodc8f92a2021-03-03 17:43:38 -0800216 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
217 if (ret != 0)
218 perror("VIDIOC_REQBUFS failed");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800219
Steve Chodc8f92a2021-03-03 17:43:38 -0800220 printf("%d buffers requested, %d buffers for compressed data returned\n",
221 kRequestBufferCount, reqbuf.count);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800222
Steve Chodc8f92a2021-03-03 17:43:38 -0800223 ret = request_mmap_buffers(OUTPUT_queue, &reqbuf);
224 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800225
Steve Chodc8f92a2021-03-03 17:43:38 -0800226 // 3. Start streaming on the OUTPUT queue via VIDIOC_STREAMON().
227 if (!ret) {
228 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_STREAMON, &OUTPUT_queue->type);
229 if (ret != 0)
230 perror("VIDIOC_STREAMON failed");
231 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800232
Steve Chodc8f92a2021-03-03 17:43:38 -0800233 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800234}
235
Steve Chodc8f92a2021-03-03 17:43:38 -0800236int submit_compressed_frame(struct compressed_file* file,
237 struct queue* OUTPUT_queue,
238 uint32_t index) {
239 const uint32_t num = file->header.numerator;
240 const uint32_t den = file->header.denominator;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800241
Steve Chodc8f92a2021-03-03 17:43:38 -0800242 struct ivf_frame_header frame_header = {0};
243 if (fread(&frame_header, sizeof(struct ivf_frame_header), 1, file->fp) != 1) {
244 if (!feof(file->fp))
245 fprintf(stderr, "unable to read ivf frame header\n");
246 return -1;
247 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800248
Steve Chodc8f92a2021-03-03 17:43:38 -0800249 struct mmap_buffers* buffers = OUTPUT_queue->buffers;
250 if (fread(buffers[index].start[0], sizeof(uint8_t), frame_header.size,
251 file->fp) != frame_header.size) {
252 fprintf(stderr, "unable to read ivf frame data\n");
253 return -1;
254 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800255
Steve Chodc8f92a2021-03-03 17:43:38 -0800256 struct v4l2_buffer v4l2_buffer;
257 struct v4l2_plane planes[VIDEO_MAX_PLANES];
258 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
259 v4l2_buffer.index = index;
260 v4l2_buffer.type = OUTPUT_queue->type;
261 v4l2_buffer.memory = OUTPUT_queue->memory;
262 v4l2_buffer.length = 1;
263 v4l2_buffer.timestamp.tv_sec = 0;
264 v4l2_buffer.timestamp.tv_usec = ((frame_header.timestamp * den) / num) * 100;
265 v4l2_buffer.m.planes = planes;
266 v4l2_buffer.m.planes[0].length = buffers[index].length[0];
267 v4l2_buffer.m.planes[0].bytesused = frame_header.size;
268 v4l2_buffer.m.planes[0].data_offset = 0;
269 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
270 if (ret != 0) {
271 perror("VIDIOC_QBUF failed");
272 return -1;
273 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800274
Steve Chodc8f92a2021-03-03 17:43:38 -0800275 file->submitted_frames++;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800276
Steve Chodc8f92a2021-03-03 17:43:38 -0800277 return 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800278}
279
Steve Chodc8f92a2021-03-03 17:43:38 -0800280int prime_OUTPUT(struct compressed_file* file, struct queue* OUTPUT_queue) {
281 int ret = 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800282
Steve Chodc8f92a2021-03-03 17:43:38 -0800283 for (uint32_t i = 0; i < OUTPUT_queue->cnt; ++i) {
284 ret = submit_compressed_frame(file, OUTPUT_queue, i);
285 if (ret)
286 break;
287 }
288 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800289}
290
Steve Chodc8f92a2021-03-03 17:43:38 -0800291void cleanup_queue(struct queue* queue) {
292 if (queue->cnt) {
293 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800294
Steve Chodc8f92a2021-03-03 17:43:38 -0800295 for (uint32_t i = 0; i < queue->cnt; i++)
296 for (uint32_t j = 0; j < queue->num_planes; j++) {
297 if (buffers[i].length[j])
298 munmap(buffers[i].start[j], buffers[i].length[j]);
299 if (buffers[i].bo)
300 gbm_bo_destroy(buffers[i].bo);
301 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800302
Steve Chodc8f92a2021-03-03 17:43:38 -0800303 free(queue->buffers);
304 queue->cnt = 0;
305 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800306}
307
Steve Chodc8f92a2021-03-03 17:43:38 -0800308int queue_buffer_CAPTURE(struct queue* queue, uint32_t index) {
309 struct v4l2_buffer v4l2_buffer;
310 struct v4l2_plane planes[VIDEO_MAX_PLANES];
311 memset(&v4l2_buffer, 0, sizeof v4l2_buffer);
312 memset(&planes, 0, sizeof planes);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800313
Steve Chodc8f92a2021-03-03 17:43:38 -0800314 v4l2_buffer.type = queue->type;
315 v4l2_buffer.memory = queue->memory;
316 v4l2_buffer.index = index;
317 v4l2_buffer.m.planes = planes;
318 v4l2_buffer.length = queue->num_planes;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800319
Steve Chodc8f92a2021-03-03 17:43:38 -0800320 struct gbm_bo* bo = queue->buffers[index].bo;
321 for (uint32_t i = 0; i < queue->num_planes; ++i) {
322 if (queue->memory == V4L2_MEMORY_DMABUF) {
323 v4l2_buffer.m.planes[i].m.fd = gbm_bo_get_plane_fd(bo, i);
324 } else if (queue->memory == V4L2_MEMORY_MMAP) {
325 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800326
Steve Chodc8f92a2021-03-03 17:43:38 -0800327 v4l2_buffer.m.planes[i].length = buffers[index].length[i];
328 v4l2_buffer.m.planes[i].bytesused = buffers[index].length[i];
329 v4l2_buffer.m.planes[i].data_offset = 0;
330 }
331 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800332
Steve Chodc8f92a2021-03-03 17:43:38 -0800333 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
334 if (ret != 0) {
335 perror("VIDIOC_QBUF failed");
336 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800337
Steve Chodc8f92a2021-03-03 17:43:38 -0800338 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800339}
340
341// this is the output queue that will produce uncompressed frames
342// 4.5.1.6
Steve Chodc8f92a2021-03-03 17:43:38 -0800343int setup_CAPTURE(struct gbm_device* gbm,
344 struct queue* CAPTURE_queue,
345 uint64_t modifier) {
346 int ret = 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800347
Steve Chodc8f92a2021-03-03 17:43:38 -0800348 // 1. Call VIDIOC_G_FMT() on the CAPTURE queue to get format for the
349 // destination buffers parsed/decoded from the bytestream.
350 if (!ret) {
351 struct v4l2_format fmt;
352 memset(&fmt, 0, sizeof(fmt));
353 fmt.type = CAPTURE_queue->type;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800354
Steve Chodc8f92a2021-03-03 17:43:38 -0800355 int ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_G_FMT, &fmt);
356 if (ret != 0)
357 perror("VIDIOC_G_FMT failed");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800358
Steve Chodc8f92a2021-03-03 17:43:38 -0800359 CAPTURE_queue->image_width = fmt.fmt.pix_mp.width;
360 CAPTURE_queue->image_height = fmt.fmt.pix_mp.height;
361 CAPTURE_queue->num_planes = fmt.fmt.pix_mp.num_planes;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800362
Steve Chodc8f92a2021-03-03 17:43:38 -0800363 printf("CAPTURE: %d x %d\n", fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height);
364 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800365
Steve Chodc8f92a2021-03-03 17:43:38 -0800366 // 4. Optional. Set the CAPTURE format via VIDIOC_S_FMT() on the CAPTURE
367 // queue.
368 // The client may choose a different format than selected/suggested by the
369 // decoder in VIDIOC_G_FMT().
370 if (!ret) {
371 struct v4l2_format fmt;
372 memset(&fmt, 0, sizeof(fmt));
373 fmt.type = CAPTURE_queue->type;
374 fmt.fmt.pix_mp.pixelformat = CAPTURE_queue->fourcc;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800375
Steve Chodc8f92a2021-03-03 17:43:38 -0800376 fmt.fmt.pix_mp.width = CAPTURE_queue->image_width;
377 fmt.fmt.pix_mp.height = CAPTURE_queue->image_height;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800378
Steve Chodc8f92a2021-03-03 17:43:38 -0800379 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_S_FMT, &fmt);
380 if (ret != 0)
381 perror("VIDIOC_S_FMT failed");
382 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800383
Steve Chodc8f92a2021-03-03 17:43:38 -0800384 // 10. Allocate CAPTURE buffers via VIDIOC_REQBUFS() on the CAPTURE queue.
385 if (!ret) {
386 struct v4l2_requestbuffers reqbuf;
387 memset(&reqbuf, 0, sizeof(reqbuf));
388 reqbuf.count = kRequestBufferCount;
389 reqbuf.type = CAPTURE_queue->type;
390 reqbuf.memory = CAPTURE_queue->memory;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800391
Steve Chodc8f92a2021-03-03 17:43:38 -0800392 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
393 if (ret != 0)
394 perror("VIDIOC_REQBUFS failed");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800395
Steve Chodc8f92a2021-03-03 17:43:38 -0800396 printf("%d buffers requested, %d buffers for decoded data returned\n",
397 kRequestBufferCount, reqbuf.count);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800398
Steve Chodc8f92a2021-03-03 17:43:38 -0800399 if (CAPTURE_queue->memory == V4L2_MEMORY_DMABUF) {
400 const uint32_t buffer_alloc = reqbuf.count * sizeof(struct mmap_buffers);
401 struct mmap_buffers* buffers = (struct mmap_buffers*)malloc(buffer_alloc);
402 memset(buffers, 0, buffer_alloc);
403 CAPTURE_queue->buffers = buffers;
404 CAPTURE_queue->cnt = reqbuf.count;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800405
Steve Chodc8f92a2021-03-03 17:43:38 -0800406 for (uint32_t i = 0; i < CAPTURE_queue->cnt; ++i) {
407 const uint32_t width = CAPTURE_queue->image_width;
408 const uint32_t height = CAPTURE_queue->image_height;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800409
Steve Chodc8f92a2021-03-03 17:43:38 -0800410 struct gbm_bo* bo = gbm_bo_create_with_modifiers(
411 gbm, width, height, GBM_FORMAT_NV12, &modifier, 1);
412 CAPTURE_queue->buffers[i].bo = bo;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800413
Steve Chodc8f92a2021-03-03 17:43:38 -0800414 if (bo) {
415 ret = queue_buffer_CAPTURE(CAPTURE_queue, i);
416 if (ret != 0)
417 break;
418 } else {
419 fprintf(stderr, "could not allocate a bo %d x %d\n", width, height);
420 ret = -1;
421 break;
422 }
423 }
424 } else if (CAPTURE_queue->memory == V4L2_MEMORY_MMAP) {
425 ret = request_mmap_buffers(CAPTURE_queue, &reqbuf);
426 for (uint32_t i = 0; i < reqbuf.count; i++) {
427 queue_buffer_CAPTURE(CAPTURE_queue, i);
428 }
429 } else {
430 ret = -1;
431 }
432 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800433
Steve Chodc8f92a2021-03-03 17:43:38 -0800434 // 11. Call VIDIOC_STREAMON() on the CAPTURE queue to start decoding frames.
435 if (!ret) {
436 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_STREAMON, &CAPTURE_queue->type);
437 if (ret != 0)
438 perror("VIDIOC_STREAMON failed");
439 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800440
Steve Chodc8f92a2021-03-03 17:43:38 -0800441 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800442}
443
Steve Cho1ffec222021-03-04 10:42:45 -0800444void write_file_to_disk(FILE* fp,
445 struct queue* CAPTURE_queue,
Steve Chodc8f92a2021-03-03 17:43:38 -0800446 uint32_t index,
447 uint32_t cnt) {
Steve Cho1ffec222021-03-04 10:42:45 -0800448 if (V4L2_MEMORY_DMABUF == CAPTURE_queue->memory) {
449 struct gbm_bo* bo = CAPTURE_queue->buffers[index].bo;
450 int bo_fd = gbm_bo_get_fd(bo);
451 size_t buffer_size = lseek(bo_fd, 0, SEEK_END);
452 lseek(bo_fd, 0, SEEK_SET);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800453
Steve Cho1ffec222021-03-04 10:42:45 -0800454 uint8_t* buffer = mmap(0, buffer_size, PROT_READ, MAP_SHARED, bo_fd, 0);
455
456 fwrite(buffer, buffer_size, 1, fp);
457
458 munmap(buffer, buffer_size);
459 } else {
460 if (CAPTURE_queue->num_planes == 1) {
461 size_t buffer_size =
462 (3 * CAPTURE_queue->image_width * CAPTURE_queue->image_height) >> 1;
463 uint8_t* buffer = CAPTURE_queue->buffers[index].start[0];
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800464
Steve Chodc8f92a2021-03-03 17:43:38 -0800465 fwrite(buffer, buffer_size, 1, fp);
Steve Chodc8f92a2021-03-03 17:43:38 -0800466 } else {
Steve Cho1ffec222021-03-04 10:42:45 -0800467 for (uint32_t i = 0; i < CAPTURE_queue->num_planes; ++i) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800468 size_t buffer_size =
Steve Cho1ffec222021-03-04 10:42:45 -0800469 (CAPTURE_queue->image_width * CAPTURE_queue->image_height) >> i;
470 uint8_t* buffer = CAPTURE_queue->buffers[index].start[i];
471
Steve Chodc8f92a2021-03-03 17:43:38 -0800472 fwrite(buffer, buffer_size, 1, fp);
Steve Chodc8f92a2021-03-03 17:43:38 -0800473 }
474 }
Steve Chodc8f92a2021-03-03 17:43:38 -0800475 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800476}
477
Steve Chodc8f92a2021-03-03 17:43:38 -0800478int dequeue_buffer(struct queue* queue, uint32_t* index) {
479 struct v4l2_buffer v4l2_buffer;
480 struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
481 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
482 v4l2_buffer.type = queue->type;
483 v4l2_buffer.length = queue->num_planes;
484 v4l2_buffer.m.planes = planes;
485 v4l2_buffer.m.planes[0].bytesused = 0;
486 int ret = ioctl(queue->v4lfd, VIDIOC_DQBUF, &v4l2_buffer);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800487
Steve Chodc8f92a2021-03-03 17:43:38 -0800488 *index = v4l2_buffer.index;
489 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800490}
491
Steve Chodc8f92a2021-03-03 17:43:38 -0800492int decode(struct compressed_file* file,
493 struct queue* CAPTURE_queue,
494 struct queue* OUTPUT_queue,
495 uint64_t modifier,
Steve Cho1ffec222021-03-04 10:42:45 -0800496 bool write_out_individual_frames,
497 bool write_out_file,
498 char* file_name,
Steve Chodc8f92a2021-03-03 17:43:38 -0800499 uint32_t frames_to_decode) {
500 int ret = 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800501
Steve Chodc8f92a2021-03-03 17:43:38 -0800502 if (!ret) {
503 uint32_t cnt = 0;
Steve Cho1ffec222021-03-04 10:42:45 -0800504
505 FILE* fp_frame;
506 FILE* fp_file;
507 char filename_frame[256];
508
509 if (write_out_file) {
510 // replace .ivf with .yuv
511 strcpy(strrchr(file_name, '.'), ".yuv");
512
513 fp_file = fopen(file_name, "wb");
514
515 if (!fp_file) {
516 fprintf(stderr, "Unable to open output yuv file: %s\n", file_name);
517 return 1;
518 }
519 }
520
Steve Chodc8f92a2021-03-03 17:43:38 -0800521 while (cnt < frames_to_decode) {
522 {
523 uint32_t index = 0;
524 ret = dequeue_buffer(CAPTURE_queue, &index);
525 if (ret != 0) {
526 if (errno != EAGAIN)
527 perror("VIDIOC_DQBUF failed");
528 continue;
529 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800530
Steve Cho1ffec222021-03-04 10:42:45 -0800531 if (write_out_individual_frames) {
532 sprintf(filename_frame, "image_%dx%d_%d.yuv",
533 CAPTURE_queue->image_width, CAPTURE_queue->image_height, cnt);
534 fp_frame = fopen(filename_frame, "wb");
535
536 if (!fp_frame) {
537 fprintf(stderr, "Unable to open frame yuv file: %s\n",
538 filename_frame);
539 ret = 1;
540 break;
541 } else {
542 write_file_to_disk(fp_frame, CAPTURE_queue, index, cnt);
543 }
544 }
545
546 if (write_out_file) {
547 write_file_to_disk(fp_file, CAPTURE_queue, index, cnt);
548 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800549
Steve Chodc8f92a2021-03-03 17:43:38 -0800550 // Done with buffer, queue it back up.
551 ret = queue_buffer_CAPTURE(CAPTURE_queue, index);
552 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800553
Steve Chodc8f92a2021-03-03 17:43:38 -0800554 // A frame was recieved on the CAPTURE queue, that means there should
555 // now be a free OUTPUT buffer.
556 {
557 uint32_t index = 0;
558 ret = dequeue_buffer(OUTPUT_queue, &index);
559 if (ret != 0) {
560 if (errno != EAGAIN)
561 perror("VIDIOC_DQBUF failed");
562 continue;
563 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800564
Steve Chodc8f92a2021-03-03 17:43:38 -0800565 if (submit_compressed_frame(file, OUTPUT_queue, index))
566 break;
567 }
568 cnt++;
Steve Cho1ffec222021-03-04 10:42:45 -0800569
570 if (write_out_individual_frames)
571 fclose(fp_frame);
Steve Chodc8f92a2021-03-03 17:43:38 -0800572 }
Steve Cho1ffec222021-03-04 10:42:45 -0800573
574 if (write_out_file)
575 fclose(fp_file);
576
Steve Chodc8f92a2021-03-03 17:43:38 -0800577 printf("%d frames decoded.\n", cnt);
578 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800579
Steve Chodc8f92a2021-03-03 17:43:38 -0800580 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800581}
582
Steve Chodc8f92a2021-03-03 17:43:38 -0800583static void print_help(const char* argv0) {
584 printf("usage: %s [OPTIONS]\n", argv0);
585 printf(" -f, --file ivf file to decode\n");
Steve Cho1ffec222021-03-04 10:42:45 -0800586 printf(
587 " -w, --write write out decompressed frames to individual "
588 "files\n");
589 printf(
590 " -y, --output_yuv write out decompressed frames to a single file\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800591 printf(" -m, --max max number of frames to decode\n");
592 printf(" -b, --buffer use mmap instead of dmabuf\n");
593 printf(" -o, --output_fmt fourcc of output format\n");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800594}
595
596static const struct option longopts[] = {
Steve Cho1ffec222021-03-04 10:42:45 -0800597 {"file", required_argument, NULL, 'f'},
598 {"write", no_argument, NULL, 'w'},
599 {"output_yuv", no_argument, NULL, 'y'},
600 {"max", required_argument, NULL, 'm'},
601 {"buffer", no_argument, NULL, 'b'},
602 {"output_fmt", no_argument, NULL, 'o'},
603 {0, 0, 0, 0},
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800604};
605
Steve Chodc8f92a2021-03-03 17:43:38 -0800606int main(int argc, char* argv[]) {
607 printf("simple v4l2 decode\n");
608 int c;
609 char* file_name = NULL;
Steve Cho1ffec222021-03-04 10:42:45 -0800610 bool write_out_individual_frames = false;
611 bool write_out_file = false;
Steve Chodc8f92a2021-03-03 17:43:38 -0800612 uint32_t frames_to_decode = UINT_MAX;
613 uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
614 uint32_t uncompressed_fourcc = v4l2_fourcc('N', 'V', '1', '2');
615 uint32_t CAPTURE_memory = V4L2_MEMORY_DMABUF;
Steve Cho1ffec222021-03-04 10:42:45 -0800616 while ((c = getopt_long(argc, argv, "wybm:f:o:", longopts, NULL)) != -1) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800617 switch (c) {
618 case 'f':
619 file_name = strdup(optarg);
620 break;
621 case 'm':
622 frames_to_decode = atoi(optarg);
623 printf("only decoding a max of %d frames.\n", frames_to_decode);
624 break;
625 case 'w':
Steve Cho1ffec222021-03-04 10:42:45 -0800626 write_out_individual_frames = true;
627 break;
628 case 'y':
629 write_out_file = true;
Steve Chodc8f92a2021-03-03 17:43:38 -0800630 break;
631 case 'b':
632 CAPTURE_memory = V4L2_MEMORY_MMAP;
633 break;
634 case 'o':
635 if (strlen(optarg) == 4) {
636 uncompressed_fourcc =
637 v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
638 toupper(optarg[2]), toupper(optarg[3]));
639 printf("using (%s) as the CAPTURE format\n", optarg);
640 if (uncompressed_fourcc == v4l2_fourcc('Q', '1', '2', '8')) {
641 printf("compressed format, setting modifier\n");
642 modifier = DRM_FORMAT_MOD_QCOM_COMPRESSED;
643 }
644 }
645 break;
646 default:
647 break;
648 }
649 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800650
Steve Chodc8f92a2021-03-03 17:43:38 -0800651 if (!file_name) {
652 print_help(argv[0]);
653 exit(1);
654 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800655
Steve Chodc8f92a2021-03-03 17:43:38 -0800656 int drm_device_fd = bs_drm_open_main_display();
657 if (drm_device_fd < 0) {
658 fprintf(stderr, "failed to open card for display\n");
659 return 1;
660 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800661
Steve Chodc8f92a2021-03-03 17:43:38 -0800662 struct gbm_device* gbm = gbm_create_device(drm_device_fd);
663 if (!gbm) {
664 fprintf(stderr, "failed to create gbm device\n");
665 close(drm_device_fd);
666 exit(EXIT_FAILURE);
667 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800668
Steve Chodc8f92a2021-03-03 17:43:38 -0800669 struct compressed_file compressed_file = open_file(file_name);
670 if (!compressed_file.fp) {
671 fprintf(stderr, "Unable to open ivf file: %s\n", file_name);
672 exit(EXIT_FAILURE);
673 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800674
Steve Chodc8f92a2021-03-03 17:43:38 -0800675 int v4lfd = open(kDecodeDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC);
676 if (v4lfd < 0) {
677 fprintf(stderr, "Unable to open device file: %s\n", kDecodeDevice);
678 exit(EXIT_FAILURE);
679 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800680
Steve Chodc8f92a2021-03-03 17:43:38 -0800681 if (capabilities(v4lfd, compressed_file.header.fourcc, uncompressed_fourcc) !=
682 0) {
683 fprintf(stderr, "Capabilities not present for decode.\n");
684 exit(EXIT_FAILURE);
685 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800686
Steve Chodc8f92a2021-03-03 17:43:38 -0800687 struct queue OUTPUT_queue = {.v4lfd = v4lfd,
688 .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
689 .fourcc = compressed_file.header.fourcc,
690 .num_planes = 1,
691 .memory = V4L2_MEMORY_MMAP};
692 int ret = setup_OUTPUT(&OUTPUT_queue);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800693
Steve Chodc8f92a2021-03-03 17:43:38 -0800694 if (!ret)
695 ret = prime_OUTPUT(&compressed_file, &OUTPUT_queue);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800696
Steve Chodc8f92a2021-03-03 17:43:38 -0800697 struct queue CAPTURE_queue = {.v4lfd = v4lfd,
698 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
699 .fourcc = uncompressed_fourcc,
700 .num_planes = 1,
701 .memory = CAPTURE_memory};
702 if (!ret)
703 ret = setup_CAPTURE(gbm, &CAPTURE_queue, modifier);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800704
Steve Chodc8f92a2021-03-03 17:43:38 -0800705 if (!ret)
706 ret = decode(&compressed_file, &CAPTURE_queue, &OUTPUT_queue, modifier,
Steve Cho1ffec222021-03-04 10:42:45 -0800707 write_out_individual_frames, write_out_file, file_name,
708 frames_to_decode);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800709
Steve Chodc8f92a2021-03-03 17:43:38 -0800710 cleanup_queue(&OUTPUT_queue);
711 cleanup_queue(&CAPTURE_queue);
712 close(v4lfd);
713 fclose(compressed_file.fp);
714 close(drm_device_fd);
715 free(file_name);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800716
Steve Chodc8f92a2021-03-03 17:43:38 -0800717 return 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800718}