2398dd8ef889892fed769c684c399ef328dd7714
[platform/upstream/gst-plugins-base.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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, 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 (g_once_init_enter (&ximage_meta_info)) {
72     const GstMetaInfo *meta =
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     g_once_init_leave (&ximage_meta_info, meta);
78   }
79   return ximage_meta_info;
80 }
81
82 /* X11 stuff */
83 static gboolean error_caught = FALSE;
84
85 static int
86 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
87 {
88   char error_msg[1024];
89
90   XGetErrorText (display, xevent->error_code, error_msg, 1024);
91   GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
92   error_caught = TRUE;
93   return 0;
94 }
95
96 static GstXImageMeta *
97 gst_buffer_add_ximage_meta (GstBuffer * buffer, GstXImageBufferPool * xpool)
98 {
99   GstXImageSink *ximagesink;
100   int (*handler) (Display *, XErrorEvent *);
101   gboolean success = FALSE;
102   GstXContext *xcontext;
103   GstXImageMeta *meta;
104   gint width, height, align = 15, offset;
105   GstXImageBufferPoolPrivate *priv;
106
107   priv = xpool->priv;
108   ximagesink = xpool->sink;
109   xcontext = ximagesink->xcontext;
110
111   width = priv->padded_width;
112   height = priv->padded_height;
113
114   meta =
115       (GstXImageMeta *) gst_buffer_add_meta (buffer, GST_XIMAGE_META_INFO,
116       NULL);
117 #ifdef HAVE_XSHM
118   meta->SHMInfo.shmaddr = ((void *) -1);
119   meta->SHMInfo.shmid = -1;
120 #endif
121   meta->x = priv->align.padding_left;
122   meta->y = priv->align.padding_top;
123   meta->width = GST_VIDEO_INFO_WIDTH (&priv->info);
124   meta->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
125   meta->sink = gst_object_ref (ximagesink);
126
127   GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", buffer,
128       width, height);
129
130   g_mutex_lock (&ximagesink->x_lock);
131
132   /* Setting an error handler to catch failure */
133   error_caught = FALSE;
134   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
135
136 #ifdef HAVE_XSHM
137   if (xcontext->use_xshm) {
138     meta->ximage = XShmCreateImage (xcontext->disp,
139         xcontext->visual,
140         xcontext->depth, ZPixmap, NULL, &meta->SHMInfo, width, height);
141     if (!meta->ximage || error_caught) {
142       g_mutex_unlock (&ximagesink->x_lock);
143
144       /* Reset error flag */
145       error_caught = FALSE;
146
147       /* Push a warning */
148       GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
149           ("Failed to create output image buffer of %dx%d pixels",
150               width, height),
151           ("could not XShmCreateImage a %dx%d image", width, height));
152
153       /* Retry without XShm */
154       ximagesink->xcontext->use_xshm = FALSE;
155
156       /* Hold X mutex again to try without XShm */
157       g_mutex_lock (&ximagesink->x_lock);
158
159       goto no_xshm;
160     }
161
162     /* we have to use the returned bytes_per_line for our shm size */
163     meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
164     GST_LOG_OBJECT (ximagesink,
165         "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
166         meta->size, width, meta->ximage->bytes_per_line);
167
168     /* get shared memory */
169     meta->SHMInfo.shmid =
170         shmget (IPC_PRIVATE, meta->size + align, IPC_CREAT | 0777);
171     if (meta->SHMInfo.shmid == -1)
172       goto shmget_failed;
173
174     /* attach */
175     meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
176     if (meta->SHMInfo.shmaddr == ((void *) -1))
177       goto shmat_failed;
178
179     /* now we can set up the image data */
180     meta->ximage->data = meta->SHMInfo.shmaddr;
181     meta->SHMInfo.readOnly = FALSE;
182
183     if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
184       goto xattach_failed;
185
186     XSync (xcontext->disp, FALSE);
187
188     /* Now that everyone has attached, we can delete the shared memory segment.
189      * This way, it will be deleted as soon as we detach later, and not
190      * leaked if we crash. */
191     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
192
193     GST_DEBUG_OBJECT (ximagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
194         meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
195   } else
196   no_xshm:
197 #endif /* HAVE_XSHM */
198   {
199     guint allocsize;
200
201     meta->ximage = XCreateImage (xcontext->disp,
202         xcontext->visual,
203         xcontext->depth, ZPixmap, 0, NULL, width, height, xcontext->bpp, 0);
204     if (!meta->ximage || error_caught)
205       goto create_failed;
206
207     /* upstream will assume that rowstrides are multiples of 4, but this
208      * doesn't always seem to be the case with XCreateImage() */
209     if ((meta->ximage->bytes_per_line % 4) != 0) {
210       GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
211           "usually assumed");
212     }
213
214     /* we have to use the returned bytes_per_line for our image size */
215     meta->size = meta->ximage->bytes_per_line * meta->ximage->height;
216
217     /* alloc a bit more for unexpected strides to avoid crashes upstream.
218      * FIXME: if we get an unrounded stride, the image will be displayed
219      * distorted, since all upstream elements assume a rounded stride */
220     allocsize =
221         GST_ROUND_UP_4 (meta->ximage->bytes_per_line) * meta->ximage->height;
222
223     meta->ximage->data = g_malloc (allocsize + align);
224     GST_LOG_OBJECT (ximagesink,
225         "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
226         "stride %d", meta->size, allocsize, width,
227         meta->ximage->bytes_per_line);
228
229     XSync (xcontext->disp, FALSE);
230   }
231
232   if ((offset = ((guintptr) meta->ximage->data & align)))
233     offset = (align + 1) - offset;
234
235   GST_DEBUG_OBJECT (ximagesink, "memory %p, align %d, offset %d",
236       meta->ximage->data, align, offset);
237
238   /* Reset error handler */
239   error_caught = FALSE;
240   XSetErrorHandler (handler);
241
242   gst_buffer_append_memory (buffer,
243       gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->ximage->data,
244           meta->size + align, offset, meta->size, NULL, NULL));
245
246   g_mutex_unlock (&ximagesink->x_lock);
247
248   success = TRUE;
249
250 beach:
251   if (!success)
252     meta = NULL;
253
254   return meta;
255
256   /* ERRORS */
257 create_failed:
258   {
259     g_mutex_unlock (&ximagesink->x_lock);
260     /* Reset error handler */
261     error_caught = FALSE;
262     XSetErrorHandler (handler);
263     /* Push an error */
264     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
265         ("Failed to create output image buffer of %dx%d pixels",
266             width, height),
267         ("could not XShmCreateImage a %dx%d image", width, height));
268     goto beach;
269   }
270 #ifdef HAVE_XSHM
271 shmget_failed:
272   {
273     g_mutex_unlock (&ximagesink->x_lock);
274     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
275         ("Failed to create output image buffer of %dx%d pixels",
276             width, height),
277         ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
278             meta->size));
279     goto beach;
280   }
281 shmat_failed:
282   {
283     g_mutex_unlock (&ximagesink->x_lock);
284     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
285         ("Failed to create output image buffer of %dx%d pixels",
286             width, height), ("Failed to shmat: %s", g_strerror (errno)));
287     /* Clean up the shared memory segment */
288     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
289     goto beach;
290   }
291 xattach_failed:
292   {
293     /* Clean up the shared memory segment */
294     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
295     g_mutex_unlock (&ximagesink->x_lock);
296
297     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
298         ("Failed to create output image buffer of %dx%d pixels",
299             width, height), ("Failed to XShmAttach"));
300     goto beach;
301   }
302 #endif
303 }
304
305 static void
306 gst_ximage_meta_free (GstXImageMeta * meta, GstBuffer * buffer)
307 {
308   GstXImageSink *ximagesink;
309
310   ximagesink = meta->sink;
311
312   GST_DEBUG_OBJECT (ximagesink, "free meta on buffer %p", buffer);
313
314   /* Hold the object lock to ensure the XContext doesn't disappear */
315   GST_OBJECT_LOCK (ximagesink);
316   /* We might have some buffers destroyed after changing state to NULL */
317   if (ximagesink->xcontext == NULL) {
318     GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
319 #ifdef HAVE_XSHM
320     /* Need to free the shared memory segment even if the x context
321      * was already cleaned up */
322     if (meta->SHMInfo.shmaddr != ((void *) -1)) {
323       shmdt (meta->SHMInfo.shmaddr);
324     }
325 #endif
326     goto beach;
327   }
328
329   g_mutex_lock (&ximagesink->x_lock);
330
331 #ifdef HAVE_XSHM
332   if (ximagesink->xcontext->use_xshm) {
333     if (meta->SHMInfo.shmaddr != ((void *) -1)) {
334       GST_DEBUG_OBJECT (ximagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
335           meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
336       XShmDetach (ximagesink->xcontext->disp, &meta->SHMInfo);
337       XSync (ximagesink->xcontext->disp, FALSE);
338       shmdt (meta->SHMInfo.shmaddr);
339       meta->SHMInfo.shmaddr = (void *) -1;
340     }
341     if (meta->ximage)
342       XDestroyImage (meta->ximage);
343   } else
344 #endif /* HAVE_XSHM */
345   {
346     if (meta->ximage) {
347       XDestroyImage (meta->ximage);
348     }
349   }
350
351   XSync (ximagesink->xcontext->disp, FALSE);
352
353   g_mutex_unlock (&ximagesink->x_lock);
354
355 beach:
356   GST_OBJECT_UNLOCK (ximagesink);
357
358   gst_object_unref (meta->sink);
359 }
360
361 #ifdef HAVE_XSHM
362 /* This function checks that it is actually really possible to create an image
363    using XShm */
364 gboolean
365 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
366     GstXContext * xcontext)
367 {
368   XImage *ximage;
369   XShmSegmentInfo SHMInfo;
370   size_t size;
371   int (*handler) (Display *, XErrorEvent *);
372   gboolean result = FALSE;
373   gboolean did_attach = FALSE;
374
375   g_return_val_if_fail (xcontext != NULL, FALSE);
376
377   /* Sync to ensure any older errors are already processed */
378   XSync (xcontext->disp, FALSE);
379
380   /* Set defaults so we don't free these later unnecessarily */
381   SHMInfo.shmaddr = ((void *) -1);
382   SHMInfo.shmid = -1;
383
384   /* Setting an error handler to catch failure */
385   error_caught = FALSE;
386   handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
387
388   /* Trying to create a 1x1 ximage */
389   GST_DEBUG ("XShmCreateImage of 1x1");
390
391   ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
392       xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
393
394   /* Might cause an error, sync to ensure it is noticed */
395   XSync (xcontext->disp, FALSE);
396   if (!ximage || error_caught) {
397     GST_WARNING ("could not XShmCreateImage a 1x1 image");
398     goto beach;
399   }
400   size = ximage->height * ximage->bytes_per_line;
401
402   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
403   if (SHMInfo.shmid == -1) {
404     GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
405         size);
406     goto beach;
407   }
408
409   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
410   if (SHMInfo.shmaddr == ((void *) -1)) {
411     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
412     /* Clean up the shared memory segment */
413     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
414     goto beach;
415   }
416
417   ximage->data = SHMInfo.shmaddr;
418   SHMInfo.readOnly = FALSE;
419
420   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
421     GST_WARNING ("Failed to XShmAttach");
422     /* Clean up the shared memory segment */
423     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
424     goto beach;
425   }
426
427   /* Sync to ensure we see any errors we caused */
428   XSync (xcontext->disp, FALSE);
429
430   /* Delete the shared memory segment as soon as everyone is attached.
431    * This way, it will be deleted as soon as we detach later, and not
432    * leaked if we crash. */
433   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
434
435   if (!error_caught) {
436     GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
437         SHMInfo.shmseg);
438
439     did_attach = TRUE;
440     /* store whether we succeeded in result */
441     result = TRUE;
442   } else {
443     GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
444         "Not using shared memory.");
445   }
446
447 beach:
448   /* Sync to ensure we swallow any errors we caused and reset error_caught */
449   XSync (xcontext->disp, FALSE);
450
451   error_caught = FALSE;
452   XSetErrorHandler (handler);
453
454   if (did_attach) {
455     GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
456         SHMInfo.shmid, SHMInfo.shmseg);
457     XShmDetach (xcontext->disp, &SHMInfo);
458     XSync (xcontext->disp, FALSE);
459   }
460   if (SHMInfo.shmaddr != ((void *) -1))
461     shmdt (SHMInfo.shmaddr);
462   if (ximage)
463     XDestroyImage (ximage);
464   return result;
465 }
466 #endif /* HAVE_XSHM */
467
468 /* bufferpool */
469 static void gst_ximage_buffer_pool_finalize (GObject * object);
470
471 #define GST_XIMAGE_BUFFER_POOL_GET_PRIVATE(obj)  \
472    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XIMAGE_BUFFER_POOL, GstXImageBufferPoolPrivate))
473
474 #define gst_ximage_buffer_pool_parent_class parent_class
475 G_DEFINE_TYPE (GstXImageBufferPool, gst_ximage_buffer_pool,
476     GST_TYPE_BUFFER_POOL);
477
478 static const gchar **
479 ximage_buffer_pool_get_options (GstBufferPool * pool)
480 {
481   static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
482     GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
483   };
484
485   return options;
486 }
487
488 static gboolean
489 ximage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
490 {
491   GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
492   GstXImageBufferPoolPrivate *priv = xpool->priv;
493   GstVideoInfo info;
494   GstCaps *caps;
495
496   if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
497     goto wrong_config;
498
499   if (caps == NULL)
500     goto no_caps;
501
502   /* now parse the caps from the config */
503   if (!gst_video_info_from_caps (&info, caps))
504     goto wrong_caps;
505
506   GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
507       caps);
508
509   /* keep track of the width and height and caps */
510   if (priv->caps)
511     gst_caps_unref (priv->caps);
512   priv->caps = gst_caps_ref (caps);
513
514   /* check for the configured metadata */
515   priv->add_metavideo =
516       gst_buffer_pool_config_has_option (config,
517       GST_BUFFER_POOL_OPTION_VIDEO_META);
518
519   /* parse extra alignment info */
520   priv->need_alignment = gst_buffer_pool_config_has_option (config,
521       GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
522
523   if (priv->need_alignment) {
524     gst_buffer_pool_config_get_video_alignment (config, &priv->align);
525
526     GST_LOG_OBJECT (pool, "padding %u-%ux%u-%u", priv->align.padding_top,
527         priv->align.padding_left, priv->align.padding_left,
528         priv->align.padding_bottom);
529
530     /* do padding and alignment */
531     gst_video_info_align (&info, &priv->align);
532
533     /* we need the video metadata too now */
534     priv->add_metavideo = TRUE;
535   } else {
536     gst_video_alignment_reset (&priv->align);
537   }
538
539   /* add the padding */
540   priv->padded_width =
541       GST_VIDEO_INFO_WIDTH (&info) + priv->align.padding_left +
542       priv->align.padding_right;
543   priv->padded_height =
544       GST_VIDEO_INFO_HEIGHT (&info) + priv->align.padding_top +
545       priv->align.padding_bottom;
546
547   priv->info = info;
548
549   return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
550
551   /* ERRORS */
552 wrong_config:
553   {
554     GST_WARNING_OBJECT (pool, "invalid config");
555     return FALSE;
556   }
557 no_caps:
558   {
559     GST_WARNING_OBJECT (pool, "no caps in config");
560     return FALSE;
561   }
562 wrong_caps:
563   {
564     GST_WARNING_OBJECT (pool,
565         "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
566     return FALSE;
567   }
568 }
569
570 /* This function handles GstXImageBuffer creation depending on XShm availability */
571 static GstFlowReturn
572 ximage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
573     GstBufferPoolAcquireParams * params)
574 {
575   GstXImageBufferPool *xpool = GST_XIMAGE_BUFFER_POOL_CAST (pool);
576   GstXImageBufferPoolPrivate *priv = xpool->priv;
577   GstVideoInfo *info;
578   GstBuffer *ximage;
579   GstXImageMeta *meta;
580
581   info = &priv->info;
582
583   ximage = gst_buffer_new ();
584   meta = gst_buffer_add_ximage_meta (ximage, xpool);
585   if (meta == NULL) {
586     gst_buffer_unref (ximage);
587     goto no_buffer;
588   }
589   if (priv->add_metavideo) {
590     GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");
591     /* these are just the defaults for now */
592     gst_buffer_add_video_meta_full (ximage, GST_VIDEO_FRAME_FLAG_NONE,
593         GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
594         GST_VIDEO_INFO_HEIGHT (info), GST_VIDEO_INFO_N_PLANES (info),
595         info->offset, info->stride);
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 }