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