1 /*
2 * Copyright 2022 Rockchip Electronics Co., Ltd
3 * Author: Jeffy Chen <jeffy.chen@rock-chips.com>
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 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28
29 #include <xf86drm.h>
30 #include <xf86drmMode.h>
31 #include <drm_fourcc.h>
32
33 #include <gst/video/video.h>
34 #include <gst/base/gstpushsrc.h>
35 #include <gst/allocators/gstdmabuf.h>
36
37 #ifndef DRM_FORMAT_NV12_10
38 #define DRM_FORMAT_NV12_10 fourcc_code('N', 'A', '1', '2')
39 #endif
40
41 #ifndef DRM_FORMAT_NV15
42 #define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5')
43 #endif
44
45 #define DEFAULT_PROP_FRAMERATE_LIMIT 120
46
47 static gboolean DEFAULT_PROP_DMA_FEATURE = FALSE;
48
49 #define GST_TYPE_KMS_SRC (gst_kms_src_get_type())
50 G_DECLARE_FINAL_TYPE (GstKmsSrc, gst_kms_src, GST, KMS_SRC, GstPushSrc);
51
52 struct _GstKmsSrc {
53 GstBaseSrc element;
54
55 GstAllocator *allocator;
56 GstVideoInfo info;
57
58 gchar *devname;
59 gchar *bus_id;
60
61 gint fd;
62
63 guint crtc_id;
64 guint encoder_id;
65 guint connector_id;
66 guint plane_id;
67 guint fb_id;
68
69 gboolean dma_feature;
70
71 guint framerate_limit;
72 guint fps_n;
73 guint fps_d;
74 gboolean sync_fb;
75 gboolean sync_vblank;
76
77 GstPoll *poll;
78 GstPollFD pollfd;
79
80 guint last_fb_id;
81 GstClockTime last_frame_time;
82
83 GstClockTime start_time;
84 };
85
86 #define parent_class gst_kms_src_parent_class
87 G_DEFINE_TYPE (GstKmsSrc, gst_kms_src, GST_TYPE_PUSH_SRC);
88
89 #define GST_KMS_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
90 GST_TYPE_KMS_SRC, GstKmsSrc))
91
92 #define GST_CAT_DEFAULT kms_src_debug
93 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
94
95 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
96 GST_PAD_SRC,
97 GST_PAD_ALWAYS,
98 GST_STATIC_CAPS_ANY);
99
100 enum
101 {
102 PROP_DRIVER_NAME = 1,
103 PROP_BUS_ID,
104 PROP_CRTC_ID,
105 PROP_ENCODER_ID,
106 PROP_CONNECTOR_ID,
107 PROP_PLANE_ID,
108 PROP_FB_ID,
109 PROP_DMA_FEATURE,
110 PROP_FRAMERATE_LIMIT,
111 PROP_SYNC_FB,
112 PROP_SYNC_VBLANK,
113 PROP_LAST,
114 };
115
116 struct kmssrc_fb {
117 guint handles[4];
118 guint pitches[4];
119 guint offsets[4];
120 guint width;
121 guint height;
122 guint fourcc;
123 };
124
125 static void
gst_kms_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)126 gst_kms_src_set_property (GObject * object, guint prop_id,
127 const GValue * value, GParamSpec * pspec)
128 {
129 GstKmsSrc *self = GST_KMS_SRC (object);
130
131 switch (prop_id) {
132 case PROP_DRIVER_NAME:
133 g_free (self->devname);
134 self->devname = g_value_dup_string (value);
135 break;
136 case PROP_BUS_ID:
137 g_free (self->bus_id);
138 self->bus_id = g_value_dup_string (value);
139 break;
140 case PROP_CRTC_ID:
141 self->crtc_id = g_value_get_uint (value);
142 break;
143 case PROP_ENCODER_ID:
144 self->encoder_id = g_value_get_uint (value);
145 break;
146 case PROP_CONNECTOR_ID:
147 self->connector_id = g_value_get_uint (value);
148 break;
149 case PROP_PLANE_ID:
150 self->plane_id = g_value_get_uint (value);
151 break;
152 case PROP_FB_ID:
153 self->fb_id = g_value_get_uint (value);
154 break;
155 case PROP_DMA_FEATURE:
156 self->dma_feature = g_value_get_boolean (value);
157 break;
158 case PROP_FRAMERATE_LIMIT:
159 self->framerate_limit = g_value_get_uint (value);
160 break;
161 case PROP_SYNC_FB:
162 self->sync_fb = g_value_get_boolean (value);
163 break;
164 case PROP_SYNC_VBLANK:
165 self->sync_vblank = g_value_get_boolean (value);
166 break;
167 default:
168 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
169 break;
170 }
171 }
172
173 static void
gst_kms_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)174 gst_kms_src_get_property (GObject * object, guint prop_id, GValue * value,
175 GParamSpec * pspec)
176 {
177 GstKmsSrc *self = GST_KMS_SRC (object);
178
179 switch (prop_id) {
180 case PROP_DRIVER_NAME:
181 g_value_set_string (value, self->devname);
182 break;
183 case PROP_BUS_ID:
184 g_value_set_string (value, self->bus_id);
185 break;
186 case PROP_CRTC_ID:
187 g_value_set_uint (value, self->crtc_id);
188 break;
189 case PROP_ENCODER_ID:
190 g_value_set_uint (value, self->encoder_id);
191 break;
192 case PROP_CONNECTOR_ID:
193 g_value_set_uint (value, self->connector_id);
194 break;
195 case PROP_PLANE_ID:
196 g_value_set_uint (value, self->plane_id);
197 break;
198 case PROP_FB_ID:
199 g_value_set_uint (value, self->fb_id);
200 break;
201 case PROP_DMA_FEATURE:
202 g_value_set_boolean (value, self->dma_feature);
203 break;
204 case PROP_FRAMERATE_LIMIT:
205 g_value_set_uint (value, self->framerate_limit);
206 break;
207 case PROP_SYNC_FB:
208 g_value_set_boolean (value, self->sync_fb);
209 break;
210 case PROP_SYNC_VBLANK:
211 g_value_set_boolean (value, self->sync_vblank);
212 break;
213 default:
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215 break;
216 }
217 }
218
219 static guint
gst_kms_src_get_crtc_fb(GstKmsSrc * self,guint crtc_id)220 gst_kms_src_get_crtc_fb (GstKmsSrc * self, guint crtc_id)
221 {
222 drmModeCrtcPtr crtc;
223 guint fb_id;
224
225 crtc = drmModeGetCrtc (self->fd, crtc_id);
226 if (!crtc)
227 return 0;
228
229 fb_id = crtc->buffer_id;
230
231 drmModeFreeCrtc (crtc);
232 return fb_id;
233 }
234
235 static guint
gst_kms_src_get_encoder_fb(GstKmsSrc * self,guint encoder_id)236 gst_kms_src_get_encoder_fb (GstKmsSrc * self, guint encoder_id)
237 {
238 drmModeEncoderPtr encoder;
239 guint fb_id;
240
241 encoder = drmModeGetEncoder (self->fd, encoder_id);
242 if (!encoder)
243 return 0;
244
245 fb_id = gst_kms_src_get_crtc_fb (self, encoder->crtc_id);
246
247 drmModeFreeEncoder (encoder);
248 return fb_id;
249 }
250
251 static guint
gst_kms_src_get_connector_fb(GstKmsSrc * self,guint connector_id)252 gst_kms_src_get_connector_fb (GstKmsSrc * self, guint connector_id)
253 {
254 drmModeConnectorPtr connector;
255 guint fb_id;
256
257 connector = drmModeGetConnector (self->fd, connector_id);
258 if (!connector)
259 return 0;
260
261 fb_id = gst_kms_src_get_encoder_fb (self, connector->encoder_id);
262
263 drmModeFreeConnector (connector);
264 return fb_id;
265 }
266
267 static guint
gst_kms_src_get_plane_fb(GstKmsSrc * self,guint plane_id)268 gst_kms_src_get_plane_fb (GstKmsSrc * self, guint plane_id)
269 {
270 drmModePlanePtr plane;
271 guint fb_id;
272
273 plane = drmModeGetPlane (self->fd, plane_id);
274 if (!plane)
275 return 0;
276
277 fb_id = plane->fb_id;
278
279 drmModeFreePlane (plane);
280 return fb_id;
281 }
282
283 static guint
gst_kms_src_get_fb_id(GstKmsSrc * self)284 gst_kms_src_get_fb_id (GstKmsSrc * self)
285 {
286 if (self->fb_id)
287 return self->fb_id;
288
289 if (self->plane_id)
290 return gst_kms_src_get_plane_fb (self, self->plane_id);
291
292 if (self->connector_id)
293 return gst_kms_src_get_connector_fb (self, self->connector_id);
294
295 if (self->encoder_id)
296 return gst_kms_src_get_encoder_fb (self, self->encoder_id);
297
298 if (self->crtc_id)
299 return gst_kms_src_get_crtc_fb (self, self->crtc_id);
300
301 return 0;
302 }
303
304 static guint
gst_kms_src_get_encoder_crtc(GstKmsSrc * self,guint encoder_id)305 gst_kms_src_get_encoder_crtc (GstKmsSrc * self, guint encoder_id)
306 {
307 drmModeEncoderPtr encoder;
308 guint crtc_id;
309
310 encoder = drmModeGetEncoder (self->fd, encoder_id);
311 if (!encoder)
312 return 0;
313
314 crtc_id = encoder->crtc_id;
315
316 drmModeFreeEncoder (encoder);
317 return crtc_id;
318 }
319
320 static guint
gst_kms_src_get_connector_crtc(GstKmsSrc * self,guint connector_id)321 gst_kms_src_get_connector_crtc (GstKmsSrc * self, guint connector_id)
322 {
323 drmModeConnectorPtr connector;
324 guint crtc_id;
325
326 connector = drmModeGetConnector (self->fd, connector_id);
327 if (!connector)
328 return 0;
329
330 crtc_id = gst_kms_src_get_encoder_crtc (self, connector->encoder_id);
331
332 drmModeFreeConnector (connector);
333 return crtc_id;
334 }
335
336 static guint
gst_kms_src_get_plane_crtc(GstKmsSrc * self,guint plane_id)337 gst_kms_src_get_plane_crtc (GstKmsSrc * self, guint plane_id)
338 {
339 drmModePlanePtr plane;
340 guint crtc_id;
341
342 plane = drmModeGetPlane (self->fd, plane_id);
343 if (!plane)
344 return 0;
345
346 crtc_id = plane->crtc_id;
347
348 drmModeFreePlane (plane);
349 return crtc_id;
350 }
351
352 static guint
gst_kms_src_get_crtc_id(GstKmsSrc * self)353 gst_kms_src_get_crtc_id (GstKmsSrc * self)
354 {
355 if (self->crtc_id)
356 return self->crtc_id;
357
358 if (self->plane_id)
359 return gst_kms_src_get_plane_crtc (self, self->plane_id);
360
361 if (self->connector_id)
362 return gst_kms_src_get_connector_crtc (self, self->connector_id);
363
364 if (self->encoder_id)
365 return gst_kms_src_get_encoder_crtc (self, self->encoder_id);
366
367 return 0;
368 }
369
370 static guint
gst_kms_src_get_crtc_pipe(GstKmsSrc * self,guint crtc_id)371 gst_kms_src_get_crtc_pipe (GstKmsSrc * self, guint crtc_id)
372 {
373 drmModeResPtr res;
374 guint crtc_pipe = 0;
375 gint i;
376
377 if (!crtc_id)
378 return 0;
379
380 res = drmModeGetResources (self->fd);
381 if (!res)
382 return 0;
383
384 for (i = 0; i < res->count_crtcs; i++) {
385 if (res->crtcs[i] == crtc_id) {
386 crtc_pipe = i;
387 break;
388 }
389 }
390 drmModeFreeResources (res);
391 return crtc_pipe;
392 }
393
394 static void
sync_handler(gint fd,guint frame,guint sec,guint usec,gpointer data)395 sync_handler (gint fd, guint frame, guint sec, guint usec, gpointer data)
396 {
397 gboolean *waiting;
398
399 (void) fd;
400 (void) frame;
401 (void) sec;
402 (void) usec;
403
404 waiting = data;
405 *waiting = FALSE;
406 }
407
408 static gboolean
gst_kms_src_sync_vblank(GstKmsSrc * self)409 gst_kms_src_sync_vblank (GstKmsSrc * self)
410 {
411 gboolean waiting;
412 drmEventContext evctxt = {
413 .version = DRM_EVENT_CONTEXT_VERSION,
414 .vblank_handler = sync_handler,
415 };
416 drmVBlank vbl = {
417 .request = {
418 .type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
419 .sequence = 1,
420 .signal = (gulong) & waiting,
421 },
422 };
423 guint crtc_id = gst_kms_src_get_crtc_id (self);
424 guint crtc_pipe = gst_kms_src_get_crtc_pipe (self, crtc_id);
425 gint ret;
426
427 if (crtc_pipe == 1)
428 vbl.request.type |= DRM_VBLANK_SECONDARY;
429 else if (crtc_pipe > 1)
430 vbl.request.type |= crtc_pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
431
432 waiting = TRUE;
433 ret = drmWaitVBlank (self->fd, &vbl);
434 if (ret)
435 return FALSE;
436
437 while (waiting) {
438 do {
439 ret = gst_poll_wait (self->poll, 3 * GST_SECOND);
440 } while (ret == -1 && (errno == EAGAIN || errno == EINTR));
441
442 ret = drmHandleEvent (self->fd, &evctxt);
443 if (ret)
444 return FALSE;
445 }
446
447 GST_DEBUG_OBJECT (self, "sync vblank with CRTC: %d(%d)", crtc_id, crtc_pipe);
448 return TRUE;
449 }
450
451 static guint
gst_kms_src_get_next_fb_id(GstKmsSrc * self)452 gst_kms_src_get_next_fb_id (GstKmsSrc * self)
453 {
454 GstClockTimeDiff diff;
455 gboolean sync_fb = self->sync_fb && !self->fb_id;
456 guint duration = 0;
457 guint fb_id;
458
459 if (self->sync_vblank)
460 gst_kms_src_sync_vblank (self);
461
462 if (self->fps_d && self->fps_n)
463 duration =
464 gst_util_uint64_scale (GST_SECOND, self->fps_d, self->fps_n);
465 else if (self->framerate_limit)
466 duration = GST_SECOND / self->framerate_limit;
467
468 while (1) {
469 diff = GST_CLOCK_DIFF (self->last_frame_time, gst_util_get_timestamp ());
470
471 fb_id = gst_kms_src_get_fb_id (self);
472 if (!fb_id)
473 return 0;
474
475 if (!sync_fb || fb_id != self->last_fb_id) {
476 sync_fb = FALSE;
477
478 if (diff >= duration)
479 return fb_id;
480 }
481
482 g_usleep (1000);
483 }
484
485 return 0;
486 }
487
488 #ifndef HAS_DRM_CLOSE_HANDLE
489 /* From libdrm 2.4.109 : xf86drm.c */
490 static int
drmCloseBufferHandle(int fd,uint32_t handle)491 drmCloseBufferHandle(int fd, uint32_t handle)
492 {
493 struct drm_gem_close args;
494
495 memset(&args, 0, sizeof(args));
496 args.handle = handle;
497 return drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &args);
498 }
499 #endif
500
501 static void
gst_kms_src_free_fb(GstKmsSrc * self,struct kmssrc_fb * fb)502 gst_kms_src_free_fb (GstKmsSrc * self, struct kmssrc_fb * fb)
503 {
504 guint i;
505
506 for (i = 0; i < 4; i++) {
507 if (fb->handles[i])
508 drmCloseBufferHandle (self->fd, fb->handles[i]);
509
510 fb->handles[i] = 0;
511 }
512 }
513
514 static gboolean
gst_kms_src_update_info(GstKmsSrc * self,struct kmssrc_fb * fb)515 gst_kms_src_update_info (GstKmsSrc * self, struct kmssrc_fb * fb)
516 {
517 GstVideoInfo *info = &self->info;
518 GstVideoFormat format;
519 guint i;
520
521 #define KMSSRC_CASE_FOURCC(fourcc, gst) \
522 case DRM_FORMAT_ ## fourcc: format = GST_VIDEO_FORMAT_ ## gst; break;
523
524 switch (fb->fourcc) {
525 KMSSRC_CASE_FOURCC (ARGB8888, BGRA);
526 KMSSRC_CASE_FOURCC (XRGB8888, BGRx);
527 KMSSRC_CASE_FOURCC (ABGR8888, RGBA);
528 KMSSRC_CASE_FOURCC (XBGR8888, RGBx);
529 KMSSRC_CASE_FOURCC (RGB888, BGR);
530 KMSSRC_CASE_FOURCC (BGR888, RGB);
531 KMSSRC_CASE_FOURCC (YUV422, Y42B);
532 KMSSRC_CASE_FOURCC (NV16, NV16);
533 KMSSRC_CASE_FOURCC (NV61, NV61);
534 KMSSRC_CASE_FOURCC (UYVY, UYVY);
535 KMSSRC_CASE_FOURCC (YVYU, YVYU);
536 KMSSRC_CASE_FOURCC (YUYV, YUY2);
537 KMSSRC_CASE_FOURCC (YUV420, I420);
538 KMSSRC_CASE_FOURCC (YVU420, YV12);
539 KMSSRC_CASE_FOURCC (NV12, NV12);
540 KMSSRC_CASE_FOURCC (NV21, NV21);
541 KMSSRC_CASE_FOURCC (RGB565, RGB16);
542 KMSSRC_CASE_FOURCC (BGR565, BGR16);
543 #ifdef HAVE_NV12_10LE40
544 KMSSRC_CASE_FOURCC (NV15, NV12_10LE40);
545 KMSSRC_CASE_FOURCC (NV12_10, NV12_10LE40);
546 #endif
547 default:
548 GST_ERROR_OBJECT (self, "format not supported %x", fb->fourcc);
549 return FALSE;
550 }
551
552 gst_video_info_set_format (info, format, fb->width, fb->height);
553
554 GST_VIDEO_INFO_SIZE (info) = 0;
555 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
556 GST_VIDEO_INFO_PLANE_STRIDE (info, i) = fb->pitches[i];
557 GST_VIDEO_INFO_PLANE_OFFSET (info, i) = fb->offsets[i];
558
559 GST_DEBUG_OBJECT (self, "plane %d, stride %d, offset %" G_GSIZE_FORMAT, i,
560 GST_VIDEO_INFO_PLANE_STRIDE (info, i),
561 GST_VIDEO_INFO_PLANE_OFFSET (info, i));
562
563 GST_VIDEO_INFO_SIZE (info) += fb->pitches[i] * fb->height;
564 }
565 GST_DEBUG_OBJECT (self, "size %" G_GSIZE_FORMAT, GST_VIDEO_INFO_SIZE (info));
566
567 return TRUE;
568 }
569
570 static gboolean
gst_kms_src_get_fb(GstKmsSrc * self,guint fb_id,struct kmssrc_fb * kmssrc_fb)571 gst_kms_src_get_fb (GstKmsSrc * self, guint fb_id, struct kmssrc_fb * kmssrc_fb)
572 {
573 drmModeFBPtr fb;
574 #ifdef HAS_DRM_MODE_FB2
575 drmModeFB2Ptr fb2;
576 guint i;
577 #endif
578
579 memset (kmssrc_fb, 0, sizeof (*kmssrc_fb));
580
581 #ifdef HAS_DRM_MODE_FB2
582 fb2 = drmModeGetFB2 (self->fd, fb_id);
583 if (fb2) {
584 for (i = 0; i < 4; i++) {
585 kmssrc_fb->handles[i] = fb2->handles[i];
586 kmssrc_fb->pitches[i] = fb2->pitches[i];
587 kmssrc_fb->offsets[i] = fb2->offsets[i];
588 }
589 kmssrc_fb->fourcc = fb2->pixel_format;
590 kmssrc_fb->width = fb2->width;
591 kmssrc_fb->height = fb2->height;
592 drmModeFreeFB2 (fb2);
593 return TRUE;
594 }
595 #endif
596
597 fb = drmModeGetFB (self->fd, fb_id);
598 if (!fb)
599 return FALSE;
600
601 kmssrc_fb->handles[0] = fb->handle;
602 kmssrc_fb->pitches[0] = fb->pitch;
603
604 switch (fb->bpp) {
605 case 32:
606 if (fb->depth == 32)
607 kmssrc_fb->fourcc = DRM_FORMAT_ARGB8888;
608 else
609 kmssrc_fb->fourcc = DRM_FORMAT_XRGB8888;
610 break;
611 case 16:
612 kmssrc_fb->fourcc = DRM_FORMAT_RGB565;
613 break;
614 default:
615 GST_ERROR_OBJECT (self, "FB format unsupported");
616 gst_kms_src_free_fb (self, kmssrc_fb);
617 drmModeFreeFB (fb);
618 return FALSE;
619 }
620
621 kmssrc_fb->width = fb->width;
622 kmssrc_fb->height = fb->height;
623 drmModeFreeFB (fb);
624 return TRUE;
625 }
626
627 static GstBuffer *
gst_kms_src_import_drm_fb(GstKmsSrc * self,guint fb_id)628 gst_kms_src_import_drm_fb (GstKmsSrc * self, guint fb_id)
629 {
630 GstVideoInfo *info = &self->info;
631 GstBuffer *buf = NULL;
632 GstMemory *mem;
633 struct kmssrc_fb fb;
634 struct stat st[4];
635 gint i, dmafd[4], size[4];
636
637 if (!gst_kms_src_get_fb (self, fb_id, &fb)) {
638 GST_ERROR_OBJECT (self, "could not get DRM FB %d", fb_id);
639 return NULL;
640 }
641
642 if (!gst_kms_src_update_info (self, &fb))
643 goto err;
644
645 buf = gst_buffer_new ();
646 if (!buf)
647 goto err;
648
649 for (i = 0; i < 4; i++) {
650 if (!fb.handles[i])
651 break;
652
653 size[i] = fb.pitches[i] * fb.height;
654
655 GST_DEBUG_OBJECT (self, "importing DRM handle %d(%d) for %dx%d",
656 fb.handles[i], i, fb.pitches[i], fb.height);
657
658 if (drmPrimeHandleToFD (self->fd, fb.handles[i], DRM_CLOEXEC | DRM_RDWR,
659 &dmafd[i]) < 0) {
660 GST_ERROR_OBJECT (self, "could not import DRM handle %d", fb.handles[i]);
661 goto err;
662 }
663
664 fstat (dmafd[i], &st[i]);
665 if (i && !memcmp (&st[i], &st[i - 1], sizeof (struct stat))) {
666 /* Reuse the old fd */
667 close (dmafd[i]);
668 dmafd[i] = dmafd[i - 1];
669
670 /* Check for contig planes */
671 if (fb.offsets[i] == fb.offsets[i - 1] + size[i - 1]) {
672 gsize mem_size, mem_offset;
673
674 mem_size = gst_memory_get_sizes (mem, &mem_offset, NULL);
675 gst_memory_resize (mem, mem_offset, mem_size + size[i]);
676 continue;
677 }
678 }
679
680 mem = gst_fd_allocator_alloc (self->allocator, dmafd[i], st[i].st_size,
681 GST_FD_MEMORY_FLAG_NONE);
682 if (!mem) {
683 close (dmafd[i]);
684 goto err;
685 }
686
687 gst_memory_resize (mem, fb.offsets[i], size[i]);
688 gst_buffer_append_memory (buf, mem);
689 }
690
691 gst_buffer_add_video_meta_full (buf, GST_VIDEO_FRAME_FLAG_NONE,
692 GST_VIDEO_INFO_FORMAT (info),
693 GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
694 GST_VIDEO_INFO_N_PLANES (info), info->offset, info->stride);
695
696 gst_kms_src_free_fb (self, &fb);
697 return buf;
698 err:
699 if (buf)
700 gst_buffer_unref (buf);
701
702 gst_kms_src_free_fb (self, &fb);
703 return NULL;
704 }
705
706 static GstFlowReturn
gst_kms_src_create(GstPushSrc * src,GstBuffer ** ret)707 gst_kms_src_create (GstPushSrc * src, GstBuffer ** ret)
708 {
709 GstKmsSrc *self = GST_KMS_SRC (src);
710 GstBuffer *buf;
711 guint fb_id;
712
713 GST_DEBUG_OBJECT (self, "creating buffer");
714
715 fb_id = gst_kms_src_get_next_fb_id (self);
716 if (!fb_id) {
717 GST_ERROR_OBJECT (self, "could not get valid FB");
718 goto err;
719 }
720
721 GST_DEBUG_OBJECT (self, "importing DRM FB %d (old: %d)",
722 fb_id, self->last_fb_id);
723
724 buf = gst_kms_src_import_drm_fb (self, fb_id);
725 if (!buf) {
726 GST_ERROR_OBJECT (self, "could not import FB %d", fb_id);
727 goto err;
728 }
729
730 self->last_frame_time = gst_util_get_timestamp ();
731 self->last_fb_id = fb_id;
732
733 GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) =
734 self->last_frame_time - self->start_time;
735
736 *ret = buf;
737
738 return GST_FLOW_OK;
739 err:
740 if (self->last_fb_id)
741 return GST_FLOW_EOS;
742
743 return GST_FLOW_ERROR;
744 }
745
746 /* Based on gst-plugins-good/sys/ximage/gstximagesrc.c */
747 static gboolean
gst_kms_src_set_caps(GstBaseSrc * basesrc,GstCaps * caps)748 gst_kms_src_set_caps (GstBaseSrc * basesrc, GstCaps * caps)
749 {
750 GstKmsSrc *self = GST_KMS_SRC (basesrc);
751 GstVideoInfo *info = &self->info;
752 GstStructure *structure;
753 const GValue *new_fps;
754
755 /* The only thing that can change is the framerate downstream wants */
756 structure = gst_caps_get_structure (caps, 0);
757 new_fps = gst_structure_get_value (structure, "framerate");
758 if (!new_fps)
759 return FALSE;
760
761 /* Store this FPS for use when generating buffers */
762 info->fps_n = self->fps_n = gst_value_get_fraction_numerator (new_fps);
763 info->fps_d = self->fps_d = gst_value_get_fraction_denominator (new_fps);
764
765 GST_DEBUG_OBJECT (self, "peer wants %d/%d fps", self->fps_n, self->fps_d);
766
767 if (self->framerate_limit * self->fps_d > self->fps_n)
768 GST_WARNING_OBJECT (self, "framerate-limit ignored");
769
770 if (self->sync_fb)
771 GST_WARNING_OBJECT (self, "sync-fb enabled, framerate might be inaccurate");
772
773 return TRUE;
774 }
775
776 static GstCaps *
gst_kms_src_get_caps(GstBaseSrc * basesrc,GstCaps * filter)777 gst_kms_src_get_caps (GstBaseSrc * basesrc, GstCaps * filter)
778 {
779 GstKmsSrc *self = GST_KMS_SRC (basesrc);
780 GstStructure *s;
781 GstCaps *caps;
782 struct kmssrc_fb fb;
783 guint fb_id;
784
785 fb_id = gst_kms_src_get_fb_id (self);
786 if (!fb_id)
787 return gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (basesrc));
788
789 if (!gst_kms_src_get_fb (self, fb_id, &fb)) {
790 GST_ERROR_OBJECT (self, "could not get DRM FB %d", fb_id);
791 return NULL;
792 }
793
794 if (!gst_kms_src_update_info (self, &fb)) {
795 gst_kms_src_free_fb (self, &fb);
796 return NULL;
797 }
798
799 gst_kms_src_free_fb (self, &fb);
800
801 caps = gst_video_info_to_caps (&self->info);
802
803 if (self->dma_feature)
804 gst_caps_set_features (caps, 0,
805 gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_DMABUF, NULL));
806
807 if (filter) {
808 GstCaps *intersection;
809
810 intersection =
811 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
812 gst_caps_unref (caps);
813 caps = intersection;
814 }
815
816 s = gst_caps_get_structure (caps, 0);
817 gst_structure_set (s, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT,
818 1, NULL);
819
820 GST_DEBUG_OBJECT (self, "returning caps: %" GST_PTR_FORMAT, caps);
821 return caps;
822 }
823
824 static gboolean
gst_kms_src_stop(GstBaseSrc * basesrc)825 gst_kms_src_stop (GstBaseSrc * basesrc)
826 {
827 GstKmsSrc *self = GST_KMS_SRC (basesrc);
828
829 gst_poll_remove_fd (self->poll, &self->pollfd);
830 gst_poll_restart (self->poll);
831 gst_poll_fd_init (&self->pollfd);
832
833 if (self->allocator)
834 g_object_unref (self->allocator);
835
836 if (self->fd >= 0)
837 drmClose (self->fd);
838
839 return TRUE;
840 }
841
842 static guint
gst_kms_src_find_best_crtc(GstKmsSrc * self)843 gst_kms_src_find_best_crtc (GstKmsSrc * self)
844 {
845 drmModeCrtcPtr crtc;
846 drmModeResPtr res;
847 guint crtc_id;
848 gint i;
849
850 res = drmModeGetResources (self->fd);
851 if (!res)
852 return 0;
853
854 for (i = 0, crtc_id = 0; i < res->count_crtcs; i++) {
855 crtc = drmModeGetCrtc (self->fd, res->crtcs[i]);
856 if (crtc && crtc->mode_valid) {
857 drmModeFreeCrtc (crtc);
858 crtc_id = res->crtcs[i];
859 break;
860 }
861 drmModeFreeCrtc (crtc);
862 }
863 drmModeFreeResources (res);
864
865 if (crtc_id)
866 GST_DEBUG_OBJECT (self, "using best CRTC %d", crtc_id);
867
868 return crtc_id;
869 }
870
871 static gboolean
gst_kms_src_start(GstBaseSrc * basesrc)872 gst_kms_src_start (GstBaseSrc * basesrc)
873 {
874 GstKmsSrc *self = GST_KMS_SRC (basesrc);
875
876 self->allocator = gst_dmabuf_allocator_new ();
877 if (!self->allocator)
878 return FALSE;
879
880 if (self->devname || self->bus_id)
881 self->fd = drmOpen (self->devname, self->bus_id);
882
883 if (self->fd < 0)
884 self->fd = open ("/dev/dri/card0", O_RDWR | O_CLOEXEC);
885
886 if (self->fd < 0) {
887 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE,
888 ("Could not open DRM module %s", GST_STR_NULL (self->devname)),
889 ("reason: %s (%d)", g_strerror (errno), errno));
890 gst_kms_src_stop (basesrc);
891 return FALSE;
892 }
893
894 if (!self->fb_id && !self->plane_id && !self->connector_id &&
895 !self->encoder_id && !self->crtc_id) {
896 self->crtc_id = gst_kms_src_find_best_crtc (self);
897 if (!self->crtc_id) {
898 GST_ERROR_OBJECT (self, "could not find a valid CRTC");
899 gst_kms_src_stop (basesrc);
900 return FALSE;
901 }
902 }
903
904 self->last_fb_id = 0;
905 self->last_frame_time = gst_util_get_timestamp ();
906 self->start_time = gst_util_get_timestamp ();
907
908 self->pollfd.fd = self->fd;
909 gst_poll_add_fd (self->poll, &self->pollfd);
910 gst_poll_fd_ctl_read (self->poll, &self->pollfd, TRUE);
911
912 return TRUE;
913 }
914
915 static void
gst_kms_src_finalize(GObject * object)916 gst_kms_src_finalize (GObject * object)
917 {
918 GstKmsSrc *self;
919
920 self = GST_KMS_SRC (object);
921 g_clear_pointer (&self->devname, g_free);
922 g_clear_pointer (&self->bus_id, g_free);
923
924 gst_poll_free (self->poll);
925
926 G_OBJECT_CLASS (parent_class)->finalize (object);
927 }
928
929 static void
gst_kms_src_init(GstKmsSrc * self)930 gst_kms_src_init (GstKmsSrc * self)
931 {
932 self->devname = g_strdup ("rockchip");
933
934 self->fd = -1;
935
936 self->crtc_id = 0;
937 self->encoder_id = 0;
938 self->connector_id = 0;
939 self->plane_id = 0;
940 self->fb_id = 0;
941
942 self->framerate_limit = DEFAULT_PROP_FRAMERATE_LIMIT;
943 self->sync_fb = TRUE;
944 self->sync_vblank = TRUE;
945 self->fps_n = 0;
946 self->fps_d = 1;
947
948 gst_video_info_init (&self->info);
949
950 gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME);
951 gst_base_src_set_live (GST_BASE_SRC (self), TRUE);
952
953 gst_poll_fd_init (&self->pollfd);
954 self->poll = gst_poll_new (TRUE);
955 }
956
957 static void
gst_kms_src_class_init(GstKmsSrcClass * klass)958 gst_kms_src_class_init (GstKmsSrcClass * klass)
959 {
960 GObjectClass *gobject_class;
961 GstElementClass *gstelement_class;
962 GstBaseSrcClass *gstbase_src_class;
963 GstPushSrcClass *gstpush_src_class;
964 const gchar *env;
965
966 gobject_class = G_OBJECT_CLASS (klass);
967 gstelement_class = GST_ELEMENT_CLASS (klass);
968 gstbase_src_class = GST_BASE_SRC_CLASS (klass);
969 gstpush_src_class = GST_PUSH_SRC_CLASS (klass);
970
971 gobject_class->finalize = gst_kms_src_finalize;
972 gobject_class->set_property = gst_kms_src_set_property;
973 gobject_class->get_property = gst_kms_src_get_property;
974
975 g_object_class_install_property (gobject_class, PROP_DRIVER_NAME,
976 g_param_spec_string ("driver-name",
977 "device name", "DRM device driver name", NULL,
978 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
979
980 g_object_class_install_property (gobject_class, PROP_BUS_ID,
981 g_param_spec_string ("bus-id", "Bus ID", "DRM bus ID", NULL,
982 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
983
984 g_object_class_install_property (gobject_class, PROP_CRTC_ID,
985 g_param_spec_uint ("crtc-id", "DRM crtc ID",
986 "DRM crtc ID (0 = unspecified)",
987 0, G_MAXINT, 0,
988 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
989
990 g_object_class_install_property (gobject_class, PROP_ENCODER_ID,
991 g_param_spec_uint ("encoder-id", "DRM plane ID",
992 "DRM encoder ID (0 = unspecified)",
993 0, G_MAXINT, 0,
994 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
995
996 g_object_class_install_property (gobject_class, PROP_CONNECTOR_ID,
997 g_param_spec_uint ("connector-id", "DRM connector ID",
998 "DRM connector ID (0 = unspecified)",
999 0, G_MAXINT, 0,
1000 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1001
1002 g_object_class_install_property (gobject_class, PROP_PLANE_ID,
1003 g_param_spec_uint ("plane-id", "DRM plane ID",
1004 "DRM plane ID (0 = unspecified)",
1005 0, G_MAXINT, 0,
1006 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1007
1008 g_object_class_install_property (gobject_class, PROP_FB_ID,
1009 g_param_spec_uint ("fb-id", "DRM FB ID",
1010 "DRM FB ID (0 = unspecified)",
1011 0, G_MAXINT, 0,
1012 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1013
1014 env = g_getenv ("GST_KMSSRC_DMA_FEATURE");
1015 if (env && !strcmp (env, "1"))
1016 DEFAULT_PROP_DMA_FEATURE = TRUE;
1017
1018 g_object_class_install_property (gobject_class, PROP_DMA_FEATURE,
1019 g_param_spec_boolean ("dma-feature", "DMA feature",
1020 "Enable GST DMA feature", DEFAULT_PROP_DMA_FEATURE,
1021 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1022
1023 g_object_class_install_property (gobject_class, PROP_FRAMERATE_LIMIT,
1024 g_param_spec_uint ("framerate-limit", "Limited framerate",
1025 "Limited framerate",
1026 0, G_MAXINT, DEFAULT_PROP_FRAMERATE_LIMIT,
1027 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1028
1029 g_object_class_install_property (gobject_class, PROP_SYNC_FB,
1030 g_param_spec_boolean ("sync-fb", "Sync with FB flip",
1031 "Sync with FB flip", TRUE,
1032 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1033
1034 g_object_class_install_property (gobject_class, PROP_SYNC_VBLANK,
1035 g_param_spec_boolean ("sync-vblank", "Sync with vblank",
1036 "Sync with vblank", TRUE,
1037 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1038
1039 gst_element_class_set_static_metadata (gstelement_class,
1040 "KMS Video Source",
1041 "Source/Video",
1042 "KMS Video Source",
1043 "Jeffy Chen <jeffy.chen@rock-chips.com>");
1044
1045 gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
1046
1047 gstbase_src_class->set_caps = GST_DEBUG_FUNCPTR (gst_kms_src_set_caps);
1048 gstbase_src_class->get_caps = GST_DEBUG_FUNCPTR (gst_kms_src_get_caps);
1049 gstbase_src_class->start = GST_DEBUG_FUNCPTR (gst_kms_src_start);
1050 gstbase_src_class->stop = GST_DEBUG_FUNCPTR (gst_kms_src_stop);
1051 gstpush_src_class->create = GST_DEBUG_FUNCPTR (gst_kms_src_create);
1052 }
1053
1054 static gboolean
plugin_init(GstPlugin * plugin)1055 plugin_init (GstPlugin * plugin)
1056 {
1057 if (!gst_element_register (plugin, "kmssrc", GST_RANK_NONE,
1058 gst_kms_src_get_type ()))
1059 return FALSE;
1060
1061 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "kmssrc", 0, "KmsSrc");
1062 return TRUE;
1063 }
1064
1065 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1066 GST_VERSION_MINOR,
1067 kmssrc,
1068 "KMS Src Plugin",
1069 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
1070