blob: 83f185e53888bc2ff14a2aace83a55ccb2376524 [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>
Steve Chofa7be4c2021-04-27 10:50:48 -070014#include <openssl/md5.h>
Fritz Koenig7d0e8412021-02-05 15:40:08 -080015#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <sys/ioctl.h>
20#include <sys/mman.h>
21#include <unistd.h>
22
23#include "bs_drm.h"
24
Steve Chodc8f92a2021-03-03 17:43:38 -080025static const char* kDecodeDevice = "/dev/video-dec0";
Fritz Koenig7d0e8412021-02-05 15:40:08 -080026static const int kInputbufferMaxSize = 4 * 1024 * 1024;
27static const int kRequestBufferCount = 8;
28static const uint32_t kIVFHeaderSignature = v4l2_fourcc('D', 'K', 'I', 'F');
29
Steve Chofa7be4c2021-04-27 10:50:48 -070030static bool verbose_enabled = false;
31
Fritz Koenig7d0e8412021-02-05 15:40:08 -080032struct mmap_buffers {
Steve Chodc8f92a2021-03-03 17:43:38 -080033 void* start[VIDEO_MAX_PLANES];
34 size_t length[VIDEO_MAX_PLANES];
35 struct gbm_bo* bo;
Fritz Koenig7d0e8412021-02-05 15:40:08 -080036};
37
38struct queue {
Steve Chodc8f92a2021-03-03 17:43:38 -080039 int v4lfd;
40 enum v4l2_buf_type type;
41 uint32_t fourcc;
42 struct mmap_buffers* buffers;
43 uint32_t image_width;
44 uint32_t image_height;
45 uint32_t cnt;
46 uint32_t num_planes;
47 uint32_t memory;
Steve Chofa7be4c2021-04-27 10:50:48 -070048 uint32_t stride;
Fritz Koenig7d0e8412021-02-05 15:40:08 -080049};
50
51struct ivf_file_header {
Steve Chodc8f92a2021-03-03 17:43:38 -080052 uint32_t signature;
53 uint16_t version;
54 uint16_t header_length;
55 uint32_t fourcc;
56 uint16_t width;
57 uint16_t height;
58 uint32_t denominator;
59 uint32_t numerator;
60 uint32_t frame_cnt;
61 uint32_t unused;
Fritz Koenig7d0e8412021-02-05 15:40:08 -080062} __attribute__((packed));
63
64struct ivf_frame_header {
Steve Chodc8f92a2021-03-03 17:43:38 -080065 uint32_t size;
66 uint64_t timestamp;
Fritz Koenig7d0e8412021-02-05 15:40:08 -080067} __attribute__((packed));
68
69struct compressed_file {
Steve Chodc8f92a2021-03-03 17:43:38 -080070 FILE* fp;
71 struct ivf_file_header header;
72 uint32_t submitted_frames;
Fritz Koenig7d0e8412021-02-05 15:40:08 -080073};
74
Steve Chodc8f92a2021-03-03 17:43:38 -080075void print_fourcc(uint32_t fourcc) {
76 printf("%c%c%c%c\n", fourcc & 0xff, fourcc >> 8 & 0xff, fourcc >> 16 & 0xff,
77 fourcc >> 24 & 0xff);
Fritz Koenig7d0e8412021-02-05 15:40:08 -080078}
79
Steve Chodc8f92a2021-03-03 17:43:38 -080080struct compressed_file open_file(const char* file_name) {
81 struct compressed_file file = {0};
Fritz Koenig7d0e8412021-02-05 15:40:08 -080082
Steve Chodc8f92a2021-03-03 17:43:38 -080083 FILE* fp = fopen(file_name, "rb");
84 if (fp) {
85 if (fread(&file.header, sizeof(struct ivf_file_header), 1, fp) != 1) {
86 fclose(fp);
87 fprintf(stderr, "unable to read ivf file header\n");
88 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -080089
Steve Chodc8f92a2021-03-03 17:43:38 -080090 if (file.header.signature != kIVFHeaderSignature) {
91 fclose(fp);
92 fprintf(stderr, "Incorrect header signature : 0x%0x != 0x%0x\n",
93 file.header.signature, kIVFHeaderSignature);
94 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -080095
Steve Chodc8f92a2021-03-03 17:43:38 -080096 file.fp = fp;
97 print_fourcc(file.header.fourcc);
98 printf("ivf file header: %d x %d\n", file.header.width, file.header.height);
Steve Chofa7be4c2021-04-27 10:50:48 -070099 printf("ivf file header: frame_cnt = %d\n", file.header.frame_cnt);
Steve Chodc8f92a2021-03-03 17:43:38 -0800100 } else {
101 fprintf(stderr, "unable to open file: %s\n", file_name);
102 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800103
Steve Chodc8f92a2021-03-03 17:43:38 -0800104 return file;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800105}
106
Steve Chodc8f92a2021-03-03 17:43:38 -0800107int query_format(int v4lfd, enum v4l2_buf_type type, uint32_t fourcc) {
108 struct v4l2_fmtdesc fmtdesc;
109 memset(&fmtdesc, 0, sizeof(fmtdesc));
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800110
Steve Chodc8f92a2021-03-03 17:43:38 -0800111 fmtdesc.type = type;
112 while (ioctl(v4lfd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
113 if (fourcc == 0)
114 print_fourcc(fmtdesc.pixelformat);
115 else if (fourcc == fmtdesc.pixelformat)
116 return 1;
117 fmtdesc.index++;
118 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800119
Steve Chodc8f92a2021-03-03 17:43:38 -0800120 return 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800121}
122
Steve Chodc8f92a2021-03-03 17:43:38 -0800123int capabilities(int v4lfd,
124 uint32_t compressed_format,
125 uint32_t uncompressed_format) {
126 struct v4l2_capability cap;
127 memset(&cap, 0, sizeof(cap));
128 int ret = ioctl(v4lfd, VIDIOC_QUERYCAP, &cap);
129 if (ret != 0)
130 perror("VIDIOC_QUERYCAP failed");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800131
Steve Chodc8f92a2021-03-03 17:43:38 -0800132 printf("driver=\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x\n", cap.driver,
133 cap.bus_info, cap.card, v4lfd);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800134
Steve Chodc8f92a2021-03-03 17:43:38 -0800135 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
136 compressed_format)) {
137 printf("Supported compressed formats:\n");
138 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, 0);
139 ret = 1;
140 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800141
Steve Chodc8f92a2021-03-03 17:43:38 -0800142 if (!query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
143 uncompressed_format)) {
144 printf("Supported uncompressed formats:\n");
145 query_format(v4lfd, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, 0);
146 ret = 1;
147 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800148
Steve Chodc8f92a2021-03-03 17:43:38 -0800149 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800150}
151
Steve Chodc8f92a2021-03-03 17:43:38 -0800152int request_mmap_buffers(struct queue* queue,
153 struct v4l2_requestbuffers* reqbuf) {
154 const int v4lfd = queue->v4lfd;
155 const uint32_t buffer_alloc = reqbuf->count * sizeof(struct mmap_buffers);
156 struct mmap_buffers* buffers = (struct mmap_buffers*)malloc(buffer_alloc);
Steve Chofa7be4c2021-04-27 10:50:48 -0700157 assert(buffers);
Steve Chodc8f92a2021-03-03 17:43:38 -0800158 memset(buffers, 0, buffer_alloc);
159 queue->buffers = buffers;
160 queue->cnt = reqbuf->count;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800161
Steve Chodc8f92a2021-03-03 17:43:38 -0800162 int ret;
Steve Chofa7be4c2021-04-27 10:50:48 -0700163 for (uint32_t i = 0; i < reqbuf->count; ++i) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800164 struct v4l2_buffer buffer;
165 struct v4l2_plane planes[VIDEO_MAX_PLANES];
166 memset(&buffer, 0, sizeof(buffer));
167 buffer.type = reqbuf->type;
168 buffer.memory = queue->memory;
169 buffer.index = i;
170 buffer.length = queue->num_planes;
171 buffer.m.planes = planes;
172 ret = ioctl(v4lfd, VIDIOC_QUERYBUF, &buffer);
173 if (ret != 0) {
174 printf("VIDIOC_QUERYBUF failed: %d\n", ret);
175 break;
176 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800177
Steve Chofa7be4c2021-04-27 10:50:48 -0700178 for (uint32_t j = 0; j < queue->num_planes; ++j) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800179 buffers[i].length[j] = buffer.m.planes[j].length;
180 buffers[i].start[j] =
181 mmap(NULL, buffer.m.planes[j].length, PROT_READ | PROT_WRITE,
182 MAP_SHARED, v4lfd, buffer.m.planes[j].m.mem_offset);
183 if (MAP_FAILED == buffers[i].start[j]) {
184 fprintf(stderr,
185 "failed to mmap buffer of length(%d) and offset(0x%x)\n",
186 buffer.m.planes[j].length, buffer.m.planes[j].m.mem_offset);
187 }
188 }
189 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800190
Steve Chodc8f92a2021-03-03 17:43:38 -0800191 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800192}
193
Steve Chofa7be4c2021-04-27 10:50:48 -0700194// NV12 to I420 conversion.
195// This function converts the NV12 |buffer_in| into an I420 |buffer_out|.
196// |buffer_in| is padded, whereas |buffer_out| is tightly packed.
197// Example with width = 8, height = 2, stride = 10.
198//
199// NV12 I420
200// YYYYYYYY00 YYYYYYYY
201// YYYYYYYY00 YYYYYYYY
202// UVUVUVUV00 UUUUVVVV
203//
204// HW pads 0s for |stride - width| bytes after each row on Trogdor.
205// But other platforms might leave the padding uninitialized,
206// and in yet others accessing it might causes a crash of some sort (access
207// violation).
208void nv12_to_i420(uint32_t width,
209 uint32_t height,
210 uint32_t stride,
211 uint8_t* buffer_in,
212 uint8_t* buffer_out) {
213 // Copy luma data from |buffer_in| one row at a time
214 // to avoid touching the padding.
215 for (int row = 0; row < height; ++row)
216 memcpy(buffer_out + row * width, buffer_in + row * stride, width);
217
218 const size_t y_plane_size = width * height;
219 const size_t u_plane_size = y_plane_size / 4;
220 uint8_t* u_plane_out = &buffer_out[y_plane_size];
221 uint8_t* v_plane_out = u_plane_out + u_plane_size;
222 const size_t uv_plane_offset = stride * height;
223
224 for (int row = 0; row < height / 2; ++row) {
225 for (int column = 0; column < width / 2; ++column) {
226 *(u_plane_out + row * width / 2 + column) =
227 buffer_in[uv_plane_offset + row * stride + 2 * column];
228
229 *(v_plane_out + row * width / 2 + column) =
230 buffer_in[uv_plane_offset + row * stride + 2 * column + 1];
231 }
232 }
233}
234
235// Compute md5sum for each frame.
236void compute_md5sum(uint8_t* buffer_i420, uint32_t output_size) {
237 int n;
238 uint8_t out[16];
239
240 MD5_CTX ctx;
241 MD5_Init(&ctx);
242 MD5_Update(&ctx, buffer_i420, output_size);
243 MD5_Final(out, &ctx);
244
245 for (n = 0; n < 16; ++n)
246 printf("%02x", out[n]); // md5sum value for each frame
247 printf("\n");
248}
249
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800250// this is the input queue that will take compressed data
251// 4.5.1.5
Steve Chodc8f92a2021-03-03 17:43:38 -0800252int setup_OUTPUT(struct queue* OUTPUT_queue) {
253 int ret = 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800254
Steve Chodc8f92a2021-03-03 17:43:38 -0800255 // 1. Set the coded format on OUTPUT via VIDIOC_S_FMT()
256 if (!ret) {
257 struct v4l2_format fmt;
258 memset(&fmt, 0, sizeof(fmt));
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800259
Steve Chodc8f92a2021-03-03 17:43:38 -0800260 fmt.type = OUTPUT_queue->type;
261 fmt.fmt.pix_mp.pixelformat = OUTPUT_queue->fourcc;
262 fmt.fmt.pix_mp.plane_fmt[0].sizeimage = kInputbufferMaxSize;
263 fmt.fmt.pix_mp.num_planes = 1;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800264
Steve Chodc8f92a2021-03-03 17:43:38 -0800265 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_S_FMT, &fmt);
266 if (ret != 0)
267 perror("VIDIOC_S_FMT failed");
268 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800269
Steve Chodc8f92a2021-03-03 17:43:38 -0800270 // 2. Allocate source (bytestream) buffers via VIDIOC_REQBUFS() on OUTPUT.
271 if (!ret) {
272 struct v4l2_requestbuffers reqbuf;
273 memset(&reqbuf, 0, sizeof(reqbuf));
274 reqbuf.count = kRequestBufferCount;
275 reqbuf.type = OUTPUT_queue->type;
276 reqbuf.memory = OUTPUT_queue->memory;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800277
Steve Chodc8f92a2021-03-03 17:43:38 -0800278 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
279 if (ret != 0)
280 perror("VIDIOC_REQBUFS failed");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800281
Steve Chodc8f92a2021-03-03 17:43:38 -0800282 printf("%d buffers requested, %d buffers for compressed data returned\n",
283 kRequestBufferCount, reqbuf.count);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800284
Steve Chodc8f92a2021-03-03 17:43:38 -0800285 ret = request_mmap_buffers(OUTPUT_queue, &reqbuf);
286 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800287
Steve Chodc8f92a2021-03-03 17:43:38 -0800288 // 3. Start streaming on the OUTPUT queue via VIDIOC_STREAMON().
289 if (!ret) {
290 ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_STREAMON, &OUTPUT_queue->type);
291 if (ret != 0)
292 perror("VIDIOC_STREAMON failed");
293 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800294
Steve Chodc8f92a2021-03-03 17:43:38 -0800295 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800296}
297
Steve Chodc8f92a2021-03-03 17:43:38 -0800298int submit_compressed_frame(struct compressed_file* file,
299 struct queue* OUTPUT_queue,
300 uint32_t index) {
301 const uint32_t num = file->header.numerator;
302 const uint32_t den = file->header.denominator;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800303
Steve Chodc8f92a2021-03-03 17:43:38 -0800304 struct ivf_frame_header frame_header = {0};
Steve Chofa7be4c2021-04-27 10:50:48 -0700305
Steve Chodc8f92a2021-03-03 17:43:38 -0800306 if (fread(&frame_header, sizeof(struct ivf_frame_header), 1, file->fp) != 1) {
307 if (!feof(file->fp))
308 fprintf(stderr, "unable to read ivf frame header\n");
Steve Chofa7be4c2021-04-27 10:50:48 -0700309 else
310 printf("unable to read ivf frame header, reached the end of ivf file\n");
311
Steve Chodc8f92a2021-03-03 17:43:38 -0800312 return -1;
313 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800314
Steve Chodc8f92a2021-03-03 17:43:38 -0800315 struct mmap_buffers* buffers = OUTPUT_queue->buffers;
316 if (fread(buffers[index].start[0], sizeof(uint8_t), frame_header.size,
317 file->fp) != frame_header.size) {
318 fprintf(stderr, "unable to read ivf frame data\n");
319 return -1;
320 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800321
Steve Chodc8f92a2021-03-03 17:43:38 -0800322 struct v4l2_buffer v4l2_buffer;
323 struct v4l2_plane planes[VIDEO_MAX_PLANES];
324 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
325 v4l2_buffer.index = index;
326 v4l2_buffer.type = OUTPUT_queue->type;
327 v4l2_buffer.memory = OUTPUT_queue->memory;
328 v4l2_buffer.length = 1;
329 v4l2_buffer.timestamp.tv_sec = 0;
330 v4l2_buffer.timestamp.tv_usec = ((frame_header.timestamp * den) / num) * 100;
331 v4l2_buffer.m.planes = planes;
332 v4l2_buffer.m.planes[0].length = buffers[index].length[0];
333 v4l2_buffer.m.planes[0].bytesused = frame_header.size;
334 v4l2_buffer.m.planes[0].data_offset = 0;
335 int ret = ioctl(OUTPUT_queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
336 if (ret != 0) {
337 perror("VIDIOC_QBUF failed");
338 return -1;
339 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800340
Steve Chodc8f92a2021-03-03 17:43:38 -0800341 file->submitted_frames++;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800342
Steve Chodc8f92a2021-03-03 17:43:38 -0800343 return 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800344}
345
Steve Chodc8f92a2021-03-03 17:43:38 -0800346int prime_OUTPUT(struct compressed_file* file, struct queue* OUTPUT_queue) {
347 int ret = 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800348
Steve Chodc8f92a2021-03-03 17:43:38 -0800349 for (uint32_t i = 0; i < OUTPUT_queue->cnt; ++i) {
350 ret = submit_compressed_frame(file, OUTPUT_queue, i);
351 if (ret)
352 break;
353 }
354 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800355}
356
Steve Chodc8f92a2021-03-03 17:43:38 -0800357void cleanup_queue(struct queue* queue) {
358 if (queue->cnt) {
359 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800360
Steve Chofa7be4c2021-04-27 10:50:48 -0700361 for (uint32_t i = 0; i < queue->cnt; ++i)
362 for (uint32_t j = 0; j < queue->num_planes; ++j) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800363 if (buffers[i].length[j])
364 munmap(buffers[i].start[j], buffers[i].length[j]);
365 if (buffers[i].bo)
366 gbm_bo_destroy(buffers[i].bo);
367 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800368
Steve Chodc8f92a2021-03-03 17:43:38 -0800369 free(queue->buffers);
370 queue->cnt = 0;
371 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800372}
373
Steve Chodc8f92a2021-03-03 17:43:38 -0800374int queue_buffer_CAPTURE(struct queue* queue, uint32_t index) {
375 struct v4l2_buffer v4l2_buffer;
376 struct v4l2_plane planes[VIDEO_MAX_PLANES];
377 memset(&v4l2_buffer, 0, sizeof v4l2_buffer);
378 memset(&planes, 0, sizeof planes);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800379
Steve Chodc8f92a2021-03-03 17:43:38 -0800380 v4l2_buffer.type = queue->type;
381 v4l2_buffer.memory = queue->memory;
382 v4l2_buffer.index = index;
383 v4l2_buffer.m.planes = planes;
384 v4l2_buffer.length = queue->num_planes;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800385
Steve Chodc8f92a2021-03-03 17:43:38 -0800386 struct gbm_bo* bo = queue->buffers[index].bo;
387 for (uint32_t i = 0; i < queue->num_planes; ++i) {
388 if (queue->memory == V4L2_MEMORY_DMABUF) {
Miguel Casas6d4098e2021-04-08 12:39:51 -0400389 v4l2_buffer.m.planes[i].m.fd = gbm_bo_get_fd_for_plane(bo, i);
Steve Chodc8f92a2021-03-03 17:43:38 -0800390 } else if (queue->memory == V4L2_MEMORY_MMAP) {
391 struct mmap_buffers* buffers = queue->buffers;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800392
Steve Chodc8f92a2021-03-03 17:43:38 -0800393 v4l2_buffer.m.planes[i].length = buffers[index].length[i];
394 v4l2_buffer.m.planes[i].bytesused = buffers[index].length[i];
395 v4l2_buffer.m.planes[i].data_offset = 0;
396 }
397 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800398
Steve Chodc8f92a2021-03-03 17:43:38 -0800399 int ret = ioctl(queue->v4lfd, VIDIOC_QBUF, &v4l2_buffer);
400 if (ret != 0) {
401 perror("VIDIOC_QBUF failed");
402 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800403
Steve Chodc8f92a2021-03-03 17:43:38 -0800404 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800405}
406
407// this is the output queue that will produce uncompressed frames
408// 4.5.1.6
Steve Chodc8f92a2021-03-03 17:43:38 -0800409int setup_CAPTURE(struct gbm_device* gbm,
410 struct queue* CAPTURE_queue,
411 uint64_t modifier) {
412 int ret = 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800413
Steve Chodc8f92a2021-03-03 17:43:38 -0800414 // 1. Call VIDIOC_G_FMT() on the CAPTURE queue to get format for the
415 // destination buffers parsed/decoded from the bytestream.
416 if (!ret) {
417 struct v4l2_format fmt;
418 memset(&fmt, 0, sizeof(fmt));
419 fmt.type = CAPTURE_queue->type;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800420
Steve Chodc8f92a2021-03-03 17:43:38 -0800421 int ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_G_FMT, &fmt);
422 if (ret != 0)
423 perror("VIDIOC_G_FMT failed");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800424
Steve Chodc8f92a2021-03-03 17:43:38 -0800425 CAPTURE_queue->image_width = fmt.fmt.pix_mp.width;
426 CAPTURE_queue->image_height = fmt.fmt.pix_mp.height;
427 CAPTURE_queue->num_planes = fmt.fmt.pix_mp.num_planes;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800428
Steve Chodc8f92a2021-03-03 17:43:38 -0800429 printf("CAPTURE: %d x %d\n", fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height);
Steve Chofa7be4c2021-04-27 10:50:48 -0700430 printf("num_planes = %d\n", fmt.fmt.pix_mp.num_planes);
431
432 // Only Y stride is needed on Trogdor.
433 CAPTURE_queue->stride = fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
434 if (verbose_enabled)
435 printf("plane 0, stride = %d\n", CAPTURE_queue->stride);
Steve Chodc8f92a2021-03-03 17:43:38 -0800436 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800437
Steve Chodc8f92a2021-03-03 17:43:38 -0800438 // 4. Optional. Set the CAPTURE format via VIDIOC_S_FMT() on the CAPTURE
439 // queue.
440 // The client may choose a different format than selected/suggested by the
441 // decoder in VIDIOC_G_FMT().
442 if (!ret) {
443 struct v4l2_format fmt;
444 memset(&fmt, 0, sizeof(fmt));
445 fmt.type = CAPTURE_queue->type;
446 fmt.fmt.pix_mp.pixelformat = CAPTURE_queue->fourcc;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800447
Steve Chodc8f92a2021-03-03 17:43:38 -0800448 fmt.fmt.pix_mp.width = CAPTURE_queue->image_width;
449 fmt.fmt.pix_mp.height = CAPTURE_queue->image_height;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800450
Steve Chodc8f92a2021-03-03 17:43:38 -0800451 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_S_FMT, &fmt);
452 if (ret != 0)
453 perror("VIDIOC_S_FMT failed");
454 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800455
Steve Chodc8f92a2021-03-03 17:43:38 -0800456 // 10. Allocate CAPTURE buffers via VIDIOC_REQBUFS() on the CAPTURE queue.
457 if (!ret) {
458 struct v4l2_requestbuffers reqbuf;
459 memset(&reqbuf, 0, sizeof(reqbuf));
460 reqbuf.count = kRequestBufferCount;
461 reqbuf.type = CAPTURE_queue->type;
462 reqbuf.memory = CAPTURE_queue->memory;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800463
Steve Chodc8f92a2021-03-03 17:43:38 -0800464 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_REQBUFS, &reqbuf);
465 if (ret != 0)
466 perror("VIDIOC_REQBUFS failed");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800467
Steve Chodc8f92a2021-03-03 17:43:38 -0800468 printf("%d buffers requested, %d buffers for decoded data returned\n",
469 kRequestBufferCount, reqbuf.count);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800470
Steve Chodc8f92a2021-03-03 17:43:38 -0800471 if (CAPTURE_queue->memory == V4L2_MEMORY_DMABUF) {
472 const uint32_t buffer_alloc = reqbuf.count * sizeof(struct mmap_buffers);
473 struct mmap_buffers* buffers = (struct mmap_buffers*)malloc(buffer_alloc);
Steve Chofa7be4c2021-04-27 10:50:48 -0700474 assert(buffers);
Steve Chodc8f92a2021-03-03 17:43:38 -0800475 memset(buffers, 0, buffer_alloc);
476 CAPTURE_queue->buffers = buffers;
477 CAPTURE_queue->cnt = reqbuf.count;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800478
Steve Chodc8f92a2021-03-03 17:43:38 -0800479 for (uint32_t i = 0; i < CAPTURE_queue->cnt; ++i) {
480 const uint32_t width = CAPTURE_queue->image_width;
481 const uint32_t height = CAPTURE_queue->image_height;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800482
Steve Chodc8f92a2021-03-03 17:43:38 -0800483 struct gbm_bo* bo = gbm_bo_create_with_modifiers(
484 gbm, width, height, GBM_FORMAT_NV12, &modifier, 1);
485 CAPTURE_queue->buffers[i].bo = bo;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800486
Steve Chodc8f92a2021-03-03 17:43:38 -0800487 if (bo) {
488 ret = queue_buffer_CAPTURE(CAPTURE_queue, i);
489 if (ret != 0)
490 break;
491 } else {
492 fprintf(stderr, "could not allocate a bo %d x %d\n", width, height);
493 ret = -1;
494 break;
495 }
496 }
497 } else if (CAPTURE_queue->memory == V4L2_MEMORY_MMAP) {
498 ret = request_mmap_buffers(CAPTURE_queue, &reqbuf);
Steve Chofa7be4c2021-04-27 10:50:48 -0700499 for (uint32_t i = 0; i < reqbuf.count; ++i) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800500 queue_buffer_CAPTURE(CAPTURE_queue, i);
501 }
502 } else {
503 ret = -1;
504 }
505 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800506
Steve Chodc8f92a2021-03-03 17:43:38 -0800507 // 11. Call VIDIOC_STREAMON() on the CAPTURE queue to start decoding frames.
508 if (!ret) {
509 ret = ioctl(CAPTURE_queue->v4lfd, VIDIOC_STREAMON, &CAPTURE_queue->type);
510 if (ret != 0)
511 perror("VIDIOC_STREAMON failed");
512 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800513
Steve Chodc8f92a2021-03-03 17:43:38 -0800514 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800515}
516
Steve Cho1ffec222021-03-04 10:42:45 -0800517void write_file_to_disk(FILE* fp,
518 struct queue* CAPTURE_queue,
Steve Chodc8f92a2021-03-03 17:43:38 -0800519 uint32_t index,
520 uint32_t cnt) {
Steve Cho1ffec222021-03-04 10:42:45 -0800521 if (V4L2_MEMORY_DMABUF == CAPTURE_queue->memory) {
522 struct gbm_bo* bo = CAPTURE_queue->buffers[index].bo;
523 int bo_fd = gbm_bo_get_fd(bo);
524 size_t buffer_size = lseek(bo_fd, 0, SEEK_END);
525 lseek(bo_fd, 0, SEEK_SET);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800526
Steve Chofa7be4c2021-04-27 10:50:48 -0700527 uint8_t* buffer_nv12 =
528 mmap(0, buffer_size, PROT_READ, MAP_SHARED, bo_fd, 0);
Steve Cho1ffec222021-03-04 10:42:45 -0800529
Steve Chofa7be4c2021-04-27 10:50:48 -0700530 // Libvpx golden md5sums are calculated in I420 for VA-API case.
531 // Use |buffer_i420| to deinterleave |buffer_nv12| for this purpose.
532 const uint32_t width = CAPTURE_queue->image_width;
533 const uint32_t height = CAPTURE_queue->image_height;
534 const uint32_t stride = CAPTURE_queue->stride;
Steve Cho1ffec222021-03-04 10:42:45 -0800535
Steve Chofa7be4c2021-04-27 10:50:48 -0700536 const uint32_t buffer_i420_size_in_bytes = (width * height * 3) / 2;
537
538 uint8_t* buffer_i420 = malloc(sizeof(size_t) * buffer_i420_size_in_bytes);
539 assert(buffer_i420);
540 memset(buffer_i420, 0, sizeof(size_t) * buffer_i420_size_in_bytes);
541
542 nv12_to_i420(width, height, stride, buffer_nv12, buffer_i420);
543
544 // Write I420 data to yuv file for each frame.
545 fwrite(buffer_i420, buffer_i420_size_in_bytes, 1, fp);
546
547 // Print frame number together with md5sum for debugging purpose.
548 if (verbose_enabled)
549 printf("frame # %d - ", cnt);
550
551 compute_md5sum(buffer_i420, buffer_i420_size_in_bytes);
552
553 free(buffer_i420);
554
555 munmap(buffer_nv12, buffer_size);
Steve Cho1ffec222021-03-04 10:42:45 -0800556 } else {
557 if (CAPTURE_queue->num_planes == 1) {
558 size_t buffer_size =
559 (3 * CAPTURE_queue->image_width * CAPTURE_queue->image_height) >> 1;
560 uint8_t* buffer = CAPTURE_queue->buffers[index].start[0];
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800561
Steve Chodc8f92a2021-03-03 17:43:38 -0800562 fwrite(buffer, buffer_size, 1, fp);
Steve Chodc8f92a2021-03-03 17:43:38 -0800563 } else {
Steve Cho1ffec222021-03-04 10:42:45 -0800564 for (uint32_t i = 0; i < CAPTURE_queue->num_planes; ++i) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800565 size_t buffer_size =
Steve Cho1ffec222021-03-04 10:42:45 -0800566 (CAPTURE_queue->image_width * CAPTURE_queue->image_height) >> i;
567 uint8_t* buffer = CAPTURE_queue->buffers[index].start[i];
568
Steve Chodc8f92a2021-03-03 17:43:38 -0800569 fwrite(buffer, buffer_size, 1, fp);
Steve Chodc8f92a2021-03-03 17:43:38 -0800570 }
571 }
Steve Chodc8f92a2021-03-03 17:43:38 -0800572 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800573}
574
Steve Chodc8f92a2021-03-03 17:43:38 -0800575int dequeue_buffer(struct queue* queue, uint32_t* index) {
576 struct v4l2_buffer v4l2_buffer;
577 struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
578 memset(&v4l2_buffer, 0, sizeof(v4l2_buffer));
579 v4l2_buffer.type = queue->type;
580 v4l2_buffer.length = queue->num_planes;
581 v4l2_buffer.m.planes = planes;
582 v4l2_buffer.m.planes[0].bytesused = 0;
583 int ret = ioctl(queue->v4lfd, VIDIOC_DQBUF, &v4l2_buffer);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800584
Steve Chodc8f92a2021-03-03 17:43:38 -0800585 *index = v4l2_buffer.index;
586 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800587}
588
Steve Chodc8f92a2021-03-03 17:43:38 -0800589int decode(struct compressed_file* file,
590 struct queue* CAPTURE_queue,
591 struct queue* OUTPUT_queue,
592 uint64_t modifier,
Steve Cho1ffec222021-03-04 10:42:45 -0800593 bool write_out_individual_frames,
594 bool write_out_file,
595 char* file_name,
Steve Chodc8f92a2021-03-03 17:43:38 -0800596 uint32_t frames_to_decode) {
597 int ret = 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800598
Steve Chodc8f92a2021-03-03 17:43:38 -0800599 if (!ret) {
600 uint32_t cnt = 0;
Steve Cho1ffec222021-03-04 10:42:45 -0800601
602 FILE* fp_frame;
603 FILE* fp_file;
604 char filename_frame[256];
605
606 if (write_out_file) {
607 // replace .ivf with .yuv
608 strcpy(strrchr(file_name, '.'), ".yuv");
609
610 fp_file = fopen(file_name, "wb");
611
612 if (!fp_file) {
613 fprintf(stderr, "Unable to open output yuv file: %s\n", file_name);
614 return 1;
615 }
616 }
617
Steve Chodc8f92a2021-03-03 17:43:38 -0800618 while (cnt < frames_to_decode) {
619 {
620 uint32_t index = 0;
621 ret = dequeue_buffer(CAPTURE_queue, &index);
622 if (ret != 0) {
623 if (errno != EAGAIN)
624 perror("VIDIOC_DQBUF failed");
625 continue;
626 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800627
Steve Cho1ffec222021-03-04 10:42:45 -0800628 if (write_out_individual_frames) {
629 sprintf(filename_frame, "image_%dx%d_%d.yuv",
630 CAPTURE_queue->image_width, CAPTURE_queue->image_height, cnt);
631 fp_frame = fopen(filename_frame, "wb");
632
633 if (!fp_frame) {
634 fprintf(stderr, "Unable to open frame yuv file: %s\n",
635 filename_frame);
636 ret = 1;
637 break;
638 } else {
639 write_file_to_disk(fp_frame, CAPTURE_queue, index, cnt);
640 }
641 }
642
643 if (write_out_file) {
644 write_file_to_disk(fp_file, CAPTURE_queue, index, cnt);
645 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800646
Steve Chodc8f92a2021-03-03 17:43:38 -0800647 // Done with buffer, queue it back up.
648 ret = queue_buffer_CAPTURE(CAPTURE_queue, index);
649 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800650
Steve Chodc8f92a2021-03-03 17:43:38 -0800651 // A frame was recieved on the CAPTURE queue, that means there should
652 // now be a free OUTPUT buffer.
653 {
654 uint32_t index = 0;
655 ret = dequeue_buffer(OUTPUT_queue, &index);
656 if (ret != 0) {
657 if (errno != EAGAIN)
658 perror("VIDIOC_DQBUF failed");
659 continue;
660 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800661
Steve Chodc8f92a2021-03-03 17:43:38 -0800662 if (submit_compressed_frame(file, OUTPUT_queue, index))
663 break;
664 }
665 cnt++;
Steve Cho1ffec222021-03-04 10:42:45 -0800666
667 if (write_out_individual_frames)
668 fclose(fp_frame);
Steve Chodc8f92a2021-03-03 17:43:38 -0800669 }
Steve Cho1ffec222021-03-04 10:42:45 -0800670
671 if (write_out_file)
672 fclose(fp_file);
673
Steve Chodc8f92a2021-03-03 17:43:38 -0800674 printf("%d frames decoded.\n", cnt);
675 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800676
Steve Chodc8f92a2021-03-03 17:43:38 -0800677 return ret;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800678}
679
Steve Chodc8f92a2021-03-03 17:43:38 -0800680static void print_help(const char* argv0) {
681 printf("usage: %s [OPTIONS]\n", argv0);
682 printf(" -f, --file ivf file to decode\n");
Steve Cho1ffec222021-03-04 10:42:45 -0800683 printf(
684 " -w, --write write out decompressed frames to individual "
685 "files\n");
686 printf(
687 " -y, --output_yuv write out decompressed frames to a single file\n");
Steve Chodc8f92a2021-03-03 17:43:38 -0800688 printf(" -m, --max max number of frames to decode\n");
689 printf(" -b, --buffer use mmap instead of dmabuf\n");
690 printf(" -o, --output_fmt fourcc of output format\n");
Steve Chofa7be4c2021-04-27 10:50:48 -0700691 printf(" -v, --verbose print additional debugging info\n");
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800692}
693
694static const struct option longopts[] = {
Steve Cho1ffec222021-03-04 10:42:45 -0800695 {"file", required_argument, NULL, 'f'},
696 {"write", no_argument, NULL, 'w'},
697 {"output_yuv", no_argument, NULL, 'y'},
698 {"max", required_argument, NULL, 'm'},
699 {"buffer", no_argument, NULL, 'b'},
700 {"output_fmt", no_argument, NULL, 'o'},
Steve Chofa7be4c2021-04-27 10:50:48 -0700701 {"verbose", no_argument, NULL, 'v'},
Steve Cho1ffec222021-03-04 10:42:45 -0800702 {0, 0, 0, 0},
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800703};
704
Steve Chodc8f92a2021-03-03 17:43:38 -0800705int main(int argc, char* argv[]) {
706 printf("simple v4l2 decode\n");
707 int c;
708 char* file_name = NULL;
Steve Cho1ffec222021-03-04 10:42:45 -0800709 bool write_out_individual_frames = false;
710 bool write_out_file = false;
Steve Chodc8f92a2021-03-03 17:43:38 -0800711 uint32_t frames_to_decode = UINT_MAX;
712 uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
713 uint32_t uncompressed_fourcc = v4l2_fourcc('N', 'V', '1', '2');
714 uint32_t CAPTURE_memory = V4L2_MEMORY_DMABUF;
Steve Chofa7be4c2021-04-27 10:50:48 -0700715 while ((c = getopt_long(argc, argv, "wybvm:f:o:", longopts, NULL)) != -1) {
Steve Chodc8f92a2021-03-03 17:43:38 -0800716 switch (c) {
717 case 'f':
718 file_name = strdup(optarg);
719 break;
720 case 'm':
721 frames_to_decode = atoi(optarg);
722 printf("only decoding a max of %d frames.\n", frames_to_decode);
723 break;
724 case 'w':
Steve Cho1ffec222021-03-04 10:42:45 -0800725 write_out_individual_frames = true;
726 break;
727 case 'y':
728 write_out_file = true;
Steve Chodc8f92a2021-03-03 17:43:38 -0800729 break;
730 case 'b':
731 CAPTURE_memory = V4L2_MEMORY_MMAP;
732 break;
733 case 'o':
734 if (strlen(optarg) == 4) {
735 uncompressed_fourcc =
736 v4l2_fourcc(toupper(optarg[0]), toupper(optarg[1]),
737 toupper(optarg[2]), toupper(optarg[3]));
738 printf("using (%s) as the CAPTURE format\n", optarg);
739 if (uncompressed_fourcc == v4l2_fourcc('Q', '1', '2', '8')) {
740 printf("compressed format, setting modifier\n");
741 modifier = DRM_FORMAT_MOD_QCOM_COMPRESSED;
742 }
743 }
744 break;
Steve Chofa7be4c2021-04-27 10:50:48 -0700745 case 'v':
746 verbose_enabled = true;
747 break;
Steve Chodc8f92a2021-03-03 17:43:38 -0800748 default:
749 break;
750 }
751 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800752
Steve Chodc8f92a2021-03-03 17:43:38 -0800753 if (!file_name) {
754 print_help(argv[0]);
755 exit(1);
756 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800757
Steve Chodc8f92a2021-03-03 17:43:38 -0800758 int drm_device_fd = bs_drm_open_main_display();
759 if (drm_device_fd < 0) {
760 fprintf(stderr, "failed to open card for display\n");
761 return 1;
762 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800763
Steve Chodc8f92a2021-03-03 17:43:38 -0800764 struct gbm_device* gbm = gbm_create_device(drm_device_fd);
765 if (!gbm) {
766 fprintf(stderr, "failed to create gbm device\n");
767 close(drm_device_fd);
768 exit(EXIT_FAILURE);
769 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800770
Steve Chodc8f92a2021-03-03 17:43:38 -0800771 struct compressed_file compressed_file = open_file(file_name);
772 if (!compressed_file.fp) {
773 fprintf(stderr, "Unable to open ivf file: %s\n", file_name);
774 exit(EXIT_FAILURE);
775 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800776
Steve Chodc8f92a2021-03-03 17:43:38 -0800777 int v4lfd = open(kDecodeDevice, O_RDWR | O_NONBLOCK | O_CLOEXEC);
778 if (v4lfd < 0) {
779 fprintf(stderr, "Unable to open device file: %s\n", kDecodeDevice);
780 exit(EXIT_FAILURE);
781 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800782
Steve Chodc8f92a2021-03-03 17:43:38 -0800783 if (capabilities(v4lfd, compressed_file.header.fourcc, uncompressed_fourcc) !=
784 0) {
785 fprintf(stderr, "Capabilities not present for decode.\n");
786 exit(EXIT_FAILURE);
787 }
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800788
Steve Chodc8f92a2021-03-03 17:43:38 -0800789 struct queue OUTPUT_queue = {.v4lfd = v4lfd,
790 .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
791 .fourcc = compressed_file.header.fourcc,
792 .num_planes = 1,
793 .memory = V4L2_MEMORY_MMAP};
794 int ret = setup_OUTPUT(&OUTPUT_queue);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800795
Steve Chodc8f92a2021-03-03 17:43:38 -0800796 if (!ret)
797 ret = prime_OUTPUT(&compressed_file, &OUTPUT_queue);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800798
Steve Chodc8f92a2021-03-03 17:43:38 -0800799 struct queue CAPTURE_queue = {.v4lfd = v4lfd,
800 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
801 .fourcc = uncompressed_fourcc,
802 .num_planes = 1,
803 .memory = CAPTURE_memory};
804 if (!ret)
805 ret = setup_CAPTURE(gbm, &CAPTURE_queue, modifier);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800806
Steve Chodc8f92a2021-03-03 17:43:38 -0800807 if (!ret)
808 ret = decode(&compressed_file, &CAPTURE_queue, &OUTPUT_queue, modifier,
Steve Cho1ffec222021-03-04 10:42:45 -0800809 write_out_individual_frames, write_out_file, file_name,
810 frames_to_decode);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800811
Steve Chodc8f92a2021-03-03 17:43:38 -0800812 cleanup_queue(&OUTPUT_queue);
813 cleanup_queue(&CAPTURE_queue);
814 close(v4lfd);
815 fclose(compressed_file.fp);
816 close(drm_device_fd);
817 free(file_name);
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800818
Steve Chodc8f92a2021-03-03 17:43:38 -0800819 return 0;
Fritz Koenig7d0e8412021-02-05 15:40:08 -0800820}