dri2videosink: destroy context only whe window is destroyed
[gstreamer-omap:gst-plugins-bad.git] / sys / dri2 / gstdri2util.c
1 /*
2  * GStreamer
3  *
4  * Copyright (C) 2012 Texas Instruments
5  *
6  * Authors:
7  *  Rob Clark <rob.clark@linaro.org>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation
12  * version 2.1 of the License.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <ctype.h>
29
30 #include <gst/video/video-crop.h>
31
32 #include "gstdri2util.h"
33 #include "gstdri2bufferpool.h"
34
35 static GstMiniObjectClass *dri2window_parent_class = NULL;
36
37 static Bool WireToEvent (Display * dpy, XExtDisplayInfo * info,
38     XEvent * event, xEvent * wire)
39 {
40   switch ((wire->u.u.type & 0x7f) - info->codes->first_event) {
41
42   case DRI2_BufferSwapComplete: {
43     //    xDRI2BufferSwapComplete *awire = (xDRI2BufferSwapComplete *)wire;
44     // TODO use this to know when the previous buffer is no longer visible..
45     GST_LOG ("BufferSwapComplete");
46     return True;
47   }
48   case DRI2_InvalidateBuffers: {
49     //    xDRI2InvalidateBuffers *awire = (xDRI2InvalidateBuffers *)wire;
50     GST_LOG ("InvalidateBuffers");
51     //    dri2InvalidateBuffers(dpy, awire->drawable);
52     return False;
53   }
54   default:
55     /* client doesn't support server event */
56     break;
57   }
58
59   return False;
60 }
61
62 static Status EventToWire (Display * dpy, XExtDisplayInfo * info,
63     XEvent * event, xEvent * wire)
64 {
65   switch (event->type) {
66   default:
67     /* client doesn't support server event */
68     break;
69   }
70
71   return Success;
72 }
73
74 static const DRI2EventOps ops = {
75     .WireToEvent = WireToEvent,
76     .EventToWire = EventToWire,
77 };
78
79 static DRI2Buffer * get_buffer (GstDRI2Window * xwindow, guint attach,
80     gint width, gint height, guint32 format);
81
82 static Bool is_fourcc(guint32 val)
83 {
84   return g_ascii_isalnum ((val >> 24) & 0xff)
85       && g_ascii_isalnum ((val >> 16) & 0xff)
86       && g_ascii_isalnum ((val >> 8) & 0xff)
87       && g_ascii_isalnum ((val >> 0) & 0xff);
88 }
89
90 /*
91  * GstDRI2DrawContext
92  */
93
94 /* This function calculates the pixel aspect ratio based on the properties
95  * in the xcontext structure and stores it there.
96  */
97 static void
98 gst_dri2context_calculate_pixel_aspect_ratio (GstDRI2Context * dcontext)
99 {
100   static const gint par[][2] = {
101     {1, 1},                     /* regular screen */
102     {16, 15},                   /* PAL TV */
103     {11, 10},                   /* 525 line Rec.601 video */
104     {54, 59},                   /* 625 line Rec.601 video */
105     {64, 45},                   /* 1280x1024 on 16:9 display */
106     {5, 3},                     /* 1280x1024 on 4:3 display */
107     {4, 3}                      /* 800x600 on 16:9 display */
108   };
109   gint i;
110   gint index;
111   gdouble ratio;
112   gdouble delta;
113
114 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
115
116   /* first calculate the "real" ratio; which is the "physical" w/h divided
117    * by the w/h in pixels of the display
118    *
119    * TODO:
120   ratio = (gdouble) (dcontext->physical_width * dcontext->display_height)
121       / (dcontext->physical_height * dcontext->display_width);
122    */
123
124   /* XXX */
125   ratio = 1;
126
127   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
128   /* now find the one from par[][2] with the lowest delta to the real one */
129   delta = DELTA (0);
130   index = 0;
131
132   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
133     gdouble this_delta = DELTA (i);
134
135     if (this_delta < delta) {
136       index = i;
137       delta = this_delta;
138     }
139   }
140
141   GST_DEBUG ("Decided on index %d (%d/%d)", index,
142       par[index][0], par[index][1]);
143
144   if (dcontext->par) {
145     g_value_unset (dcontext->par);
146     g_free (dcontext->par);
147   }
148   dcontext->par = g_new0 (GValue, 1);
149   g_value_init (dcontext->par, GST_TYPE_FRACTION);
150   gst_value_set_fraction (dcontext->par, par[index][0], par[index][1]);
151   GST_DEBUG ("set dcontext PAR to %d/%d",
152       gst_value_get_fraction_numerator (dcontext->par),
153       gst_value_get_fraction_denominator (dcontext->par));
154 }
155
156 GstDRI2Context *
157 gst_dri2context_new (GstElement * elem)
158 {
159   GstDRI2Context *dcontext;
160   Window root;
161   drm_magic_t magic;
162   int eventBase, errorBase, major, minor;
163   unsigned int i, nformats, *formats = NULL;
164   int fd = -1;
165
166   dcontext = g_new0 (GstDRI2Context, 1);
167   dcontext->elem = elem;
168   dcontext->x_lock = g_mutex_new ();
169
170   dcontext->x_display = XOpenDisplay (NULL);
171   if (!dcontext->x_display) {
172     GST_ERROR_OBJECT (elem, "Failed to open X display");
173     goto fail;
174   }
175
176   if (!DRI2InitDisplay(dcontext->x_display, &ops)) {
177     GST_ERROR_OBJECT (elem, "DRI2InitDisplay failed");
178     goto fail;
179   }
180
181   if (!DRI2QueryExtension (dcontext->x_display, &eventBase, &errorBase)) {
182     GST_ERROR_OBJECT (elem, "DRI2QueryExtension failed");
183     goto fail;
184   }
185
186   GST_DEBUG_OBJECT (elem, "DRI2QueryExtension: "
187       "eventBase=%d, errorBase=%d", eventBase, errorBase);
188
189   if (!DRI2QueryVersion (dcontext->x_display, &major, &minor)) {
190     GST_ERROR_OBJECT (elem, "DRI2QueryVersion failed");
191     goto fail;
192   }
193
194   GST_DEBUG_OBJECT (elem, "DRI2QueryVersion: major=%d, minor=%d",
195       major, minor);
196
197   root = RootWindow (dcontext->x_display,
198       DefaultScreen (dcontext->x_display));
199
200   if (!DRI2Connect (dcontext->x_display, root,
201       DRI2DriverXV, &dcontext->driver, &dcontext->device)) {
202     GST_ERROR_OBJECT (elem, "DRI2Connect failed");
203     goto fail;
204   }
205
206   GST_DEBUG_OBJECT (elem, "DRI2Connect: driver=%s, device=%s",
207       dcontext->driver, dcontext->device);
208
209   fd = open (dcontext->device, O_RDWR);
210   if (fd < 0) {
211     GST_ERROR_OBJECT (elem, "open failed");
212     goto fail;
213   }
214
215   if (drmGetMagic (fd, &magic)) {
216     GST_ERROR_OBJECT (elem, "drmGetMagic failed");
217     goto fail;
218   }
219
220   if (!DRI2Authenticate (dcontext->x_display, root, magic)) {
221     GST_ERROR_OBJECT (elem, "DRI2Authenticate failed");
222     goto fail;
223   }
224
225   dcontext->drm_fd = fd;
226   dcontext->dev = omap_device_new (fd);
227
228   if (!DRI2GetFormats (dcontext->x_display, root, &nformats, &formats)) {
229     GST_ERROR_OBJECT (elem, "DRI2GetFormats failed");
230     goto fail;
231   }
232
233   if (nformats == 0) {
234     GST_ERROR_OBJECT (elem, "no formats!");
235     goto fail;
236   }
237
238   /* print out supported formats */
239   GST_DEBUG_OBJECT (elem, "Found %d supported formats:", nformats);
240   for (i = 0; i < nformats; i++) {
241     if (is_fourcc(formats[i])) {
242       GST_DEBUG_OBJECT (elem, "  %d: %08x (\"%.4s\")", i, formats[i],
243           (char *)&formats[i]);
244     } else {
245       GST_DEBUG_OBJECT (elem, "  %d: %08x (device dependent)", i, formats[i]);
246     }
247   }
248
249   free(formats);
250
251   gst_dri2context_calculate_pixel_aspect_ratio (dcontext);
252
253   dcontext->black = XBlackPixel (dcontext->x_display, dcontext->screen_num);
254
255   return dcontext;
256
257 fail:
258   free(formats);
259   if (dcontext->dev)
260     omap_device_del (dcontext->dev);
261
262   /* TODO: the code in _delete uses drmClose, but the fd is from open(2) ?? */
263   if (fd >= 0)
264     drmClose (fd);
265
266   g_mutex_free (dcontext->x_lock);
267   g_free (dcontext);
268
269   return NULL;
270 }
271
272 void
273 gst_dri2context_delete (GstDRI2Context *dcontext)
274 {
275   if (dcontext->par) {
276     g_value_unset (dcontext->par);
277     g_free (dcontext->par);
278   }
279
280   g_mutex_lock (dcontext->x_lock);
281   XCloseDisplay (dcontext->x_display);
282   g_mutex_unlock (dcontext->x_lock);
283   g_mutex_free (dcontext->x_lock);
284
285   omap_device_del (dcontext->dev);
286   drmClose (dcontext->drm_fd);
287
288   XFree (dcontext->driver);
289   XFree (dcontext->device);
290
291   g_free (dcontext);
292 }
293
294 /*
295  * GstDRI2Window
296  */
297
298 /* NOTES:
299  * at startup (or on first buffer allocation?) request front buffer..
300  * otherwise I think we can do GetBuffers 1 at a time, w/ different
301  * attachment points.. use width==0, height==0 to destroy the buffer
302  * Keep the table of attachment->buffer globally, to handle resolution
303  * changes.. the old bufferpool is torn down, but still goes via the
304  * per video-sink table of attachments, because during the transition
305  * period we could have some not-yet-displayed buffers at the previous
306  * dimensions/format..
307  */
308
309 GstDRI2Window *
310 gst_dri2window_new_from_handle (GstDRI2Context *dcontext, XID xwindow_id)
311 {
312   GstDRI2Window *xwindow;
313   XWindowAttributes attr;
314
315   xwindow = (GstDRI2Window *)gst_mini_object_new (GST_TYPE_DRI2WINDOW);
316   xwindow->dcontext = dcontext;
317   xwindow->window = xwindow_id;
318   xwindow->pool_lock = g_mutex_new ();
319   xwindow->buffer_pool = NULL;
320   xwindow->pool_valid = FALSE;
321
322   /* Set the event we want to receive and create a GC */
323   g_mutex_lock (dcontext->x_lock);
324
325   XGetWindowAttributes (dcontext->x_display, xwindow->window,
326       &attr);
327
328   xwindow->width = attr.width;
329   xwindow->height = attr.height;
330
331   /* We have to do that to prevent X from redrawing the background on
332    * ConfigureNotify. This takes away flickering of video when resizing. */
333   XSetWindowBackgroundPixmap (dcontext->x_display,
334       xwindow->window, None);
335
336   XMapWindow (dcontext->x_display, xwindow->window);
337
338   xwindow->gc = XCreateGC (dcontext->x_display,
339       xwindow->window, 0, NULL);
340   g_mutex_unlock (dcontext->x_lock);
341
342   DRI2CreateDrawable (dcontext->x_display, xwindow->window);
343
344   /* request the front buffer.. we don't need to keep it, just to
345    * request it.. otherwise DRI2 core on xserver side gets miffed:
346    *   [DRI2] swap_buffers: drawable has no back or front?
347    */
348   free (get_buffer (xwindow, DRI2BufferFrontLeft,
349       xwindow->width, xwindow->height, 32));
350
351   return xwindow;
352 }
353
354 GstDRI2Window *
355 gst_dri2window_new (GstDRI2Context * dcontext, gint width, gint height)
356 {
357   GstDRI2Window *xwindow;
358   Window root;
359   Atom wm_delete;
360   XID xwindow_id;
361
362   g_mutex_lock (dcontext->x_lock);
363
364   GST_DEBUG_OBJECT (dcontext->elem, "creating window: %dx%d", width, height);
365
366   root = DefaultRootWindow (dcontext->x_display);
367   xwindow_id = XCreateSimpleWindow (dcontext->x_display, root, 0, 0,
368       width, height, 2, 2, dcontext->black);
369
370
371   /* Tell the window manager we'd like delete client messages instead of
372    * being killed */
373   wm_delete = XInternAtom (dcontext->x_display, "WM_DELETE_WINDOW", True);
374   if (wm_delete != None) {
375     (void) XSetWMProtocols (dcontext->x_display, xwindow_id,
376         &wm_delete, 1);
377   }
378
379   g_mutex_unlock (dcontext->x_lock);
380
381   xwindow = gst_dri2window_new_from_handle (dcontext, xwindow_id);
382   xwindow->internal = TRUE;
383
384   return xwindow;
385 }
386
387 void
388 gst_dri2window_delete (GstDRI2Window * xwindow)
389 {
390   g_mutex_lock (xwindow->pool_lock);
391   xwindow->pool_valid = FALSE;
392   if (xwindow->buffer_pool) {
393     gst_drm_buffer_pool_destroy (xwindow->buffer_pool);
394     xwindow->buffer_pool = NULL;
395   }
396   g_mutex_unlock (xwindow->pool_lock);
397
398   gst_mini_object_unref (GST_MINI_OBJECT (xwindow));
399 }
400
401 static void
402 gst_dri2window_finalize (GstDRI2Window * xwindow)
403 {
404   GstDRI2Context *dcontext = xwindow->dcontext;
405
406   g_mutex_lock (xwindow->pool_lock);
407   xwindow->pool_valid = FALSE;
408   if (xwindow->buffer_pool) {
409     gst_drm_buffer_pool_destroy (xwindow->buffer_pool);
410     xwindow->buffer_pool = NULL;
411   }
412   g_mutex_unlock (xwindow->pool_lock);
413
414   g_mutex_free (xwindow->pool_lock);
415
416   g_mutex_lock (dcontext->x_lock);
417
418   DRI2DestroyDrawable (dcontext->x_display, xwindow->window);
419
420   /* If we did not create the window we just free the GC and let it live */
421   if (xwindow->internal)
422     XDestroyWindow (dcontext->x_display, xwindow->window);
423   else
424     XSelectInput (dcontext->x_display, xwindow->window, 0);
425
426   XFreeGC (dcontext->x_display, xwindow->gc);
427
428   XSync (dcontext->x_display, FALSE);
429
430   // XXX free xwindow->dri2bufs
431   // TODO we probably want xwindow to be a refcnt'd miniobj so we don't end w/
432   // dri2buffer's referencing deleted xwindow's..
433
434   g_mutex_unlock (dcontext->x_lock);
435
436   gst_dri2context_delete (dcontext);
437
438   GST_MINI_OBJECT_CLASS (dri2window_parent_class)->finalize (GST_MINI_OBJECT
439       (xwindow));
440 }
441
442 /* call with x_lock held */
443 void
444 gst_dri2window_update_geometry (GstDRI2Window * xwindow)
445 {
446   XWindowAttributes attr;
447
448   XGetWindowAttributes (xwindow->dcontext->x_display,
449       xwindow->window, &attr);
450
451   xwindow->width  = attr.width;
452   xwindow->height = attr.height;
453 }
454
455 void
456 gst_dri2window_set_pool_valid (GstDRI2Window * xwindow, gboolean valid)
457 {
458   g_mutex_lock (xwindow->pool_lock);
459   xwindow->pool_valid = valid;
460   g_mutex_unlock (xwindow->pool_lock);
461 }
462
463 void
464 gst_dri2window_check_caps (GstDRI2Window * xwindow, GstCaps * caps)
465 {
466   g_mutex_lock (xwindow->pool_lock);
467   if (xwindow->buffer_pool) {
468     if (!gst_drm_buffer_pool_check_caps (xwindow->buffer_pool, caps)) {
469       GST_INFO_OBJECT (xwindow->dcontext->elem, "caps change");
470       gst_drm_buffer_pool_destroy (xwindow->buffer_pool);
471       xwindow->buffer_pool = NULL;
472     }
473   }
474   g_mutex_unlock (xwindow->pool_lock);
475 }
476
477 static inline gboolean
478 ok_buffer (GstDRI2Window * xwindow, GstBuffer * buf)
479 {
480   return GST_IS_DRI2_BUFFER (buf) &&
481       (GST_DRI2_BUFFER_POOL (GST_DRM_BUFFER (buf)->pool)->xwindow == xwindow);
482
483 }
484
485 GstFlowReturn
486 gst_dri2window_buffer_show (GstDRI2Window * xwindow, GstBuffer * buf)
487 {
488   GstDRI2Context *dcontext = xwindow->dcontext;
489   GstDRI2Buffer *dri2buf;
490   GstVideoCrop *crop;
491   CARD64 count;
492   BoxRec b;
493
494   if (! ok_buffer (xwindow, buf)) {
495     GST_WARNING_OBJECT (dcontext->elem, "unexpected buffer: %p", buf);
496     return GST_FLOW_UNEXPECTED;
497   }
498
499   dri2buf = GST_DRI2_BUFFER (buf);
500
501   crop = gst_buffer_get_video_crop (buf);
502   if (crop) {
503     b.x1 = gst_video_crop_left (crop);
504     b.y1 = gst_video_crop_top (crop);
505     b.x2 = b.x1 + gst_video_crop_width (crop) - 1;
506     b.y2 = b.y1 + gst_video_crop_height (crop) - 1;
507   } else {
508     b.x1 = 0;
509     b.y1 = 0;
510     b.x2 = GST_DRM_BUFFER (dri2buf)->pool->width - 1;
511     b.y2 = GST_DRM_BUFFER (dri2buf)->pool->height - 1;
512   }
513
514   g_mutex_lock (dcontext->x_lock);
515   DRI2SwapBuffersVid (dcontext->x_display, xwindow->window, 0, 0, 0,
516       &count, dri2buf->dri2buf->attachment, &b);
517   /* TODO: probably should wait for DRI2_BufferSwapComplete instead..
518    * although that probably depends on someone making an x11 call to
519    * dispatch the events
520    */
521   DRI2WaitSBC (dcontext->x_display, xwindow->window, count,
522       /* just re-use count as a valid ptr.. we don't need ust/msc/sbc: */
523       &count, &count, &count);
524   g_mutex_unlock (dcontext->x_lock);
525
526   return GST_FLOW_OK;
527 }
528
529 GstBuffer *
530 gst_dri2window_buffer_prepare (GstDRI2Window * xwindow, GstBuffer * buf)
531 {
532   GstBuffer *newbuf = NULL;
533
534   if (! ok_buffer (xwindow, buf)) {
535
536     gst_dri2window_buffer_alloc (xwindow, GST_BUFFER_SIZE (buf),
537         GST_BUFFER_CAPS (buf), &newbuf);
538
539     if (newbuf) {
540       GST_DEBUG_OBJECT (xwindow->dcontext->elem,
541           "slow-path.. I got a %s so I need to memcpy",
542           g_type_name (G_OBJECT_TYPE (buf)));
543       memcpy (GST_BUFFER_DATA (newbuf),
544           GST_BUFFER_DATA (buf),
545           MIN (GST_BUFFER_SIZE (newbuf), GST_BUFFER_SIZE (buf)));
546     }
547   }
548
549   return newbuf;
550 }
551
552 GstFlowReturn
553 gst_dri2window_buffer_alloc (GstDRI2Window * xwindow, guint size,
554     GstCaps * caps, GstBuffer ** buf)
555 {
556   GstDRI2Context *dcontext = xwindow->dcontext;
557   GstFlowReturn ret = GST_FLOW_ERROR;
558
559   *buf = NULL;
560
561   g_mutex_lock (xwindow->pool_lock);
562 #if 0
563   /* double check if we need this.. if we do, we probably need to
564    * move pool_valid back to dri2videosink itself, because the
565    * window can be created after the PAUSED->READY state transition
566    */
567   if (G_UNLIKELY (! xwindow->pool_valid)) {
568     GST_DEBUG_OBJECT (dcontext->elem, "the pool is flushing");
569     ret = GST_FLOW_WRONG_STATE;
570     g_mutex_unlock (xwindow->pool_lock);
571     goto beach;
572   }
573 #endif
574
575   /* initialize the buffer pool if not initialized yet */
576   if (G_UNLIKELY (!xwindow->buffer_pool ||
577       gst_drm_buffer_pool_size (xwindow->buffer_pool) != size)) {
578
579     if (xwindow->buffer_pool) {
580       GST_INFO_OBJECT (dcontext->elem, "size change");
581       gst_drm_buffer_pool_destroy (xwindow->buffer_pool);
582     }
583
584     GST_LOG_OBJECT (dcontext->elem, "Creating buffer pool");
585     xwindow->buffer_pool = GST_DRM_BUFFER_POOL (gst_dri2_buffer_pool_new (
586         xwindow, dcontext->drm_fd, caps, size));
587     if (!xwindow->buffer_pool) {
588       goto beach;
589     }
590   }
591
592   *buf = GST_BUFFER (gst_drm_buffer_pool_get (xwindow->buffer_pool, FALSE));
593
594   if (*buf)
595     ret = GST_FLOW_OK;
596
597 beach:
598   g_mutex_unlock (xwindow->pool_lock);
599   return ret;
600 }
601
602 /*
603  * These are used by the bufferpool to allocate buffers.. the bufferpool
604  * needs to go thru the GstDRI2Window, because we need one place to track
605  * which attachment points are in use and which are not to hande cases of
606  * switching between resolutions, where the bufferpool is replaced but
607  * with a transition period of having both buffers of the old and new size
608  * floating around
609  */
610
611 static DRI2Buffer *
612 get_buffer (GstDRI2Window * xwindow, guint attach, gint width, gint height,
613     guint32 format)
614 {
615   GstDRI2Context *dcontext = xwindow->dcontext;
616   int nbufs = 1;
617   unsigned attachments[] = { attach, format };
618   DRI2Buffer *dri2buf;
619   g_mutex_lock (dcontext->x_lock);
620   dri2buf = DRI2GetBuffersVid(dcontext->x_display, xwindow->window,
621       width, height, attachments, nbufs, &nbufs);
622   g_mutex_unlock (dcontext->x_lock);
623   GST_DEBUG_OBJECT (dcontext->elem, "got %d buffer(s)", nbufs);
624   if (nbufs != 1) {
625     free (dri2buf);
626     return NULL;
627   }
628   return dri2buf;
629 }
630
631 DRI2Buffer *
632 gst_dri2window_get_dri2buffer (GstDRI2Window * xwindow, gint width, gint height,
633     guint32 format)
634 {
635   GstDRI2Context *dcontext = xwindow->dcontext;
636   int idx;
637
638   /* find an empty slot, note first slot is the (fake) front buffer,
639    * attached when the GstDRI2Window is constructed:
640    */
641   for (idx = 0; idx < G_N_ELEMENTS (xwindow->dri2bufs); idx++) {
642     if (!xwindow->dri2bufs[idx]) {
643       xwindow->dri2bufs[idx] = get_buffer (xwindow, idx + 1,
644           width, height, format);
645       g_warn_if_fail ((xwindow->dri2bufs[idx]->attachment - 1) == idx);
646       return xwindow->dri2bufs[idx];
647     }
648   }
649
650   GST_ERROR_OBJECT (dcontext->elem, "out of buffer slots");
651
652   return NULL;
653 }
654
655 void
656 gst_dri2window_free_dri2buffer (GstDRI2Window * xwindow, DRI2Buffer * dri2buf)
657 {
658   int idx = dri2buf->attachment - 1;
659   get_buffer (xwindow, dri2buf->attachment, 0, 0, 0);
660   free (xwindow->dri2bufs[idx]);
661   xwindow->dri2bufs[idx] = NULL;
662 }
663
664 static void
665 gst_dri2window_class_init (gpointer g_class, gpointer class_data)
666 {
667   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
668
669   dri2window_parent_class = g_type_class_peek_parent (g_class);
670
671   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
672       GST_DEBUG_FUNCPTR (gst_dri2window_finalize);
673 }
674
675 GType
676 gst_dri2window_get_type (void)
677 {
678   static GType type;
679
680   if (G_UNLIKELY (type == 0)) {
681     static const GTypeInfo info = {
682       .class_size = sizeof (GstMiniObjectClass),
683       .class_init = gst_dri2window_class_init,
684       .instance_size = sizeof (GstDRI2Window),
685     };
686     type = g_type_register_static (GST_TYPE_MINI_OBJECT,
687         "GstDRI2Window", &info, 0);
688   }
689   return type;
690 }