1 /*
2 * KMS/DRM input device
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <fcntl.h>
22 #include <unistd.h>
23
24 #include <drm.h>
25 #include <drm_fourcc.h>
26 #include <drm_mode.h>
27 #include <xf86drm.h>
28 #include <xf86drmMode.h>
29
30 // Required for compatibility when building against libdrm < 2.4.83.
31 #ifndef DRM_FORMAT_MOD_INVALID
32 #define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
33 #endif
34
43
46
49
54
57
62
65
73
75 {
78
79 for (
i = 0;
i <
desc->nb_objects;
i++)
80 close(
desc->objects[
i].fd);
81
83 }
84
86 {
88
90 }
91
93 drmModePlane *plane,
95 {
98 int err, fd;
99
100 fb = drmModeGetFB(
ctx->hwctx->fd, plane->fb_id);
102 err = errno;
104 "%"PRIu32": %s.\n", plane->fb_id, strerror(err));
107 }
108 if (
fb->width !=
ctx->width ||
fb->height !=
ctx->height) {
110 "dimensions changed: now %"PRIu32"x%"PRIu32".\n",
111 ctx->plane_id,
fb->width,
fb->height);
114 }
119 }
120
121 err = drmPrimeHandleToFD(
ctx->hwctx->fd,
fb->handle, O_RDONLY, &fd);
122 if (err < 0) {
123 err = errno;
125 "framebuffer handle: %s.\n", strerror(err));
128 }
129
131 .nb_objects = 1,
132 .objects[0] = {
133 .fd = fd,
134 .size =
fb->height *
fb->pitch,
135 .format_modifier =
ctx->drm_format_modifier,
136 },
137 .nb_layers = 1,
138 .layers[0] = {
139 .format =
ctx->drm_format,
140 .nb_planes = 1,
141 .planes[0] = {
142 .object_index = 0,
143 .offset = 0,
145 },
146 },
147 };
148
149 err = 0;
152 return err;
153 }
154
155 #if HAVE_LIBDRM_GETFB2
157 drmModePlane *plane,
159 {
162 int err,
i, nb_objects;
163 uint64_t modifier =
ctx->drm_format_modifier;
164
165 fb = drmModeGetFB2(
ctx->hwctx->fd, plane->fb_id);
167 err = errno;
169 "%"PRIu32": %s.\n", plane->fb_id, strerror(err));
171 }
172 if (
fb->pixel_format !=
ctx->drm_format) {
174 "format changed: now %"PRIx32".\n",
175 ctx->plane_id,
fb->pixel_format);
178 }
179 if (
fb->width !=
ctx->width ||
fb->height !=
ctx->height) {
181 "dimensions changed: now %"PRIu32"x%"PRIu32".\n",
182 ctx->plane_id,
fb->width,
fb->height);
185 }
186 if (!
fb->handles[0]) {
190 }
191
192 if (
fb->flags & DRM_MODE_FB_MODIFIERS)
193 modifier =
fb->modifier;
194
196 .nb_layers = 1,
197 .layers[0] = {
198 .format =
ctx->drm_format,
199 },
200 };
201
202 nb_objects = 0;
203 for (
i = 0;
i < 4 &&
fb->handles[
i];
i++) {
205 int dup = 0, j, obj;
206
208
209 for (j = 0; j <
i; j++) {
210 if (
fb->handles[
i] ==
fb->handles[j]) {
211 dup = 1;
212 break;
213 }
214 }
215 if (dup) {
216 obj =
desc->layers[0].planes[j].object_index;
217
220
222 .object_index = obj,
223 .offset =
fb->offsets[
i],
224 .pitch =
fb->pitches[
i],
225 };
226
227 } else {
228 int fd;
229 err = drmPrimeHandleToFD(
ctx->hwctx->fd,
fb->handles[
i],
230 O_RDONLY, &fd);
231 if (err < 0) {
232 err = errno;
234 "framebuffer handle: %s.\n", strerror(err));
237 }
238
239 obj = nb_objects++;
241 .fd = fd,
243 .format_modifier = modifier,
244 };
246 .object_index = obj,
247 .offset =
fb->offsets[
i],
248 .pitch =
fb->pitches[
i],
249 };
250 }
251 }
252 desc->nb_objects = nb_objects;
253 desc->layers[0].nb_planes =
i;
254
255 err = 0;
258 return err;
259 }
260 #endif
261
263 {
265 drmModePlane *plane =
NULL;
268 int64_t now;
269 int err;
270
272 if (
ctx->frame_last) {
273 int64_t delay;
274 while (1) {
275 delay =
ctx->frame_last +
ctx->frame_delay - now;
276 if (delay <= 0)
277 break;
280 }
281 }
282 ctx->frame_last = now;
284
285 plane = drmModeGetPlane(
ctx->hwctx->fd,
ctx->plane_id);
286 if (!plane) {
287 err = errno;
289 "%"PRIu32
": %s.\n",
ctx->plane_id, strerror(err));
292 }
293 if (!plane->fb_id) {
295 "an associated framebuffer.\n",
ctx->plane_id);
298 }
299
304 }
305
306 #if HAVE_LIBDRM_GETFB2
307 if (
ctx->fb2_available)
308 err = kmsgrab_get_fb2(avctx, plane,
desc);
309 else
310 #endif
312 if (err < 0)
314
319 }
320
322 if (!
frame->hw_frames_ctx) {
325 }
326
329 if (!
frame->buf[0]) {
332 }
333
338
339 drmModeFreePlane(plane);
342
348 }
349
354
355 return 0;
356
358 drmModeFreePlane(plane);
361 return err;
362 }
363
364 static const struct {
368 // Monochrome.
369 #ifdef DRM_FORMAT_R8
371 #endif
372 #ifdef DRM_FORMAT_R16
375 #endif
376 // <8-bit RGB.
386 // 8-bit RGB.
397 // 10-bit RGB.
400 // 8-bit YUV 4:2:0.
402 // 8-bit YUV 4:2:2.
406 };
407
409 {
411 drmModePlaneRes *plane_res =
NULL;
412 drmModePlane *plane =
NULL;
414 #if HAVE_LIBDRM_GETFB2
415 drmModeFB2 *fb2 =
NULL;
416 #endif
419
422 if (err < 0) {
424 return err;
425 }
428
429 err = drmSetClientCap(
ctx->hwctx->fd,
430 DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
431 if (err < 0) {
433 "capability: primary planes will not be usable.\n");
434 }
435
436 if (
ctx->source_plane > 0) {
437 plane = drmModeGetPlane(
ctx->hwctx->fd,
ctx->source_plane);
438 if (!plane) {
439 err = errno;
441 "%s.\n",
ctx->source_plane, strerror(err));
444 }
445
446 if (plane->fb_id == 0) {
448 "an attached framebuffer.\n",
ctx->source_plane);
451 }
452 } else {
453 plane_res = drmModeGetPlaneResources(
ctx->hwctx->fd);
454 if (!plane_res) {
455 err = errno;
457 "resources: %s.\n", strerror(err));
460 }
461
462 for (
i = 0;
i < plane_res->count_planes;
i++) {
463 plane = drmModeGetPlane(
ctx->hwctx->fd,
464 plane_res->planes[
i]);
465 if (!plane) {
466 err = errno;
468 "plane %"PRIu32": %s.\n",
469 plane_res->planes[
i], strerror(err));
470 continue;
471 }
472
474 "CRTC %"PRIu32" FB %"PRIu32".\n",
475 plane->plane_id, plane->crtc_id, plane->fb_id);
476
477 if ((
ctx->source_crtc > 0 &&
478 plane->crtc_id !=
ctx->source_crtc) ||
479 plane->fb_id == 0) {
480 // Either not connected to the target source CRTC
481 // or not active.
482 drmModeFreePlane(plane);
484 continue;
485 }
486
487 break;
488 }
489
490 if (
i == plane_res->count_planes) {
491 if (
ctx->source_crtc > 0) {
493 "CRTC %"PRId64
".\n",
ctx->source_crtc);
494 } else {
496 }
499 }
500
502 "locate framebuffers.\n", plane->plane_id);
503 }
504
505 ctx->plane_id = plane->plane_id;
506
507 #if HAVE_LIBDRM_GETFB2
508 fb2 = drmModeGetFB2(
ctx->hwctx->fd, plane->fb_id);
509 if (!fb2 && errno == ENOSYS) {
511 "will try to use GETFB instead.\n");
512 } else if (!fb2) {
513 err = errno;
515 "framebuffer %"PRIu32": %s.\n",
516 plane->fb_id, strerror(err));
519 } else {
521 "%"PRIu32": %"PRIu32"x%"PRIu32" "
522 "format %"PRIx32" modifier %"PRIx64" flags %"PRIx32".\n",
523 fb2->fb_id, fb2->width, fb2->height,
524 fb2->pixel_format, fb2->modifier, fb2->flags);
525
526 ctx->width = fb2->width;
527 ctx->height = fb2->height;
528
529 if (!fb2->handles[0]) {
531 "maybe you need some additional capabilities?\n");
534 }
535
541 "%"PRIx32" does not match expected format.\n",
542 fb2->pixel_format);
545 }
546 ctx->drm_format = fb2->pixel_format;
548 break;
549 }
550 }
553 "%"PRIx32" is not a known supported format.\n",
554 fb2->pixel_format);
557 }
558
559 if (fb2->flags & DRM_MODE_FB_MODIFIERS) {
561 ctx->drm_format_modifier != fb2->modifier) {
563 "%"PRIx64" does not match expected modifier.\n",
564 fb2->modifier);
567 } else {
568 ctx->drm_format_modifier = fb2->modifier;
569 }
570 }
572 "DRM format %"PRIx32" modifier %"PRIx64".\n",
574 ctx->drm_format,
ctx->drm_format_modifier);
575
576 ctx->fb2_available = 1;
577 }
578 #endif
579
580 if (!
ctx->fb2_available) {
582 // Backward compatibility: assume BGR0 if no format supplied.
584 }
588 break;
589 }
590 }
595 }
596
597 fb = drmModeGetFB(
ctx->hwctx->fd, plane->fb_id);
599 err = errno;
601 "framebuffer %"PRIu32": %s.\n",
602 plane->fb_id, strerror(err));
605 }
606
608 "%"PRIu32"x%"PRIu32" %"PRIu32"bpp %"PRIu32"b depth.\n",
609 fb->fb_id,
fb->width,
fb->height,
fb->bpp,
fb->depth);
610
611 ctx->width =
fb->width;
612 ctx->height =
fb->height;
613
616 "maybe you need some additional capabilities?\n");
619 }
620 }
621
623 if (!stream) {
626 }
627
633
635
637 if (!
ctx->frames_ref) {
640 }
642
644 ctx->frames->sw_format =
ctx->format,
645 ctx->frames->width =
ctx->width;
646 ctx->frames->height =
ctx->height;
647
649 if (err < 0) {
651 "hardware frames context: %d.\n", err);
653 }
654
657
658 err = 0;
660 drmModeFreePlaneResources(plane_res);
661 drmModeFreePlane(plane);
663 #if HAVE_LIBDRM_GETFB2
664 drmModeFreeFB2(fb2);
665 #endif
666 return err;
667 }
668
670 {
672
675
676 return 0;
677 }
678
679 #define OFFSET(x) offsetof(KMSGrabContext, x)
680 #define FLAGS AV_OPT_FLAG_DECODING_PARAM
682 { "device", "DRM device path",
684 { .str =
"/dev/dri/card0" }, 0, 0,
FLAGS },
685 { "format", "Pixel format for framebuffer",
688 { "format_modifier", "DRM format modifier for framebuffer",
691 { "crtc_id", "CRTC ID to define capture source",
693 { .i64 = 0 }, 0, UINT32_MAX,
FLAGS },
694 { "plane_id", "Plane ID to define capture source",
696 { .i64 = 0 }, 0, UINT32_MAX,
FLAGS },
697 { "framerate", "Framerate to capture at",
699 { .dbl = 30.0 }, 0, 1000,
FLAGS },
701 };
702
709 };
710
720 };