4ffdbd8b0dc11f76923de34edab33592da45f54e
[platform/upstream/gstreamer.git] / sys / xvimage / xvimagepool.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 "xvimagesink.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
36 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagepool);
37 #define GST_CAT_DEFAULT gst_debug_xvimagepool
38
39
40 struct _GstXvImageBufferPoolPrivate
41 {
42   GstCaps *caps;
43   gint im_format;
44   GstVideoInfo info;
45   GstVideoAlignment align;
46   guint padded_width;
47   guint padded_height;
48   gboolean add_metavideo;
49   gboolean need_alignment;
50 };
51
52 static void gst_xvimage_meta_free (GstXvImageMeta * meta, GstBuffer * buffer);
53
54 /* xvimage metadata */
55 GType
56 gst_xvimage_meta_api_get_type (void)
57 {
58   static volatile GType type;
59   static const gchar *tags[] =
60       { "memory", "size", "colorspace", "orientation", NULL };
61
62   if (g_once_init_enter (&type)) {
63     GType _type = gst_meta_api_type_register ("GstXvImageMetaAPI", tags);
64     g_once_init_leave (&type, _type);
65   }
66   return type;
67 }
68
69 const GstMetaInfo *
70 gst_xvimage_meta_get_info (void)
71 {
72   static const GstMetaInfo *xvimage_meta_info = NULL;
73
74   if (xvimage_meta_info == NULL) {
75     xvimage_meta_info =
76         gst_meta_register (GST_XVIMAGE_META_API_TYPE, "GstXvImageMeta",
77         sizeof (GstXvImageMeta), (GstMetaInitFunction) NULL,
78         (GstMetaFreeFunction) gst_xvimage_meta_free,
79         (GstMetaTransformFunction) NULL);
80   }
81   return xvimage_meta_info;
82 }
83
84 /* X11 stuff */
85 static gboolean error_caught = FALSE;
86
87 static int
88 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
89 {
90   char error_msg[1024];
91
92   XGetErrorText (display, xevent->error_code, error_msg, 1024);
93   GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
94   error_caught = TRUE;
95   return 0;
96 }
97
98 static GstXvImageMeta *
99 gst_buffer_add_xvimage_meta (GstBuffer * buffer, GstXvImageBufferPool * xvpool)
100 {
101   GstXvImageSink *xvimagesink;
102   int (*handler) (Display *, XErrorEvent *);
103   gboolean success = FALSE;
104   GstXContext *xcontext;
105   GstXvImageMeta *meta;
106   gint width, height, im_format;
107   GstXvImageBufferPoolPrivate *priv;
108
109   priv = xvpool->priv;
110   xvimagesink = xvpool->sink;
111   xcontext = xvimagesink->xcontext;
112
113   width = priv->padded_width;
114   height = priv->padded_height;
115   im_format = priv->im_format;
116
117   meta =
118       (GstXvImageMeta *) gst_buffer_add_meta (buffer, GST_XVIMAGE_META_INFO,
119       NULL);
120 #ifdef HAVE_XSHM
121   meta->SHMInfo.shmaddr = ((void *) -1);
122   meta->SHMInfo.shmid = -1;
123 #endif
124   meta->x = priv->align.padding_left;
125   meta->y = priv->align.padding_top;
126   meta->width = priv->info.width;
127   meta->height = priv->info.height;
128   meta->sink = gst_object_ref (xvimagesink);
129   meta->im_format = im_format;
130
131   GST_DEBUG_OBJECT (xvimagesink, "creating image %p (%dx%d)", buffer,
132       width, height);
133
134   g_mutex_lock (xvimagesink->x_lock);
135
136   /* Setting an error handler to catch failure */
137   error_caught = FALSE;
138   handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
139
140 #ifdef HAVE_XSHM
141   if (xcontext->use_xshm) {
142     int expected_size;
143
144     meta->xvimage = XvShmCreateImage (xcontext->disp,
145         xcontext->xv_port_id, im_format, NULL, width, height, &meta->SHMInfo);
146     if (!meta->xvimage || error_caught) {
147       g_mutex_unlock (xvimagesink->x_lock);
148
149       /* Reset error flag */
150       error_caught = FALSE;
151
152       /* Push a warning */
153       GST_ELEMENT_WARNING (xvimagesink, RESOURCE, WRITE,
154           ("Failed to create output image buffer of %dx%d pixels",
155               width, height),
156           ("could not XShmCreateImage a %dx%d image", width, height));
157
158       /* Retry without XShm */
159       xvimagesink->xcontext->use_xshm = FALSE;
160
161       /* Hold X mutex again to try without XShm */
162       g_mutex_lock (xvimagesink->x_lock);
163       goto no_xshm;
164     }
165
166     /* we have to use the returned data_size for our shm size */
167     meta->size = meta->xvimage->data_size;
168     GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
169         meta->size);
170
171     /* calculate the expected size.  This is only for sanity checking the
172      * number we get from X. */
173     switch (im_format) {
174       case GST_MAKE_FOURCC ('I', '4', '2', '0'):
175       case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
176       {
177         gint pitches[3];
178         gint offsets[3];
179         guint plane;
180
181         offsets[0] = 0;
182         pitches[0] = GST_ROUND_UP_4 (width);
183         offsets[1] = offsets[0] + pitches[0] * GST_ROUND_UP_2 (height);
184         pitches[1] = GST_ROUND_UP_8 (width) / 2;
185         offsets[2] = offsets[1] + pitches[1] * GST_ROUND_UP_2 (height) / 2;
186         pitches[2] = GST_ROUND_UP_8 (pitches[0]) / 2;
187
188         expected_size = offsets[2] + pitches[2] * GST_ROUND_UP_2 (height) / 2;
189
190         for (plane = 0; plane < meta->xvimage->num_planes; plane++) {
191           GST_DEBUG_OBJECT (xvimagesink,
192               "Plane %u has a expected pitch of %d bytes, " "offset of %d",
193               plane, pitches[plane], offsets[plane]);
194         }
195         break;
196       }
197       case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
198       case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
199         expected_size = height * GST_ROUND_UP_4 (width * 2);
200         break;
201       default:
202         expected_size = 0;
203         break;
204     }
205     if (expected_size != 0 && meta->size != expected_size) {
206       GST_WARNING_OBJECT (xvimagesink,
207           "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
208           meta->size, expected_size);
209     }
210
211     /* Be verbose about our XvImage stride */
212     {
213       guint plane;
214
215       for (plane = 0; plane < meta->xvimage->num_planes; plane++) {
216         GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
217             "offset of %d", plane, meta->xvimage->pitches[plane],
218             meta->xvimage->offsets[plane]);
219       }
220     }
221
222     /* get shared memory */
223     meta->SHMInfo.shmid = shmget (IPC_PRIVATE, meta->size, IPC_CREAT | 0777);
224     if (meta->SHMInfo.shmid == -1)
225       goto shmget_failed;
226
227     /* attach */
228     meta->SHMInfo.shmaddr = shmat (meta->SHMInfo.shmid, NULL, 0);
229     if (meta->SHMInfo.shmaddr == ((void *) -1))
230       goto shmat_failed;
231
232     /* now we can set up the image data */
233     meta->xvimage->data = meta->SHMInfo.shmaddr;
234     meta->SHMInfo.readOnly = FALSE;
235
236     if (XShmAttach (xcontext->disp, &meta->SHMInfo) == 0)
237       goto xattach_failed;
238
239     XSync (xcontext->disp, FALSE);
240
241     /* Delete the shared memory segment as soon as we everyone is attached.
242      * This way, it will be deleted as soon as we detach later, and not
243      * leaked if we crash. */
244     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
245
246     GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
247         meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
248   } else
249   no_xshm:
250 #endif /* HAVE_XSHM */
251   {
252     meta->xvimage = XvCreateImage (xcontext->disp,
253         xcontext->xv_port_id, im_format, NULL, width, height);
254     if (!meta->xvimage || error_caught)
255       goto create_failed;
256
257     /* we have to use the returned data_size for our image size */
258     meta->size = meta->xvimage->data_size;
259     meta->xvimage->data = g_malloc (meta->size);
260
261     XSync (xcontext->disp, FALSE);
262   }
263
264   /* Reset error handler */
265   error_caught = FALSE;
266   XSetErrorHandler (handler);
267
268   gst_buffer_take_memory (buffer, -1,
269       gst_memory_new_wrapped (GST_MEMORY_FLAG_NO_SHARE, meta->xvimage->data,
270           meta->size, 0, meta->size, NULL, NULL));
271
272   g_mutex_unlock (xvimagesink->x_lock);
273
274   success = TRUE;
275
276 beach:
277   if (!success)
278     meta = NULL;
279
280   return meta;
281
282   /* ERRORS */
283 create_failed:
284   {
285     g_mutex_unlock (xvimagesink->x_lock);
286     /* Reset error handler */
287     error_caught = FALSE;
288     XSetErrorHandler (handler);
289     /* Push an error */
290     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
291         ("Failed to create output image buffer of %dx%d pixels",
292             width, height),
293         ("could not XvShmCreateImage a %dx%d image", width, height));
294     goto beach;
295   }
296 shmget_failed:
297   {
298     g_mutex_unlock (xvimagesink->x_lock);
299     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
300         ("Failed to create output image buffer of %dx%d pixels",
301             width, height),
302         ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
303             meta->size));
304     goto beach;
305   }
306 shmat_failed:
307   {
308     g_mutex_unlock (xvimagesink->x_lock);
309     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
310         ("Failed to create output image buffer of %dx%d pixels",
311             width, height), ("Failed to shmat: %s", g_strerror (errno)));
312     /* Clean up the shared memory segment */
313     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
314     goto beach;
315   }
316 xattach_failed:
317   {
318     /* Clean up the shared memory segment */
319     shmctl (meta->SHMInfo.shmid, IPC_RMID, NULL);
320     g_mutex_unlock (xvimagesink->x_lock);
321
322     GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
323         ("Failed to create output image buffer of %dx%d pixels",
324             width, height), ("Failed to XShmAttach"));
325     goto beach;
326   }
327 }
328
329 static void
330 gst_xvimage_meta_free (GstXvImageMeta * meta, GstBuffer * buffer)
331 {
332   GstXvImageSink *xvimagesink;
333
334   xvimagesink = meta->sink;
335
336   GST_DEBUG_OBJECT (xvimagesink, "free meta on buffer %p", buffer);
337
338   /* Hold the object lock to ensure the XContext doesn't disappear */
339   GST_OBJECT_LOCK (xvimagesink);
340   /* We might have some buffers destroyed after changing state to NULL */
341   if (xvimagesink->xcontext == NULL) {
342     GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
343 #ifdef HAVE_XSHM
344     /* Need to free the shared memory segment even if the x context
345      * was already cleaned up */
346     if (meta->SHMInfo.shmaddr != ((void *) -1)) {
347       shmdt (meta->SHMInfo.shmaddr);
348     }
349 #endif
350     if (meta->xvimage)
351       XFree (meta->xvimage);
352     goto beach;
353   }
354
355   g_mutex_lock (xvimagesink->x_lock);
356
357 #ifdef HAVE_XSHM
358   if (xvimagesink->xcontext->use_xshm) {
359     if (meta->SHMInfo.shmaddr != ((void *) -1)) {
360       GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
361           meta->SHMInfo.shmid, meta->SHMInfo.shmseg);
362       XShmDetach (xvimagesink->xcontext->disp, &meta->SHMInfo);
363       XSync (xvimagesink->xcontext->disp, FALSE);
364       shmdt (meta->SHMInfo.shmaddr);
365       meta->SHMInfo.shmaddr = (void *) -1;
366     }
367     if (meta->xvimage)
368       XFree (meta->xvimage);
369   } else
370 #endif /* HAVE_XSHM */
371   {
372     if (meta->xvimage) {
373       g_free (meta->xvimage->data);
374       XFree (meta->xvimage);
375     }
376   }
377
378   XSync (xvimagesink->xcontext->disp, FALSE);
379
380   g_mutex_unlock (xvimagesink->x_lock);
381
382 beach:
383   GST_OBJECT_UNLOCK (xvimagesink);
384
385   gst_object_unref (meta->sink);
386 }
387
388 #ifdef HAVE_XSHM
389 /* This function checks that it is actually really possible to create an image
390    using XShm */
391 gboolean
392 gst_xvimagesink_check_xshm_calls (GstXvImageSink * xvimagesink,
393     GstXContext * xcontext)
394 {
395   XvImage *xvimage;
396   XShmSegmentInfo SHMInfo;
397   size_t size;
398   int (*handler) (Display *, XErrorEvent *);
399   gboolean result = FALSE;
400   gboolean did_attach = FALSE;
401
402   g_return_val_if_fail (xcontext != NULL, FALSE);
403
404   /* Sync to ensure any older errors are already processed */
405   XSync (xcontext->disp, FALSE);
406
407   /* Set defaults so we don't free these later unnecessarily */
408   SHMInfo.shmaddr = ((void *) -1);
409   SHMInfo.shmid = -1;
410
411   /* Setting an error handler to catch failure */
412   error_caught = FALSE;
413   handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
414
415   /* Trying to create a 1x1 picture */
416   GST_DEBUG ("XvShmCreateImage of 1x1");
417   xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
418       xcontext->im_format, NULL, 1, 1, &SHMInfo);
419
420   /* Might cause an error, sync to ensure it is noticed */
421   XSync (xcontext->disp, FALSE);
422   if (!xvimage || error_caught) {
423     GST_WARNING ("could not XvShmCreateImage a 1x1 image");
424     goto beach;
425   }
426   size = xvimage->data_size;
427
428   SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
429   if (SHMInfo.shmid == -1) {
430     GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
431         size);
432     goto beach;
433   }
434
435   SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
436   if (SHMInfo.shmaddr == ((void *) -1)) {
437     GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
438     /* Clean up the shared memory segment */
439     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
440     goto beach;
441   }
442
443   xvimage->data = SHMInfo.shmaddr;
444   SHMInfo.readOnly = FALSE;
445
446   if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
447     GST_WARNING ("Failed to XShmAttach");
448     /* Clean up the shared memory segment */
449     shmctl (SHMInfo.shmid, IPC_RMID, NULL);
450     goto beach;
451   }
452
453   /* Sync to ensure we see any errors we caused */
454   XSync (xcontext->disp, FALSE);
455
456   /* Delete the shared memory segment as soon as everyone is attached.
457    * This way, it will be deleted as soon as we detach later, and not
458    * leaked if we crash. */
459   shmctl (SHMInfo.shmid, IPC_RMID, NULL);
460
461   if (!error_caught) {
462     GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
463         SHMInfo.shmseg);
464
465     did_attach = TRUE;
466     /* store whether we succeeded in result */
467     result = TRUE;
468   } else {
469     GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
470         "Not using shared memory.");
471   }
472
473 beach:
474   /* Sync to ensure we swallow any errors we caused and reset error_caught */
475   XSync (xcontext->disp, FALSE);
476
477   error_caught = FALSE;
478   XSetErrorHandler (handler);
479
480   if (did_attach) {
481     GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
482         SHMInfo.shmid, SHMInfo.shmseg);
483     XShmDetach (xcontext->disp, &SHMInfo);
484     XSync (xcontext->disp, FALSE);
485   }
486   if (SHMInfo.shmaddr != ((void *) -1))
487     shmdt (SHMInfo.shmaddr);
488   if (xvimage)
489     XFree (xvimage);
490   return result;
491 }
492 #endif /* HAVE_XSHM */
493
494 /* bufferpool */
495 static void gst_xvimage_buffer_pool_finalize (GObject * object);
496
497 #define GST_XVIMAGE_BUFFER_POOL_GET_PRIVATE(obj)  \
498    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_XVIMAGE_BUFFER_POOL, GstXvImageBufferPoolPrivate))
499
500 #define gst_xvimage_buffer_pool_parent_class parent_class
501 G_DEFINE_TYPE (GstXvImageBufferPool, gst_xvimage_buffer_pool,
502     GST_TYPE_BUFFER_POOL);
503
504 static const gchar **
505 xvimage_buffer_pool_get_options (GstBufferPool * pool)
506 {
507   static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
508     GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
509   };
510
511   return options;
512 }
513
514 static gboolean
515 xvimage_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
516 {
517   GstXvImageBufferPool *xvpool = GST_XVIMAGE_BUFFER_POOL_CAST (pool);
518   GstXvImageBufferPoolPrivate *priv = xvpool->priv;
519   GstVideoInfo info;
520   GstCaps *caps;
521
522   if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL))
523     goto wrong_config;
524
525   if (caps == NULL)
526     goto no_caps;
527
528   /* now parse the caps from the config */
529   if (!gst_video_info_from_caps (&info, caps))
530     goto wrong_caps;
531
532   GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
533       caps);
534
535   priv->im_format = gst_xvimagesink_get_format_from_info (xvpool->sink, &info);
536   if (priv->im_format == -1)
537     goto unknown_format;
538
539   if (priv->caps)
540     gst_caps_unref (priv->caps);
541   priv->caps = gst_caps_ref (caps);
542   priv->info = info;
543
544   /* enable metadata based on config of the pool */
545   priv->add_metavideo =
546       gst_buffer_pool_config_has_option (config,
547       GST_BUFFER_POOL_OPTION_VIDEO_META);
548
549   /* parse extra alignment info */
550   priv->need_alignment = gst_buffer_pool_config_has_option (config,
551       GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
552
553   if (priv->need_alignment) {
554     gst_buffer_pool_config_get_video_alignment (config, &priv->align);
555
556     GST_LOG_OBJECT (pool, "padding %u-%ux%u-%u", priv->align.padding_top,
557         priv->align.padding_left, priv->align.padding_left,
558         priv->align.padding_bottom);
559
560     /* we need the video metadata too now */
561     priv->add_metavideo = TRUE;
562   } else {
563     gst_video_alignment_reset (&priv->align);
564   }
565
566   /* add the padding */
567   priv->padded_width =
568       GST_VIDEO_INFO_WIDTH (&info) + priv->align.padding_left +
569       priv->align.padding_right;
570   priv->padded_height =
571       GST_VIDEO_INFO_HEIGHT (&info) + priv->align.padding_top +
572       priv->align.padding_bottom;
573
574   return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
575
576   /* ERRORS */
577 wrong_config:
578   {
579     GST_WARNING_OBJECT (pool, "invalid config");
580     return FALSE;
581   }
582 no_caps:
583   {
584     GST_WARNING_OBJECT (pool, "no caps in config");
585     return FALSE;
586   }
587 wrong_caps:
588   {
589     GST_WARNING_OBJECT (pool,
590         "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
591     return FALSE;
592   }
593 unknown_format:
594   {
595     GST_WARNING_OBJECT (xvpool->sink, "failed to get format from caps %"
596         GST_PTR_FORMAT, caps);
597     GST_ELEMENT_ERROR (xvpool->sink, RESOURCE, WRITE,
598         ("Failed to create output image buffer of %dx%d pixels",
599             priv->info.width, priv->info.height),
600         ("Invalid input caps %" GST_PTR_FORMAT, caps));
601     return FALSE;;
602   }
603 }
604
605 /* This function handles GstXImageBuffer creation depending on XShm availability */
606 static GstFlowReturn
607 xvimage_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
608     GstBufferPoolAcquireParams * params)
609 {
610   GstXvImageBufferPool *xvpool = GST_XVIMAGE_BUFFER_POOL_CAST (pool);
611   GstXvImageBufferPoolPrivate *priv = xvpool->priv;
612   GstVideoInfo *info;
613   GstBuffer *xvimage;
614   GstXvImageMeta *meta;
615
616   info = &priv->info;
617
618   xvimage = gst_buffer_new ();
619   meta = gst_buffer_add_xvimage_meta (xvimage, xvpool);
620   if (meta == NULL) {
621     gst_buffer_unref (xvimage);
622     goto no_buffer;
623   }
624
625   if (priv->add_metavideo) {
626     GstVideoMeta *meta;
627     const GstVideoFormatInfo *vinfo = info->finfo;
628     gint i;
629
630     GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");
631     /* these are just the defaults for now */
632     meta = gst_buffer_add_video_meta (xvimage, 0, GST_VIDEO_INFO_FORMAT (info),
633         priv->padded_width, priv->padded_height);
634
635     if (priv->need_alignment) {
636       meta->width = GST_VIDEO_INFO_WIDTH (&priv->info);
637       meta->height = GST_VIDEO_INFO_HEIGHT (&priv->info);
638
639       /* FIXME, not quite correct, NV12 would apply the vedge twice on the second
640        * plane */
641       for (i = 0; i < GST_VIDEO_INFO_N_COMPONENTS (info); i++) {
642         gint vedge, hedge, plane;
643
644         hedge =
645             GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (vinfo, i,
646             priv->align.padding_left);
647         vedge =
648             GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (vinfo, i,
649             priv->align.padding_top);
650         plane = GST_VIDEO_FORMAT_INFO_PLANE (vinfo, i);
651
652         GST_LOG_OBJECT (pool, "comp %d, plane %d: hedge %d, vedge %d", i,
653             plane, hedge, vedge);
654
655         meta->offset[plane] += (vedge * meta->stride[plane]) + hedge;
656       }
657     }
658   }
659
660   *buffer = xvimage;
661
662   return GST_FLOW_OK;
663
664   /* ERROR */
665 no_buffer:
666   {
667     GST_WARNING_OBJECT (pool, "can't create image");
668     return GST_FLOW_ERROR;
669   }
670 }
671
672 GstBufferPool *
673 gst_xvimage_buffer_pool_new (GstXvImageSink * xvimagesink)
674 {
675   GstXvImageBufferPool *pool;
676
677   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
678
679   pool = g_object_new (GST_TYPE_XVIMAGE_BUFFER_POOL, NULL);
680   pool->sink = gst_object_ref (xvimagesink);
681
682   GST_LOG_OBJECT (pool, "new XvImage buffer pool %p", pool);
683
684   return GST_BUFFER_POOL_CAST (pool);
685 }
686
687 static void
688 gst_xvimage_buffer_pool_class_init (GstXvImageBufferPoolClass * klass)
689 {
690   GObjectClass *gobject_class = (GObjectClass *) klass;
691   GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
692
693   g_type_class_add_private (klass, sizeof (GstXvImageBufferPoolPrivate));
694
695   gobject_class->finalize = gst_xvimage_buffer_pool_finalize;
696
697   gstbufferpool_class->get_options = xvimage_buffer_pool_get_options;
698   gstbufferpool_class->set_config = xvimage_buffer_pool_set_config;
699   gstbufferpool_class->alloc_buffer = xvimage_buffer_pool_alloc;
700 }
701
702 static void
703 gst_xvimage_buffer_pool_init (GstXvImageBufferPool * pool)
704 {
705   pool->priv = GST_XVIMAGE_BUFFER_POOL_GET_PRIVATE (pool);
706 }
707
708 static void
709 gst_xvimage_buffer_pool_finalize (GObject * object)
710 {
711   GstXvImageBufferPool *pool = GST_XVIMAGE_BUFFER_POOL_CAST (object);
712   GstXvImageBufferPoolPrivate *priv = pool->priv;
713
714   GST_LOG_OBJECT (pool, "finalize XvImage buffer pool %p", pool);
715
716   if (priv->caps)
717     gst_caps_unref (priv->caps);
718   gst_object_unref (pool->sink);
719
720   G_OBJECT_CLASS (gst_xvimage_buffer_pool_parent_class)->finalize (object);
721 }
722
723 /* This function tries to get a format matching with a given caps in the
724    supported list of formats we generated in gst_xvimagesink_get_xv_support */
725 gint
726 gst_xvimagesink_get_format_from_info (GstXvImageSink * xvimagesink,
727     GstVideoInfo * info)
728 {
729   GList *list = NULL;
730
731   g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
732
733   list = xvimagesink->xcontext->formats_list;
734
735   while (list) {
736     GstXvImageFormat *format = list->data;
737
738     if (format && format->vformat == GST_VIDEO_INFO_FORMAT (info))
739       return format->format;
740
741     list = g_list_next (list);
742   }
743
744   return -1;
745 }