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
44
48
51
56
59
64
67
75
77 {
80
81 for (
i = 0;
i <
desc->nb_objects;
i++)
83
85 }
86
88 {
90
92 }
93
95 drmModePlane *plane,
97 {
100 int err, fd;
101
102 fb = drmModeGetFB(
ctx->hwctx->fd, plane->fb_id);
104 err = errno;
106 "%"PRIu32": %s.\n", plane->fb_id, strerror(err));
109 }
110 if (
fb->width !=
ctx->width ||
fb->height !=
ctx->height) {
112 "dimensions changed: now %"PRIu32"x%"PRIu32".\n",
113 ctx->plane_id,
fb->width,
fb->height);
116 }
121 }
122
123 err = drmPrimeHandleToFD(
ctx->hwctx->fd,
fb->handle, O_RDONLY, &fd);
124 if (err < 0) {
125 err = errno;
127 "framebuffer handle: %s.\n", strerror(err));
130 }
131
133 .nb_objects = 1,
134 .objects[0] = {
135 .fd = fd,
136 .size =
fb->height *
fb->pitch,
137 .format_modifier =
ctx->drm_format_modifier,
138 },
139 .nb_layers = 1,
140 .layers[0] = {
141 .format =
ctx->drm_format,
142 .nb_planes = 1,
143 .planes[0] = {
144 .object_index = 0,
145 .offset = 0,
147 },
148 },
149 };
150
151 err = 0;
154 return err;
155 }
156
157 #if HAVE_LIBDRM_GETFB2
159 drmModePlane *plane,
161 {
164 int err,
i, nb_objects;
165 uint64_t modifier =
ctx->drm_format_modifier;
166
167 fb = drmModeGetFB2(
ctx->hwctx->fd, plane->fb_id);
169 err = errno;
171 "%"PRIu32": %s.\n", plane->fb_id, strerror(err));
173 }
174 if (
fb->pixel_format !=
ctx->drm_format) {
176 "format changed: now %"PRIx32".\n",
177 ctx->plane_id,
fb->pixel_format);
180 }
181 if (
fb->width !=
ctx->width ||
fb->height !=
ctx->height) {
183 "dimensions changed: now %"PRIu32"x%"PRIu32".\n",
184 ctx->plane_id,
fb->width,
fb->height);
187 }
188 if (!
fb->handles[0]) {
192 }
193
194 if (
fb->flags & DRM_MODE_FB_MODIFIERS)
195 modifier =
fb->modifier;
196
198 .nb_layers = 1,
199 .layers[0] = {
200 .format =
ctx->drm_format,
201 },
202 };
203
204 nb_objects = 0;
205 for (
i = 0;
i < 4 &&
fb->handles[
i];
i++) {
207 int dup = 0, j, obj;
208
210
211 for (j = 0; j <
i; j++) {
212 if (
fb->handles[
i] ==
fb->handles[j]) {
213 dup = 1;
214 break;
215 }
216 }
217 if (dup) {
218 obj =
desc->layers[0].planes[j].object_index;
219
222
224 .object_index = obj,
225 .offset =
fb->offsets[
i],
226 .pitch =
fb->pitches[
i],
227 };
228
229 } else {
230 int fd;
231 err = drmPrimeHandleToFD(
ctx->hwctx->fd,
fb->handles[
i],
232 O_RDONLY, &fd);
233 if (err < 0) {
234 err = errno;
236 "framebuffer handle: %s.\n", strerror(err));
239 }
240
241 obj = nb_objects++;
243 .fd = fd,
245 .format_modifier = modifier,
246 };
248 .object_index = obj,
249 .offset =
fb->offsets[
i],
250 .pitch =
fb->pitches[
i],
251 };
252 }
253 }
254 desc->nb_objects = nb_objects;
255 desc->layers[0].nb_planes =
i;
256
257 err = 0;
260 return err;
261 }
262 #endif
263
265 {
267 drmModePlane *plane =
NULL;
271 int err;
272
274 if (
ctx->frame_last) {
276 while (1) {
277 delay =
ctx->frame_last +
ctx->frame_delay - now;
278 if (delay <= 0)
279 break;
282 }
283 }
284 ctx->frame_last = now;
286
287 plane = drmModeGetPlane(
ctx->hwctx->fd,
ctx->plane_id);
288 if (!plane) {
289 err = errno;
291 "%"PRIu32
": %s.\n",
ctx->plane_id, strerror(err));
294 }
295 if (!plane->fb_id) {
297 "an associated framebuffer.\n",
ctx->plane_id);
300 }
301
306 }
307
308 #if HAVE_LIBDRM_GETFB2
309 if (
ctx->fb2_available)
310 err = kmsgrab_get_fb2(avctx, plane,
desc);
311 else
312 #endif
314 if (err < 0)
316
321 }
322
324 if (!
frame->hw_frames_ctx) {
327 }
328
331 if (!
frame->buf[0]) {
334 }
335
340
341 drmModeFreePlane(plane);
344
350 }
351
356
357 return 0;
358
360 drmModeFreePlane(plane);
363 return err;
364 }
365
366 static const struct {
370 // Monochrome.
371 #ifdef DRM_FORMAT_R8
373 #endif
374 #ifdef DRM_FORMAT_R16
377 #endif
378 // <8-bit RGB.
388 // 8-bit RGB.
399 // 10-bit RGB.
402 // 8-bit YUV 4:2:0.
404 // 8-bit YUV 4:2:2.
408 };
409
411 {
413 drmModePlaneRes *plane_res =
NULL;
414 drmModePlane *plane =
NULL;
416 #if HAVE_LIBDRM_GETFB2
417 drmModeFB2 *fb2 =
NULL;
418 #endif
421
424 if (err < 0) {
426 return err;
427 }
430
431 err = drmSetClientCap(
ctx->hwctx->fd,
432 DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
433 if (err < 0) {
435 "capability: primary planes will not be usable.\n");
436 }
437
438 if (
ctx->source_plane > 0) {
439 plane = drmModeGetPlane(
ctx->hwctx->fd,
ctx->source_plane);
440 if (!plane) {
441 err = errno;
443 "%s.\n",
ctx->source_plane, strerror(err));
446 }
447
448 if (plane->fb_id == 0) {
450 "an attached framebuffer.\n",
ctx->source_plane);
453 }
454 } else {
455 plane_res = drmModeGetPlaneResources(
ctx->hwctx->fd);
456 if (!plane_res) {
457 err = errno;
459 "resources: %s.\n", strerror(err));
462 }
463
464 for (
i = 0;
i < plane_res->count_planes;
i++) {
465 plane = drmModeGetPlane(
ctx->hwctx->fd,
466 plane_res->planes[
i]);
467 if (!plane) {
468 err = errno;
470 "plane %"PRIu32": %s.\n",
471 plane_res->planes[
i], strerror(err));
472 continue;
473 }
474
476 "CRTC %"PRIu32" FB %"PRIu32".\n",
477 plane->plane_id, plane->crtc_id, plane->fb_id);
478
479 if ((
ctx->source_crtc > 0 &&
480 plane->crtc_id !=
ctx->source_crtc) ||
481 plane->fb_id == 0) {
482 // Either not connected to the target source CRTC
483 // or not active.
484 drmModeFreePlane(plane);
486 continue;
487 }
488
489 break;
490 }
491
492 if (
i == plane_res->count_planes) {
493 if (
ctx->source_crtc > 0) {
495 "CRTC %"PRId64
".\n",
ctx->source_crtc);
496 } else {
498 }
501 }
502
504 "locate framebuffers.\n", plane->plane_id);
505 }
506
507 ctx->plane_id = plane->plane_id;
508
509 #if HAVE_LIBDRM_GETFB2
510 fb2 = drmModeGetFB2(
ctx->hwctx->fd, plane->fb_id);
511 if (!fb2 && errno == ENOSYS) {
513 "will try to use GETFB instead.\n");
514 } else if (!fb2) {
515 err = errno;
517 "framebuffer %"PRIu32": %s.\n",
518 plane->fb_id, strerror(err));
521 } else {
523 "%"PRIu32": %"PRIu32"x%"PRIu32" "
524 "format %"PRIx32" modifier %"PRIx64" flags %"PRIx32".\n",
525 fb2->fb_id, fb2->width, fb2->height,
526 fb2->pixel_format, fb2->modifier, fb2->flags);
527
528 ctx->width = fb2->width;
529 ctx->height = fb2->height;
530
531 if (!fb2->handles[0]) {
533 "maybe you need some additional capabilities?\n");
536 }
537
543 "%"PRIx32" does not match expected format.\n",
544 fb2->pixel_format);
547 }
548 ctx->drm_format = fb2->pixel_format;
550 break;
551 }
552 }
555 "%"PRIx32" is not a known supported format.\n",
556 fb2->pixel_format);
559 }
560
561 if (fb2->flags & DRM_MODE_FB_MODIFIERS) {
563 ctx->drm_format_modifier != fb2->modifier) {
565 "%"PRIx64" does not match expected modifier.\n",
566 fb2->modifier);
569 } else {
570 ctx->drm_format_modifier = fb2->modifier;
571 }
572 }
574 "DRM format %"PRIx32" modifier %"PRIx64".\n",
576 ctx->drm_format,
ctx->drm_format_modifier);
577
578 ctx->fb2_available = 1;
579 }
580 #endif
581
582 if (!
ctx->fb2_available) {
584 // Backward compatibility: assume BGR0 if no format supplied.
586 }
590 break;
591 }
592 }
597 }
598
599 fb = drmModeGetFB(
ctx->hwctx->fd, plane->fb_id);
601 err = errno;
603 "framebuffer %"PRIu32": %s.\n",
604 plane->fb_id, strerror(err));
607 }
608
610 "%"PRIu32"x%"PRIu32" %"PRIu32"bpp %"PRIu32"b depth.\n",
611 fb->fb_id,
fb->width,
fb->height,
fb->bpp,
fb->depth);
612
613 ctx->width =
fb->width;
614 ctx->height =
fb->height;
615
618 "maybe you need some additional capabilities?\n");
621 }
622 }
623
625 if (!stream) {
628 }
629
635
637
639 if (!
ctx->frames_ref) {
642 }
644
646 ctx->frames->sw_format =
ctx->format,
647 ctx->frames->width =
ctx->width;
648 ctx->frames->height =
ctx->height;
649
651 if (err < 0) {
653 "hardware frames context: %d.\n", err);
655 }
656
659
660 err = 0;
662 drmModeFreePlaneResources(plane_res);
663 drmModeFreePlane(plane);
665 #if HAVE_LIBDRM_GETFB2
666 drmModeFreeFB2(fb2);
667 #endif
668 return err;
669 }
670
672 {
674
677
678 return 0;
679 }
680
681 #define OFFSET(x) offsetof(KMSGrabContext, x)
682 #define FLAGS AV_OPT_FLAG_DECODING_PARAM
684 { "device", "DRM device path",
686 { .str =
"/dev/dri/card0" }, 0, 0,
FLAGS },
687 { "format", "Pixel format for framebuffer",
690 { "format_modifier", "DRM format modifier for framebuffer",
693 { "crtc_id", "CRTC ID to define capture source",
695 { .i64 = 0 }, 0, UINT32_MAX,
FLAGS },
696 { "plane_id", "Plane ID to define capture source",
698 { .i64 = 0 }, 0, UINT32_MAX,
FLAGS },
699 { "framerate", "Framerate to capture at",
701 { .dbl = 30.0 }, 0, 1000,
FLAGS },
703 };
704
711 };
712
722 };