1 /*
2 * GStreamer
3 * Copyright (C) 2018 Carlos Rafael Giani <dv@pseudoterminal.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <gudev/gudev.h>
24 #include "gstgl_gbm_utils.h"
25
26 GST_DEBUG_CATEGORY_EXTERN (gst_gl_gbm_debug);
27 #define GST_CAT_DEFAULT gst_gl_gbm_debug
28
29 const gchar *
gst_gl_gbm_get_name_for_drm_connector(drmModeConnector * connector)30 gst_gl_gbm_get_name_for_drm_connector (drmModeConnector * connector)
31 {
32 g_assert (connector != NULL);
33
34 switch (connector->connector_type) {
35 case DRM_MODE_CONNECTOR_Unknown:
36 return "Unknown";
37 case DRM_MODE_CONNECTOR_VGA:
38 return "VGA";
39 case DRM_MODE_CONNECTOR_DVII:
40 return "DVI-I";
41 case DRM_MODE_CONNECTOR_DVID:
42 return "DVI-D";
43 case DRM_MODE_CONNECTOR_DVIA:
44 return "DVI-A";
45 case DRM_MODE_CONNECTOR_Composite:
46 return "Composite";
47 case DRM_MODE_CONNECTOR_SVIDEO:
48 return "S-Video";
49 case DRM_MODE_CONNECTOR_LVDS:
50 return "LVDS";
51 case DRM_MODE_CONNECTOR_Component:
52 return "Component";
53 case DRM_MODE_CONNECTOR_9PinDIN:
54 return "9-Pin DIN";
55 case DRM_MODE_CONNECTOR_DisplayPort:
56 return "DP";
57 case DRM_MODE_CONNECTOR_HDMIA:
58 return "HDMI-A";
59 case DRM_MODE_CONNECTOR_HDMIB:
60 return "HDMI-B";
61 case DRM_MODE_CONNECTOR_TV:
62 return "TV";
63 case DRM_MODE_CONNECTOR_eDP:
64 return "eDP";
65 case DRM_MODE_CONNECTOR_VIRTUAL:
66 return "Virtual";
67 case DRM_MODE_CONNECTOR_DSI:
68 return "DSI";
69 case DRM_MODE_CONNECTOR_DPI:
70 return "DPI";
71 default:
72 return "<unknown>";
73 }
74 }
75
76
77 const gchar *
gst_gl_gbm_get_name_for_drm_encoder(drmModeEncoder * encoder)78 gst_gl_gbm_get_name_for_drm_encoder (drmModeEncoder * encoder)
79 {
80 switch (encoder->encoder_type) {
81 case DRM_MODE_ENCODER_NONE:
82 return "none";
83 case DRM_MODE_ENCODER_DAC:
84 return "DAC";
85 case DRM_MODE_ENCODER_TMDS:
86 return "TMDS";
87 case DRM_MODE_ENCODER_LVDS:
88 return "LVDS";
89 case DRM_MODE_ENCODER_TVDAC:
90 return "TVDAC";
91 case DRM_MODE_ENCODER_VIRTUAL:
92 return "Virtual";
93 case DRM_MODE_ENCODER_DSI:
94 return "DSI";
95 default:
96 return "<unknown>";
97 }
98 }
99
100
101 const gchar *
gst_gl_gbm_format_to_string(guint32 format)102 gst_gl_gbm_format_to_string (guint32 format)
103 {
104 if (format == GBM_BO_FORMAT_XRGB8888)
105 format = GBM_FORMAT_XRGB8888;
106 if (format == GBM_BO_FORMAT_ARGB8888)
107 format = GBM_FORMAT_ARGB8888;
108
109 switch (format) {
110 case GBM_FORMAT_C8:
111 return "C8";
112 case GBM_FORMAT_RGB332:
113 return "RGB332";
114 case GBM_FORMAT_BGR233:
115 return "BGR233";
116 case GBM_FORMAT_NV12:
117 return "NV12";
118 case GBM_FORMAT_XRGB4444:
119 return "XRGB4444";
120 case GBM_FORMAT_XBGR4444:
121 return "XBGR4444";
122 case GBM_FORMAT_RGBX4444:
123 return "RGBX4444";
124 case GBM_FORMAT_BGRX4444:
125 return "BGRX4444";
126 case GBM_FORMAT_XRGB1555:
127 return "XRGB1555";
128 case GBM_FORMAT_XBGR1555:
129 return "XBGR1555";
130 case GBM_FORMAT_RGBX5551:
131 return "RGBX5551";
132 case GBM_FORMAT_BGRX5551:
133 return "BGRX5551";
134 case GBM_FORMAT_ARGB4444:
135 return "ARGB4444";
136 case GBM_FORMAT_ABGR4444:
137 return "ABGR4444";
138 case GBM_FORMAT_RGBA4444:
139 return "RGBA4444";
140 case GBM_FORMAT_BGRA4444:
141 return "BGRA4444";
142 case GBM_FORMAT_ARGB1555:
143 return "ARGB1555";
144 case GBM_FORMAT_ABGR1555:
145 return "ABGR1555";
146 case GBM_FORMAT_RGBA5551:
147 return "RGBA5551";
148 case GBM_FORMAT_BGRA5551:
149 return "BGRA5551";
150 case GBM_FORMAT_RGB565:
151 return "RGB565";
152 case GBM_FORMAT_BGR565:
153 return "BGR565";
154 case GBM_FORMAT_YUYV:
155 return "YUYV";
156 case GBM_FORMAT_YVYU:
157 return "YVYU";
158 case GBM_FORMAT_UYVY:
159 return "UYVY";
160 case GBM_FORMAT_VYUY:
161 return "VYUY";
162 case GBM_FORMAT_RGB888:
163 return "RGB888";
164 case GBM_FORMAT_BGR888:
165 return "BGR888";
166 case GBM_FORMAT_XRGB8888:
167 return "XRGB8888";
168 case GBM_FORMAT_XBGR8888:
169 return "XBGR8888";
170 case GBM_FORMAT_RGBX8888:
171 return "RGBX8888";
172 case GBM_FORMAT_BGRX8888:
173 return "BGRX8888";
174 case GBM_FORMAT_AYUV:
175 return "AYUV";
176 case GBM_FORMAT_XRGB2101010:
177 return "XRGB2101010";
178 case GBM_FORMAT_XBGR2101010:
179 return "XBGR2101010";
180 case GBM_FORMAT_RGBX1010102:
181 return "RGBX1010102";
182 case GBM_FORMAT_BGRX1010102:
183 return "BGRX1010102";
184 case GBM_FORMAT_ARGB8888:
185 return "ARGB8888";
186 case GBM_FORMAT_ABGR8888:
187 return "ABGR8888";
188 case GBM_FORMAT_RGBA8888:
189 return "RGBA8888";
190 case GBM_FORMAT_BGRA8888:
191 return "BGRA8888";
192 case GBM_FORMAT_ARGB2101010:
193 return "ARGB2101010";
194 case GBM_FORMAT_ABGR2101010:
195 return "ABGR2101010";
196 case GBM_FORMAT_RGBA1010102:
197 return "RGBA1010102";
198 case GBM_FORMAT_BGRA1010102:
199 return "BGRA1010102";
200
201 default:
202 return "<unknown>";
203 }
204
205 return NULL;
206 }
207
208
209 int
gst_gl_gbm_depth_from_format(guint32 format)210 gst_gl_gbm_depth_from_format (guint32 format)
211 {
212 if (format == GBM_BO_FORMAT_XRGB8888)
213 format = GBM_FORMAT_XRGB8888;
214 if (format == GBM_BO_FORMAT_ARGB8888)
215 format = GBM_FORMAT_ARGB8888;
216
217 switch (format) {
218 case GBM_FORMAT_C8:
219 case GBM_FORMAT_RGB332:
220 case GBM_FORMAT_BGR233:
221 return 8;
222
223 case GBM_FORMAT_NV12:
224 case GBM_FORMAT_XRGB4444:
225 case GBM_FORMAT_XBGR4444:
226 case GBM_FORMAT_RGBX4444:
227 case GBM_FORMAT_BGRX4444:
228 return 12;
229
230 case GBM_FORMAT_XRGB1555:
231 case GBM_FORMAT_XBGR1555:
232 case GBM_FORMAT_RGBX5551:
233 case GBM_FORMAT_BGRX5551:
234 return 15;
235
236 case GBM_FORMAT_ARGB4444:
237 case GBM_FORMAT_ABGR4444:
238 case GBM_FORMAT_RGBA4444:
239 case GBM_FORMAT_BGRA4444:
240 case GBM_FORMAT_ARGB1555:
241 case GBM_FORMAT_ABGR1555:
242 case GBM_FORMAT_RGBA5551:
243 case GBM_FORMAT_BGRA5551:
244 case GBM_FORMAT_RGB565:
245 case GBM_FORMAT_BGR565:
246 case GBM_FORMAT_YUYV:
247 case GBM_FORMAT_YVYU:
248 case GBM_FORMAT_UYVY:
249 case GBM_FORMAT_VYUY:
250 return 16;
251
252 case GBM_FORMAT_RGB888:
253 case GBM_FORMAT_BGR888:
254 case GBM_FORMAT_XRGB8888:
255 case GBM_FORMAT_XBGR8888:
256 case GBM_FORMAT_RGBX8888:
257 case GBM_FORMAT_BGRX8888:
258 case GBM_FORMAT_AYUV:
259 return 24;
260
261 case GBM_FORMAT_XRGB2101010:
262 case GBM_FORMAT_XBGR2101010:
263 case GBM_FORMAT_RGBX1010102:
264 case GBM_FORMAT_BGRX1010102:
265 return 30;
266
267 case GBM_FORMAT_ARGB8888:
268 case GBM_FORMAT_ABGR8888:
269 case GBM_FORMAT_RGBA8888:
270 case GBM_FORMAT_BGRA8888:
271 case GBM_FORMAT_ARGB2101010:
272 case GBM_FORMAT_ABGR2101010:
273 case GBM_FORMAT_RGBA1010102:
274 case GBM_FORMAT_BGRA1010102:
275 return 32;
276
277 default:
278 GST_ERROR ("unknown GBM format %" G_GUINT32_FORMAT, format);
279 }
280
281 return 0;
282 }
283
284
285 int
gst_gl_gbm_bpp_from_format(guint32 format)286 gst_gl_gbm_bpp_from_format (guint32 format)
287 {
288 if (format == GBM_BO_FORMAT_XRGB8888)
289 format = GBM_FORMAT_XRGB8888;
290 if (format == GBM_BO_FORMAT_ARGB8888)
291 format = GBM_FORMAT_ARGB8888;
292
293 switch (format) {
294 case GBM_FORMAT_C8:
295 case GBM_FORMAT_RGB332:
296 case GBM_FORMAT_BGR233:
297 return 8;
298
299 case GBM_FORMAT_NV12:
300 return 12;
301
302 case GBM_FORMAT_XRGB4444:
303 case GBM_FORMAT_XBGR4444:
304 case GBM_FORMAT_RGBX4444:
305 case GBM_FORMAT_BGRX4444:
306 case GBM_FORMAT_ARGB4444:
307 case GBM_FORMAT_ABGR4444:
308 case GBM_FORMAT_RGBA4444:
309 case GBM_FORMAT_BGRA4444:
310 case GBM_FORMAT_XRGB1555:
311 case GBM_FORMAT_XBGR1555:
312 case GBM_FORMAT_RGBX5551:
313 case GBM_FORMAT_BGRX5551:
314 case GBM_FORMAT_ARGB1555:
315 case GBM_FORMAT_ABGR1555:
316 case GBM_FORMAT_RGBA5551:
317 case GBM_FORMAT_BGRA5551:
318 case GBM_FORMAT_RGB565:
319 case GBM_FORMAT_BGR565:
320 case GBM_FORMAT_YUYV:
321 case GBM_FORMAT_YVYU:
322 case GBM_FORMAT_UYVY:
323 case GBM_FORMAT_VYUY:
324 return 16;
325
326 case GBM_FORMAT_RGB888:
327 case GBM_FORMAT_BGR888:
328 return 24;
329
330 case GBM_FORMAT_XRGB8888:
331 case GBM_FORMAT_XBGR8888:
332 case GBM_FORMAT_RGBX8888:
333 case GBM_FORMAT_BGRX8888:
334 case GBM_FORMAT_ARGB8888:
335 case GBM_FORMAT_ABGR8888:
336 case GBM_FORMAT_RGBA8888:
337 case GBM_FORMAT_BGRA8888:
338 case GBM_FORMAT_XRGB2101010:
339 case GBM_FORMAT_XBGR2101010:
340 case GBM_FORMAT_RGBX1010102:
341 case GBM_FORMAT_BGRX1010102:
342 case GBM_FORMAT_ARGB2101010:
343 case GBM_FORMAT_ABGR2101010:
344 case GBM_FORMAT_RGBA1010102:
345 case GBM_FORMAT_BGRA1010102:
346 case GBM_FORMAT_AYUV:
347 return 32;
348
349 default:
350 GST_ERROR ("unknown GBM format %" G_GUINT32_FORMAT, format);
351 }
352
353 return 0;
354 }
355
356
357 static void
gst_gl_gbm_drm_fb_destroy_callback(struct gbm_bo * bo,void * data)358 gst_gl_gbm_drm_fb_destroy_callback (struct gbm_bo *bo, void *data)
359 {
360 int drm_fd = gbm_device_get_fd (gbm_bo_get_device (bo));
361 GstGLDRMFramebuffer *fb = (GstGLDRMFramebuffer *) (data);
362
363 if (fb->fb_id)
364 drmModeRmFB (drm_fd, fb->fb_id);
365
366 g_slice_free1 (sizeof (GstGLDRMFramebuffer), fb);
367 }
368
369
370 GstGLDRMFramebuffer *
gst_gl_gbm_drm_fb_get_from_bo(struct gbm_bo * bo)371 gst_gl_gbm_drm_fb_get_from_bo (struct gbm_bo *bo)
372 {
373 GstGLDRMFramebuffer *fb;
374 int drm_fd;
375 guint32 width, height, stride, format, handle;
376 int depth, bpp;
377 int ret;
378
379 /* We want to use this buffer object (abbr. "bo") as a scanout buffer.
380 * To that end, we associate the bo with the DRM by using drmModeAddFB().
381 * However, this needs to be called exactly once for the given bo, and the
382 * counterpart, drmModeRmFB(), needs to be called when the bo is cleaned up.
383 *
384 * To fulfill these requirements, add extra framebuffer information to the
385 * bo as "user data". This way, if this user data pointer is NULL, it means
386 * that no framebuffer information was generated yet & the bo was not set
387 * as a scanout buffer with drmModeAddFB() yet, and we have perform these
388 * steps. Otherwise, if it is non-NULL, we know we do not have to set up
389 * anything (since it was done already) and just return the pointer to the
390 * framebuffer information. */
391 fb = (GstGLDRMFramebuffer *) (gbm_bo_get_user_data (bo));
392 if (fb != NULL) {
393 /* The bo was already set up as a scanout framebuffer. Just
394 * return the framebuffer information. */
395 return fb;
396 }
397
398 /* If this point is reached, then we have to setup the bo as a
399 * scanout framebuffer. */
400
401 drm_fd = gbm_device_get_fd (gbm_bo_get_device (bo));
402
403 fb = g_slice_alloc0 (sizeof (GstGLDRMFramebuffer));
404 fb->bo = bo;
405
406 width = gbm_bo_get_width (bo);
407 height = gbm_bo_get_height (bo);
408 stride = gbm_bo_get_stride (bo);
409 format = gbm_bo_get_format (bo);
410 handle = gbm_bo_get_handle (bo).u32;
411
412 depth = gst_gl_gbm_depth_from_format (format);
413 bpp = gst_gl_gbm_bpp_from_format (format);
414
415 GST_DEBUG ("Attempting to add GBM BO as scanout framebuffer width/height: %"
416 G_GUINT32_FORMAT "/%" G_GUINT32_FORMAT " pixels stride: %"
417 G_GUINT32_FORMAT " bytes format: %s depth: %d bits total bpp: %d bits",
418 width, height, stride, gst_gl_gbm_format_to_string (format), depth, bpp);
419
420 /* Set the bo as a scanout framebuffer */
421 ret = drmModeAddFB (drm_fd, width, height, depth, bpp, stride, handle,
422 &fb->fb_id);
423 if (ret != 0) {
424 GST_ERROR ("Failed to add GBM BO as scanout framebuffer: %s (%d)",
425 g_strerror (errno), errno);
426 g_slice_free1 (sizeof (GstGLDRMFramebuffer), fb);
427 return NULL;
428 }
429
430 /* Add the framebuffer information to the bo as user data, and also install a callback
431 * that cleans up this extra information whenever the bo itself is discarded */
432 gbm_bo_set_user_data (bo, fb, gst_gl_gbm_drm_fb_destroy_callback);
433
434 return fb;
435 }
436
437
438 int
gst_gl_gbm_find_and_open_drm_node(void)439 gst_gl_gbm_find_and_open_drm_node (void)
440 {
441 /* In here we use GUDev to try to autodetect the GPU */
442
443 int drm_fd = -1;
444 GUdevClient *gudev_client = NULL;
445 GUdevEnumerator *gudev_enum = NULL;
446 GList *devlist = NULL;
447 GList *deventry = NULL;
448 const gchar *subsystems[2] = { "drm", NULL };
449
450 gudev_client = g_udev_client_new (subsystems);
451 if (gudev_client == NULL) {
452 GST_ERROR ("Could not create gudev client");
453 goto cleanup;
454 }
455 GST_DEBUG ("Created gudev client");
456
457 gudev_enum = g_udev_enumerator_new (gudev_client);
458 if (gudev_enum == NULL) {
459 GST_ERROR ("Could not create gudev enumerator");
460 goto cleanup;
461 }
462 GST_DEBUG ("Created gudev enumerator");
463
464 /* TODO: To be 100% sure we pick the right device, also check
465 * if this is a GPU, because a pure scanout device could also
466 * have a DRM subsystem for example. However, currently it is
467 * unclear how to do that. By trying to create an EGL context? */
468 g_udev_enumerator_add_match_subsystem (gudev_enum, "drm");
469 devlist = g_udev_enumerator_execute (gudev_enum);
470 GST_DEBUG ("Scanned for udev devices with a drm subsystem");
471
472 if (devlist == NULL) {
473 GST_WARNING ("Found no matching DRM devices");
474 goto cleanup;
475 }
476 GST_DEBUG ("Got %u potentially matching device(s)", g_list_length (devlist));
477
478 for (deventry = devlist; deventry != NULL; deventry = deventry->next) {
479 GUdevDevice *gudevice = G_UDEV_DEVICE (deventry->data);
480 const gchar *devnode = g_udev_device_get_device_file (gudevice);
481
482 if ((devnode == NULL) || !g_str_has_prefix (devnode, "/dev/dri/card"))
483 continue;
484
485 GST_DEBUG ("Found DRM device with device node \"%s\"", devnode);
486
487 drm_fd = open (devnode, O_RDWR | O_CLOEXEC);
488 if (drm_fd < 0) {
489 GST_WARNING ("Cannot open device node \"%s\": %s (%d)", devnode,
490 g_strerror (errno), errno);
491 continue;
492 }
493
494 GST_DEBUG ("Device node \"%s\" is a valid DRM device node", devnode);
495 break;
496 }
497
498
499 done:
500
501 if (devlist != NULL) {
502 g_list_free_full (devlist, g_object_unref);
503 devlist = NULL;
504 GST_DEBUG ("Cleaned up device list");
505 }
506
507 if (gudev_enum != NULL) {
508 g_object_unref (G_OBJECT (gudev_enum));
509 gudev_enum = NULL;
510 GST_DEBUG ("Cleaned up gudev enumerator");
511 }
512
513 if (gudev_client != NULL) {
514 g_object_unref (G_OBJECT (gudev_client));
515 gudev_client = NULL;
516 GST_DEBUG ("Cleaned up gudev client");
517 }
518
519 return drm_fd;
520
521
522 cleanup:
523
524 if (drm_fd >= 0) {
525 close (drm_fd);
526 drm_fd = -1;
527 }
528
529 goto done;
530 }
531