Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / sys / ximage / ximagepool.c
1 /* GStreamer
2  * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 /* Object header */
25 #include "ximagesink.h"
26
27 /* Debugging category */
28 #include <gst/gstinfo.h>
29
30 /* Helper functions */
31 #include <gst/video/video.h>
32 #include <gst/video/gstvideometa.h>
33 #include <gst/video/gstvideopool.h>
34
35 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagepool);
36 #define GST_CAT_DEFAULT gst_debug_ximagepool
37
38 struct _GstXImageBufferPoolPrivate
39 {
40   GstCaps *caps;
41   GstVideoInfo info;
42   GstVideoAlignment align;
43   guint padded_width;
44   guint padded_height;
45   gboolean add_metavideo;
46   gboolean need_alignment;
47 };
48
49 static void gst_ximage_meta_free (GstXImageMeta * meta, GstBuffer * buffer);
50
51 /* ximage metadata */
52 GType
53 gst_ximage_meta_api_get_type (void)
54 {
55   static volatile GType type;
56   static const gchar *tags[] =
57       { "memory", "size", "colorspace", "orientation", NULL };
58
59   if (g_once_init_enter (&type)) {
60     GType _type = gst_meta_api_type_register ("GstXImageMetaAPI", tags);
61     g_once_init_leave (&type, _type);
62   }
63   return type;
64 }
65
66 const GstMetaInfo *
67 gst_ximage_meta_get_info (void)
68 {
69   static const GstMetaInfo *ximage_meta_info = NULL;
70
71   if (ximage_meta_info == NULL) {
72     ximage_meta_info =
73         gst_meta_register (GST_XIMAGE_META_API_TYPE, "GstXImageMeta",
74         sizeof (GstXImageMeta), (GstMetaInitFunction) NULL,
75         (GstMetaFreeFunction) gst_ximage_meta_free,
76         (GstMetaTransformFunction) NULL);
77   }
78   return ximage_meta_info;
79 }
80
81 /* X11 stuff */
82 static gboolean error_caught = FALSE;
83
84 static int
85 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
86 {
87   char error_msg[1024];
88
89   XGetErrorText (display, xevent->error_code, error_msg, 1024);
90   GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
91   error_caught = TRUE;
92   return 0;
93 }
94
95 static GstXImageMeta *
96 gst_buffer_add_ximage_meta (GstBuffer * buffer, GstXImageBufferPool * xpool)
97 {
98   GstXImageSink *ximagesink;
99   int (*handler) (Display *, XErrorEvent *);
100   gboolean success = FALSE;
101   GstXContext *xcontext;
102   GstXImageMeta *meta;
103   gint width, height;
104   GstXImageBufferPoolPrivate *priv;
105
106   priv = xpool->priv;
107   ximagesink = xpool->sink;
108   xcontext = ximagesink->xcontext;
109
110   width = priv->padded_width;
111   height = priv->padded_height;
112
113   meta =
114       (GstXImageMeta *) gst_buffer_add_meta (buffer, GST_XIMAGE_META_INFO,
115       NULL);
116 #ifdef HAVE_XSHM
117   meta->SHMInfo.shmaddr = ((void *) -1);
118   meta->SHMInfo.shmid = -1;
119 #endif
120   meta->x = priv->align.padding_left;
121   meta->y = priv->align.padding_top;
122   meta->width = GST_VIDEO_INFO_WIDTH (&priv->info);
123   meta->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
124   meta->sink = gst_object_ref (ximagesink);
125
126   GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", buffer,
127       width, height);
128
129   g_mutex_lock (ximagesink->x_lock);
130
131   /* Setting an error handler to catch failure */
132   error_caught = FALSE;
133   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
134
135 #ifdef HAVE_XSHM
136   if (xcontext->use_xshm) {
137     meta->ximage = XShmCreateImage (xcontext->disp,
138         xcontext->visual,
139         xcontext->depth, ZPixmap, NULL, &meta->SHMInfo, width, height);
140     if (!meta->ximage || error_caught) {
141       g_mutex_unlock (ximagesink->x_lock);
142
143       /* Reset error flag */
144       error_caught = FALSE;
145
146       /* Push a warning */
147       GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
148           ("Failed to create output image buffer of %dx%d pixels",
149               width, height),
150           ("could not XShmCreateImage a %dx%d image", width, height));
151
152       /* Retry without XShm */
153       ximagesink->xcontext->use_xshm = FALSE;
154
155       /* Hold X mutex again to try without XShm */
156       g_mutex_lock (ximagesink->x_lock);
157
158       goto no_xshm;
159     }
160
161     /* we have to use the returned bytes_per_line for our shm size */
162     meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
163     GST_LOG_OBJECT (ximagesink,
164         "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
165         meta->size, width, meta->ximage->bytes_per_line);
166
167     /* get shared memory */
168     meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
169     if (meta->SHMInfo.shmid == -1)
170       goto shmget_failed;
171
172     /* attach */
173     meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
174     if (meta->SHMInfo.shmaddr == ((void *) -1))
175       goto shmat_failed;
176
177     /* now we can set up the image data */
178     meta->ximage->data = meta->SHMInfo.shmaddr;
179     meta->SHMInfo.readOnly = FALSE;
180
181     if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
182       goto xattach_failed;
183
184     XSync (xcontext->disp, FALSE);
185
186     /* Now that everyone has attached, we can delete the shared memory segment.
187      * This way, it will be deleted as soon as we detach later, and not
188      * leaked if we crash. */
189     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
190
191     GST_DEBUG_OBJECT (ximagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
192         meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
193   } else
194   no_xshm:
195 #endif /* HAVE_XSHM */
196   {
197     guint allocsize;
198
199     meta->ximage = XCreateImage (xcontext->disp,
200         xcontext->visual,
201         xcontext->depth, ZPixmap, 0, NULL, width, height, xcontext->bpp, 0);
202     if (!meta->ximage || error_caught)
203       goto create_failed;
204
205     /* upstream will assume that rowstrides are multiples of 4, but this
206      * doesn't always seem to be the case with XCreateImage() */
207     if ((meta->ximage->bytes_per_line % 4) != 0) {
208       GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
209           "usually assumed");
210     }
211
212     /* we have to use the returned bytes_per_line for our image size */
213     meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
214
215     /* alloc a bit more for unexpected strides to avoid crashes upstream.
216      * FIXME: if we get an unrounded stride, the image will be displayed
217      * distorted, since all upstream elements assume a rounded stride */
218     allocsize =
219         GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
220
221     meta->ximage->data = g_malloc (allocsize);
222     GST_LOG_OBJECT (ximagesink,
223         "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
224         "stride %d", meta->size, allocsize, width,
225         meta->ximage->bytes_per_line);
226
227     XSync (xcontext->disp, FALSE);
228   }
229
230   /* Reset error handler */
231   error_caught = FALSE;
232   XSetErrorHandler (handler);
233
234   gst_buffer_take_memory (buffer, -1,
235       gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
236           meta->size, 0, meta->size, NULL, NULL));
237
238   g_mutex_unlock (ximagesink->x_lock);
239
240   success = TRUE;
241
242 beach:
243   if (!success)
244     meta = NULL;
245
246   return meta;
247
248   /* ERRORS */
249 create_failed:
250   {
251     g_mutex_unlock (ximagesink->x_lock);
252     /* Reset error handler */
253     error_caught = FALSE;
254     XSetErrorHandler (handler);
255     /* Push an error */
256     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
257         ("Failed to create output image buffer of %dx%d pixels",
258             width, height),
259         ("could not XShmCreateImage a %dx%d image", width, height));
260     goto beach;
261   }
262 shmget_failed:
263   {
264     g_mutex_unlock (ximagesink->x_lock);
265     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
266         ("Failed to create output image buffer of %dx%d pixels",
267             width, height),
268         ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
269             meta->size));
270     goto beach;
271   }
272 shmat_failed:
273   {
274     g_mutex_unlock (ximagesink->x_lock);
275     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
276         ("Failed to create output image buffer of %dx%d pixels",
277             width, height), ("Failed to shmat: %s", g_strerror (errno)));
278     /* Clean up the shared memory segment */
279     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
280     goto beach;
281   }
282 xattach_failed:
283   {
284     /* Clean up the shared memory segment */
285     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
286     g_mutex_unlock (ximagesink->x_lock);
287
288     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
289         ("Failed to create output image buffer of %dx%d pixels",
290             width, height), ("Failed to XShmAttach"));
291     goto beach;
292   }
293 }
294
295 static void
296 gst_ximage_meta_free (GstXImageMeta * meta, GstBuffer * buffer)
297 {
298   GstXImageSink *ximagesink;
299
300   ximagesink = meta->sink;
301
302   GST_DEBUG_OBJECT (ximagesink, "free meta on buffer %p", buffer);
303
304   /* Hold the object lock to ensure the XContext doesn't disappear */
305   GST_OBJECT_LOCK (ximagesink);
306   /* We might have some buffers destroyed after changing state to NULL */
307   if (ximagesink->xcontext == NULL) {
308     GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
309 #ifdef HAVE_XSHM
310     /* Need to free the shared memory segment even if the x context
311      * was already cleaned up */
312     if (meta->SHMInfo.shmaddr != ((void *) -1)) {
313       shmdt (meta->SHMInfo.shmaddr);
314     }
315 #endif
316     goto beach;
317   }
318
319   g_mutex_lock (ximagesink->x_lock);
320
321 #ifdef HAVE_XSHM
322   if (ximagesink->xcontext->use_xshm) {
323     if (meta->SHMInfo.shmaddr != ((void *) -1)) {
324       GST_DEBUG_OBJECT (ximagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
325           meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
326       XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
327       XSync (ximagesink->xcontext->disp, FALSE);
328       shmdt (meta->SHMInfo.shmaddr);
329       meta->SHMInfo.shmaddr = (void *) -1;
330     }
331     if (meta->ximage)
332       XDestroyImage (meta->ximage);
333   } else
334 #endif /* HAVE_XSHM */
335   {
336     if (meta->ximage) {
337       XDestroyImage (meta->ximage);
338     }
339   }
340
341   XSync (ximagesink->xcontext->disp, FALSE);
342
343   g_mutex_unlock (ximagesink->x_lock);
344
345 beach:
346   GST_OBJECT_UNLOCK (ximagesink);
347
348   gst_object_unref (meta->sink);
349 }
350
351 #ifdef HAVE_XSHM
352 /* This function checks that it is actually really possible to create an image
353    using XShm */
354 gboolean
355 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
356     GstXContext * xcontext)
357 {
358   XImage *ximage;
359   XShmSegmentInfo SHMInfo;
360   size_t size;
361   int (*handler) (Display *, XErrorEvent *);
362   gboolean result = FALSE;
363   gboolean did_attach = FALSE;
364
365   g_return_val_if_fail (xcontext != NULL, FALSE);
366
367   /* Sync to ensure any older errors are already processed */
368   XSync (xcontext->disp, FALSE);
369
370   /* Set defaults so we don't free these later unnecessarily */
371   SHMInfo.shmaddr = ((void *) -1);
372   SHMInfo.shmid = -1;
373
374   /* Setting an error handler to catch failure */
375   error_caught = FALSE;
376   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
377
378   /* Trying to create a 1x1 ximage */
379   GST_DEBUG ("XShmCreateImage of 1x1");
380
381   ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
382       xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
383
384   /* Might cause an error, sync to ensure it is noticed */
385   XSync (xcontext->disp, FALSE);
386   if (!ximage || error_caught) {
387     GST_WARNING ("could not XShmCreateImage a 1x1 image");
388     goto beach;
389   }
390   size = ximage->height * ximage->bytes_per_line;
391
392   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
393   if (SHMInfo.shmid == -1) {
394     GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
395         size);
396     goto beach;
397   }
398
399   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
400   if (SHMInfo.shmaddr == ((void *) -1)) {
401     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
402     /* Clean up the shared memory segment */
403     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
404     goto beach;
405   }
406
407   ximage->data = SHMInfo.shmaddr;
408   SHMInfo.readOnly = FALSE;
409
410   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
411     GST_WARNING ("Failed to XShmAttach");
412     /* Clean up the shared memory segment */
413     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
414     goto beach;
415   }
416
417   /* Sync to ensure we see any errors we caused */
418   XSync (xcontext->disp, FALSE);
419
420   /* Delete the shared memory segment as soon as everyone is attached.
421    * This way, it will be deleted as soon as we detach later, and not
422    * leaked if we crash. */
423   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
424
425   if (!error_caught) {
426     GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
427         SHMInfo.shmseg);
428
429     did_attach = TRUE;
430     /* store whether we succeeded in result */
431     result = TRUE;
432   } else {
433     GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
434         "Not using shared memory.");
435   }
436
437 beach:
438   /* Sync to ensure we swallow any errors we caused and reset error_caught */
439   XSync (xcontext->disp, FALSE);
440
441   error_caught = FALSE;
442   XSetErrorHandler (handler);
443
444   if (did_attach) {
445     GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
446         SHMInfo.shmid, SHMInfo.shmseg);
447     XShmDetach (xcontext->disp, &SHMInfo);
448     XSync (xcontext->disp, FALSE);
449   }
450   if (SHMInfo.shmaddr != ((void *) -1))
451     shmdt (SHMInfo.shmaddr);
452   if (ximage)
453     XDestroyImage (ximage);
454   return result;
455 }
456 #endif /* HAVE_XSHM */
457
458 /* bufferpool */
459 static void gst_ximage_buffer_pool_finalize (GObject * object);
460
461 #define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj)  \
462    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
463
464 #define gst_ximage_buffer_pool_parent_class parent_class
465 G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
466     GST_TYPE_BUFFER_POOL);
467
468 static const gchar **
469 ximage_buffer_pool_get_options (GstBufferPool * pool)
470 {
471   static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
472     GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
473   };
474
475   return options;
476 }
477
478 static gboolean
479 ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
480 {
481   GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
482   GstXImageBufferPoolPrivate *priv = xpool->priv;
483   GstVideoInfo info;
484   const GstCaps *caps;
485
486   if (!gst_buffer_pool_config_get (config, &caps, NULL, NULL, NULL, NULL, NULL))
487     goto wrong_config;
488
489   if (caps == NULL)
490     goto no_caps;
491
492   /* now parse the caps from the config */
493   if (!gst_video_info_from_caps (&info, caps))
494     goto wrong_caps;
495
496   priv->info = info;
497
498   GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
499       caps);
500
501   /* keep track of the width and height and caps */
502   if (priv->caps)
503     gst_caps_unref (priv->caps);
504   priv->caps = gst_caps_copy (caps);
505
506   /* check for the configured metadata */
507   priv->add_metavideo =
508       gst_buffer_pool_config_has_option (config,
509       GST_BUFFER_POOL_OPTION_VIDEO_META);
510
511   /* parse extra alignment info */
512   priv->need_alignment = gst_buffer_pool_config_has_option (config,
513       GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
514
515   if (priv->need_alignment) {
516     gst_buffer_pool_config_get_video_alignment (config, &priv->align);
517
518     GST_LOG_OBJECT (pool, "padding %u-%ux%u-%u", priv->align.padding_top,
519         priv->align.padding_left, priv->align.padding_left,
520         priv->align.padding_bottom);
521
522     /* we need the video metadata too now */
523     priv->add_metavideo = TRUE;
524   } else {
525     gst_video_alignment_reset (&priv->align);
526   }
527
528   /* add the padding */
529   priv->padded_width =
530       GST_VIDEO_INFO_WIDTH (&info) + priv->align.padding_left +
531       priv->align.padding_right;
532   priv->padded_height =
533       GST_VIDEO_INFO_HEIGHT (&info) + priv->align.padding_top +
534       priv->align.padding_bottom;
535
536   return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
537
538   /* ERRORS */
539 wrong_config:
540   {
541     GST_WARNING_OBJECT (pool, "invalid config");
542     return FALSE;
543   }
544 no_caps:
545   {
546     GST_WARNING_OBJECT (pool, "no caps in config");
547     return FALSE;
548   }
549 wrong_caps:
550   {
551     GST_WARNING_OBJECT (pool,
552         "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
553     return FALSE;
554   }
555 }
556
557 /* This function handles GstXImageBuffer creation depending on XShm availability */
558 static GstFlowReturn
559 ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
560     GstBufferPoolParams * params)
561 {
562   GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
563   GstXImageBufferPoolPrivate *priv = xpool->priv;
564   GstVideoInfo *info;
565   GstBuffer *ximage;
566   GstXImageMeta *meta;
567
568   info = &priv->info;
569
570   ximage = gst_buffer_new ();
571   meta = gst_buffer_add_ximage_meta (ximage, xpool);
572   if (meta == NULL) {
573     gst_buffer_unref (ximage);
574     goto no_buffer;
575   }
576   if (priv->add_metavideo) {
577     GstVideoMeta *meta;
578
579     GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");
580     /* these are just the defaults for now */
581     meta = gst_buffer_add_video_meta (ximage, 0, GST_VIDEO_INFO_FORMAT (info),
582         priv->padded_width, priv->padded_height);
583
584     if (priv->need_alignment) {
585       gint vpad, hpad, pstride;
586
587       vpad = priv->align.padding_left;
588       hpad = priv->align.padding_top;
589
590       meta->width = GST_VIDEO_INFO_WIDTH (&priv->info);
591       meta->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
592       pstride = GST_VIDEO_INFO_COMP_PSTRIDE (&priv->info, 0);
593
594       meta->offset[0] += (vpad * meta->stride[0]) + (hpad * pstride);
595     }
596   }
597   *buffer = ximage;
598
599   return GST_FLOW_OK;
600
601   /* ERROR */
602 no_buffer:
603   {
604     GST_WARNING_OBJECT (pool, "can't create image");
605     return GST_FLOW_ERROR;
606   }
607 }
608
609 GstBufferPool *
610 gst_ximage_buffer_pool_new (GstXImageSink * ximagesink)
611 {
612   GstXImageBufferPool *pool;
613
614   g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
615
616   pool = g_object_new (GST_TYPE_XIMAGE_BUFFER_POOL, NULL);
617   pool->sink = gst_object_ref (ximagesink);
618
619   GST_LOG_OBJECT (pool, "new XImage buffer pool %p", pool);
620
621   return GST_BUFFER_POOL_CAST (pool);
622 }
623
624 static void
625 gst_ximage_buffer_pool_class_init (GstXImageBufferPoolClass * klass)
626 {
627   GObjectClass *gobject_class = (GObjectClass *) klass;
628   GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
629
630   g_type_class_add_private (klass, sizeof (GstXImageBufferPoolPrivate));
631
632   gobject_class->finalize = gst_ximage_buffer_pool_finalize;
633
634   gstbufferpool_class->get_options = ximage_buffer_pool_get_options;
635   gstbufferpool_class->set_config = ximage_buffer_pool_set_config;
636   gstbufferpool_class->alloc_buffer = ximage_buffer_pool_alloc;
637 }
638
639 static void
640 gst_ximage_buffer_pool_init (GstXImageBufferPool * pool)
641 {
642   pool->priv = GST_XIMAGE_BUFFER_POOL_GET_PRIVATE (pool);
643 }
644
645 static void
646 gst_ximage_buffer_pool_finalize (GObject * object)
647 {
648   GstXImageBufferPool *pool = GST_XIMAGE_BUFFER_POOL_CAST (object);
649   GstXImageBufferPoolPrivate *priv = pool->priv;
650
651   GST_LOG_OBJECT (pool, "finalize XImage buffer pool %p", pool);
652
653   if (priv->caps)
654     gst_caps_unref (priv->caps);
655   gst_object_unref (pool->sink);
656
657   G_OBJECT_CLASS (gst_ximage_buffer_pool_parent_class)->finalize (object);
658 }