745495d47e9ecef034fcbfeb8cc4f46397d40dc5
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapisurface.c
1 /*
2  *  gstvaapisurface.c - VA surface abstraction
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011 Intel Corporation
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public License
9  *  as published by the Free Software Foundation; either version 2.1
10  *  of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free
19  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA 02110-1301 USA
21  */
22
23 /**
24  * SECTION:gstvaapisurface
25  * @short_description: VA surface abstraction
26  */
27
28 #include "sysdeps.h"
29 #include "gstvaapicompat.h"
30 #include "gstvaapiutils.h"
31 #include "gstvaapisurface.h"
32 #include "gstvaapisurface_priv.h"
33 #include "gstvaapicontext.h"
34 #include "gstvaapiimage.h"
35 #include "gstvaapi_priv.h"
36
37 #define DEBUG 1
38 #include "gstvaapidebug.h"
39
40 G_DEFINE_TYPE(GstVaapiSurface, gst_vaapi_surface, GST_VAAPI_TYPE_OBJECT)
41
42 #define GST_VAAPI_SURFACE_GET_PRIVATE(obj)                      \
43     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                         \
44                                  GST_VAAPI_TYPE_SURFACE,        \
45                                  GstVaapiSurfacePrivate))
46
47 struct _GstVaapiSurfacePrivate {
48     guint               width;
49     guint               height;
50     GstVaapiChromaType  chroma_type;
51     GPtrArray          *subpictures;
52     GstVaapiContext    *parent_context;
53 };
54
55 enum {
56     PROP_0,
57
58     PROP_WIDTH,
59     PROP_HEIGHT,
60     PROP_CHROMA_TYPE,
61     PROP_PARENT_CONTEXT
62 };
63
64 static gboolean
65 _gst_vaapi_surface_associate_subpicture(
66     GstVaapiSurface         *surface,
67     GstVaapiSubpicture      *subpicture,
68     const GstVaapiRectangle *src_rect,
69     const GstVaapiRectangle *dst_rect
70 );
71
72 static gboolean
73 _gst_vaapi_surface_deassociate_subpicture(
74     GstVaapiSurface    *surface,
75     GstVaapiSubpicture *subpicture
76 );
77
78 static void
79 destroy_subpicture_cb(gpointer subpicture, gpointer surface)
80 {
81     _gst_vaapi_surface_deassociate_subpicture(surface, subpicture);
82     g_object_unref(subpicture);
83 }
84
85 static void
86 gst_vaapi_surface_destroy_subpictures(GstVaapiSurface *surface)
87 {
88     GstVaapiSurfacePrivate * const priv = surface->priv;
89
90     if (priv->subpictures) {
91         g_ptr_array_foreach(priv->subpictures, destroy_subpicture_cb, surface);
92         g_ptr_array_free(priv->subpictures, TRUE);
93         priv->subpictures = NULL;
94     }
95 }
96
97 static void
98 gst_vaapi_surface_destroy(GstVaapiSurface *surface)
99 {
100     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(surface);
101     VASurfaceID surface_id;
102     VAStatus status;
103
104     surface_id = GST_VAAPI_OBJECT_ID(surface);
105     GST_DEBUG("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(surface_id));
106
107     gst_vaapi_surface_destroy_subpictures(surface);
108     gst_vaapi_surface_set_parent_context(surface, NULL);
109   
110     if (surface_id != VA_INVALID_SURFACE) {
111         GST_VAAPI_DISPLAY_LOCK(display);
112         status = vaDestroySurfaces(
113             GST_VAAPI_DISPLAY_VADISPLAY(display),
114             &surface_id, 1
115         );
116         GST_VAAPI_DISPLAY_UNLOCK(display);
117         if (!vaapi_check_status(status, "vaDestroySurfaces()"))
118             g_warning("failed to destroy surface %" GST_VAAPI_ID_FORMAT,
119                       GST_VAAPI_ID_ARGS(surface_id));
120         GST_VAAPI_OBJECT_ID(surface) = VA_INVALID_SURFACE;
121     }
122 }
123
124 static gboolean
125 gst_vaapi_surface_create(GstVaapiSurface *surface)
126 {
127     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(surface);
128     GstVaapiSurfacePrivate * const priv = surface->priv;
129     VASurfaceID surface_id;
130     VAStatus status;
131     guint format;
132
133     switch (priv->chroma_type) {
134     case GST_VAAPI_CHROMA_TYPE_YUV420:
135         format = VA_RT_FORMAT_YUV420;
136         break;
137     case GST_VAAPI_CHROMA_TYPE_YUV422:
138         format = VA_RT_FORMAT_YUV422;
139         break;
140     case GST_VAAPI_CHROMA_TYPE_YUV444:
141         format = VA_RT_FORMAT_YUV444;
142         break;
143     default:
144         GST_DEBUG("unsupported chroma-type %u\n", priv->chroma_type);
145         return FALSE;
146     }
147
148     GST_VAAPI_DISPLAY_LOCK(display);
149     status = vaCreateSurfaces(
150         GST_VAAPI_DISPLAY_VADISPLAY(display),
151         priv->width,
152         priv->height,
153         format,
154         1, &surface_id
155     );
156     GST_VAAPI_DISPLAY_UNLOCK(display);
157     if (!vaapi_check_status(status, "vaCreateSurfaces()"))
158         return FALSE;
159
160     GST_DEBUG("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(surface_id));
161     GST_VAAPI_OBJECT_ID(surface) = surface_id;
162     return TRUE;
163 }
164
165 static void
166 gst_vaapi_surface_finalize(GObject *object)
167 {
168     gst_vaapi_surface_destroy(GST_VAAPI_SURFACE(object));
169
170     G_OBJECT_CLASS(gst_vaapi_surface_parent_class)->finalize(object);
171 }
172
173 static void
174 gst_vaapi_surface_set_property(
175     GObject      *object,
176     guint         prop_id,
177     const GValue *value,
178     GParamSpec   *pspec
179 )
180 {
181     GstVaapiSurface        * const surface = GST_VAAPI_SURFACE(object);
182     GstVaapiSurfacePrivate * const priv    = surface->priv;
183
184     switch (prop_id) {
185     case PROP_WIDTH:
186         priv->width = g_value_get_uint(value);
187         break;
188     case PROP_HEIGHT:
189         priv->height = g_value_get_uint(value);
190         break;
191     case PROP_CHROMA_TYPE:
192         priv->chroma_type = g_value_get_uint(value);
193         break;
194     case PROP_PARENT_CONTEXT:
195         gst_vaapi_surface_set_parent_context(surface, g_value_get_object(value));
196         break;
197     default:
198         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
199         break;
200     }
201 }
202
203 static void
204 gst_vaapi_surface_get_property(
205     GObject    *object,
206     guint       prop_id,
207     GValue     *value,
208     GParamSpec *pspec
209 )
210 {
211     GstVaapiSurface * const surface = GST_VAAPI_SURFACE(object);
212
213     switch (prop_id) {
214     case PROP_WIDTH:
215         g_value_set_uint(value, gst_vaapi_surface_get_width(surface));
216         break;
217     case PROP_HEIGHT:
218         g_value_set_uint(value, gst_vaapi_surface_get_height(surface));
219         break;
220     case PROP_CHROMA_TYPE:
221         g_value_set_uint(value, gst_vaapi_surface_get_chroma_type(surface));
222         break;
223     case PROP_PARENT_CONTEXT:
224         g_value_set_object(value, gst_vaapi_surface_get_parent_context(surface));
225         break;
226     default:
227         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
228         break;
229     }
230 }
231
232 static void
233 gst_vaapi_surface_constructed(GObject *object)
234 {
235     GstVaapiSurface * const surface = GST_VAAPI_SURFACE(object);
236     GObjectClass *parent_class;
237
238     gst_vaapi_surface_create(surface);
239
240     parent_class = G_OBJECT_CLASS(gst_vaapi_surface_parent_class);
241     if (parent_class->constructed)
242         parent_class->constructed(object);
243 }
244
245 static void
246 gst_vaapi_surface_class_init(GstVaapiSurfaceClass *klass)
247 {
248     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
249
250     g_type_class_add_private(klass, sizeof(GstVaapiSurfacePrivate));
251
252     object_class->finalize     = gst_vaapi_surface_finalize;
253     object_class->set_property = gst_vaapi_surface_set_property;
254     object_class->get_property = gst_vaapi_surface_get_property;
255     object_class->constructed  = gst_vaapi_surface_constructed;
256
257     g_object_class_install_property
258         (object_class,
259          PROP_WIDTH,
260          g_param_spec_uint("width",
261                            "Width",
262                            "The width of the surface",
263                            0, G_MAXINT32, 0,
264                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
265
266     g_object_class_install_property
267         (object_class,
268          PROP_HEIGHT,
269          g_param_spec_uint("height",
270                            "Height",
271                            "The height of the surface",
272                            0, G_MAXINT32, 0,
273                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
274
275     g_object_class_install_property
276         (object_class,
277          PROP_CHROMA_TYPE,
278          g_param_spec_uint("chroma-type",
279                            "Chroma type",
280                            "The chroma type of the surface",
281                            0, G_MAXUINT32, 0,
282                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
283
284     g_object_class_install_property
285         (object_class,
286          PROP_PARENT_CONTEXT,
287          g_param_spec_object("parent-context",
288                              "Parent Context",
289                              "The parent context, if any",
290                              GST_VAAPI_TYPE_CONTEXT,
291                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
292 }
293
294 static void
295 gst_vaapi_surface_init(GstVaapiSurface *surface)
296 {
297     GstVaapiSurfacePrivate *priv = GST_VAAPI_SURFACE_GET_PRIVATE(surface);
298
299     surface->priv        = priv;
300     priv->width          = 0;
301     priv->height         = 0;
302     priv->chroma_type    = 0;
303     priv->subpictures    = NULL;
304     priv->parent_context = NULL;
305 }
306
307 /**
308  * gst_vaapi_surface_new:
309  * @display: a #GstVaapiDisplay
310  * @chroma_type: the surface chroma format
311  * @width: the requested surface width
312  * @height: the requested surface height
313  *
314  * Creates a new #GstVaapiSurface with the specified chroma format and
315  * dimensions.
316  *
317  * Return value: the newly allocated #GstVaapiSurface object
318  */
319 GstVaapiSurface *
320 gst_vaapi_surface_new(
321     GstVaapiDisplay    *display,
322     GstVaapiChromaType  chroma_type,
323     guint               width,
324     guint               height
325 )
326 {
327     GST_DEBUG("size %ux%u, chroma type 0x%x", width, height, chroma_type);
328
329     return g_object_new(GST_VAAPI_TYPE_SURFACE,
330                         "display",      display,
331                         "id",           GST_VAAPI_ID(VA_INVALID_ID),
332                         "width",        width,
333                         "height",       height,
334                         "chroma-type",  chroma_type,
335                         NULL);
336 }
337
338 /**
339  * gst_vaapi_surface_get_id:
340  * @surface: a #GstVaapiSurface
341  *
342  * Returns the underlying VASurfaceID of the @surface.
343  *
344  * Return value: the underlying VA surface id
345  */
346 GstVaapiID
347 gst_vaapi_surface_get_id(GstVaapiSurface *surface)
348 {
349     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), VA_INVALID_SURFACE);
350
351     return GST_VAAPI_OBJECT_ID(surface);
352 }
353
354 /**
355  * gst_vaapi_surface_get_chroma_type:
356  * @surface: a #GstVaapiSurface
357  *
358  * Returns the #GstVaapiChromaType the @surface was created with.
359  *
360  * Return value: the #GstVaapiChromaType
361  */
362 GstVaapiChromaType
363 gst_vaapi_surface_get_chroma_type(GstVaapiSurface *surface)
364 {
365     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), 0);
366
367     return surface->priv->chroma_type;
368 }
369
370 /**
371  * gst_vaapi_surface_get_width:
372  * @surface: a #GstVaapiSurface
373  *
374  * Returns the @surface width.
375  *
376  * Return value: the surface width, in pixels
377  */
378 guint
379 gst_vaapi_surface_get_width(GstVaapiSurface *surface)
380 {
381     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), 0);
382
383     return surface->priv->width;
384 }
385
386 /**
387  * gst_vaapi_surface_get_height:
388  * @surface: a #GstVaapiSurface
389  *
390  * Returns the @surface height.
391  *
392  * Return value: the surface height, in pixels.
393  */
394 guint
395 gst_vaapi_surface_get_height(GstVaapiSurface *surface)
396 {
397     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), 0);
398
399     return surface->priv->height;
400 }
401
402 /**
403  * gst_vaapi_surface_get_size:
404  * @surface: a #GstVaapiSurface
405  * @pwidth: return location for the width, or %NULL
406  * @pheight: return location for the height, or %NULL
407  *
408  * Retrieves the dimensions of a #GstVaapiSurface.
409  */
410 void
411 gst_vaapi_surface_get_size(
412     GstVaapiSurface *surface,
413     guint           *pwidth,
414     guint           *pheight
415 )
416 {
417     g_return_if_fail(GST_VAAPI_IS_SURFACE(surface));
418
419     if (pwidth)
420         *pwidth = gst_vaapi_surface_get_width(surface);
421
422     if (pheight)
423         *pheight = gst_vaapi_surface_get_height(surface);
424 }
425
426 /**
427  * gst_vaapi_surface_set_parent_context:
428  * @surface: a #GstVaapiSurface
429  * @context: a #GstVaapiContext
430  *
431  * Sets new parent context, or clears any parent context if @context
432  * is %NULL. This function owns an extra reference to the context,
433  * which will be released when the surface is destroyed.
434  */
435 void
436 gst_vaapi_surface_set_parent_context(
437     GstVaapiSurface *surface,
438     GstVaapiContext *context
439 )
440 {
441     GstVaapiSurfacePrivate *priv;
442
443     g_return_if_fail(GST_VAAPI_IS_SURFACE(surface));
444
445     priv = surface->priv;
446
447     g_clear_object(&priv->parent_context);
448
449     if (context)
450         priv->parent_context = g_object_ref(context);
451 }
452
453 /**
454  * gst_vaapi_surface_get_parent_context:
455  * @surface: a #GstVaapiSurface
456  *
457  * Retrieves the parent #GstVaapiContext, or %NULL if there is
458  * none. The surface shall still own a reference to the context.
459  * i.e. the caller shall not unreference the returned context object.
460  *
461  * Return value: the parent context, if any.
462  */
463 GstVaapiContext *
464 gst_vaapi_surface_get_parent_context(GstVaapiSurface *surface)
465 {
466     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), NULL);
467
468     return surface->priv->parent_context;
469 }
470
471 /**
472  * gst_vaapi_surface_derive_image:
473  * @surface: a #GstVaapiSurface
474  *
475  * Derives a #GstVaapiImage from the @surface. This image buffer can
476  * then be mapped/unmapped for direct CPU access. This operation is
477  * only possible if the underlying implementation supports direct
478  * rendering capabilities and internal surface formats that can be
479  * represented with a #GstVaapiImage.
480  *
481  * When the operation is not possible, the function returns %NULL and
482  * the user should then fallback to using gst_vaapi_surface_get_image()
483  * or gst_vaapi_surface_put_image() to accomplish the same task in an
484  * indirect manner (additional copy).
485  *
486  * An image created with gst_vaapi_surface_derive_image() should be
487  * unreferenced when it's no longer needed. The image and image buffer
488  * data structures will be destroyed. However, the surface contents
489  * will remain unchanged until destroyed through the last call to
490  * g_object_unref().
491  *
492  * Return value: the newly allocated #GstVaapiImage object, or %NULL
493  *   on failure
494  */
495 GstVaapiImage *
496 gst_vaapi_surface_derive_image(GstVaapiSurface *surface)
497 {
498     GstVaapiDisplay *display;
499     VAImage va_image;
500     VAStatus status;
501
502     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), NULL);
503
504     display           = GST_VAAPI_OBJECT_DISPLAY(surface);
505     va_image.image_id = VA_INVALID_ID;
506     va_image.buf      = VA_INVALID_ID;
507
508     GST_VAAPI_DISPLAY_LOCK(display);
509     status = vaDeriveImage(
510         GST_VAAPI_DISPLAY_VADISPLAY(display),
511         GST_VAAPI_OBJECT_ID(surface),
512         &va_image
513     );
514     GST_VAAPI_DISPLAY_UNLOCK(display);
515     if (!vaapi_check_status(status, "vaDeriveImage()"))
516         return NULL;
517     if (va_image.image_id == VA_INVALID_ID || va_image.buf == VA_INVALID_ID)
518         return NULL;
519
520     return gst_vaapi_image_new_with_image(display, &va_image);
521 }
522
523 /**
524  * gst_vaapi_surface_get_image
525  * @surface: a #GstVaapiSurface
526  * @image: a #GstVaapiImage
527  *
528  * Retrieves surface data into a #GstVaapiImage. The @image must have
529  * a format supported by the @surface.
530  *
531  * Return value: %TRUE on success
532  */
533 gboolean
534 gst_vaapi_surface_get_image(GstVaapiSurface *surface, GstVaapiImage *image)
535 {
536     GstVaapiDisplay *display;
537     VAImageID image_id;
538     VAStatus status;
539     guint width, height;
540
541     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
542     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
543
544     display = GST_VAAPI_OBJECT_DISPLAY(surface);
545     if (!display)
546         return FALSE;
547
548     gst_vaapi_image_get_size(image, &width, &height);
549     if (width != surface->priv->width || height != surface->priv->height)
550         return FALSE;
551
552     image_id = GST_VAAPI_OBJECT_ID(image);
553     if (image_id == VA_INVALID_ID)
554         return FALSE;
555
556     GST_VAAPI_DISPLAY_LOCK(display);
557     status = vaGetImage(
558         GST_VAAPI_DISPLAY_VADISPLAY(display),
559         GST_VAAPI_OBJECT_ID(surface),
560         0, 0, width, height,
561         image_id
562     );
563     GST_VAAPI_DISPLAY_UNLOCK(display);
564     if (!vaapi_check_status(status, "vaGetImage()"))
565         return FALSE;
566
567     return TRUE;
568 }
569
570 /**
571  * gst_vaapi_surface_put_image:
572  * @surface: a #GstVaapiSurface
573  * @image: a #GstVaapiImage
574  *
575  * Copies data from a #GstVaapiImage into a @surface. The @image must
576  * have a format supported by the @surface.
577  *
578  * Return value: %TRUE on success
579  */
580 gboolean
581 gst_vaapi_surface_put_image(GstVaapiSurface *surface, GstVaapiImage *image)
582 {
583     GstVaapiDisplay *display;
584     VAImageID image_id;
585     VAStatus status;
586     guint width, height;
587
588     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
589     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
590
591     display = GST_VAAPI_OBJECT_DISPLAY(surface);
592     if (!display)
593         return FALSE;
594
595     gst_vaapi_image_get_size(image, &width, &height);
596     if (width != surface->priv->width || height != surface->priv->height)
597         return FALSE;
598
599     image_id = GST_VAAPI_OBJECT_ID(image);
600     if (image_id == VA_INVALID_ID)
601         return FALSE;
602
603     GST_VAAPI_DISPLAY_LOCK(display);
604     status = vaPutImage(
605         GST_VAAPI_DISPLAY_VADISPLAY(display),
606         GST_VAAPI_OBJECT_ID(surface),
607         image_id,
608         0, 0, width, height,
609         0, 0, width, height
610     );
611     GST_VAAPI_DISPLAY_UNLOCK(display);
612     if (!vaapi_check_status(status, "vaPutImage()"))
613         return FALSE;
614
615     return TRUE;
616 }
617
618 /**
619  * gst_vaapi_surface_associate_subpicture:
620  * @surface: a #GstVaapiSurface
621  * @subpicture: a #GstVaapiSubpicture
622  * @src_rect: the sub-rectangle of the source subpicture
623  *   image to extract and process. If %NULL, the entire image will be used.
624  * @dst_rect: the sub-rectangle of the destination
625  *   surface into which the image is rendered. If %NULL, the entire
626  *   surface will be used.
627  *
628  * Associates the @subpicture with the @surface. The @src_rect
629  * coordinates and size are relative to the source image bound to
630  * @subpicture. The @dst_rect coordinates and size are relative to the
631  * target @surface. Note that the @surface holds an additional
632  * reference to the @subpicture.
633  *
634  * Return value: %TRUE on success
635  */
636 gboolean
637 gst_vaapi_surface_associate_subpicture(
638     GstVaapiSurface         *surface,
639     GstVaapiSubpicture      *subpicture,
640     const GstVaapiRectangle *src_rect,
641     const GstVaapiRectangle *dst_rect
642 )
643 {
644     gboolean success;
645
646     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
647     g_return_val_if_fail(GST_VAAPI_IS_SUBPICTURE(subpicture), FALSE);
648
649     if (!surface->priv->subpictures) {
650         surface->priv->subpictures = g_ptr_array_new();
651         if (!surface->priv->subpictures)
652             return FALSE;
653     }
654
655     if (g_ptr_array_remove_fast(surface->priv->subpictures, subpicture)) {
656         success = _gst_vaapi_surface_deassociate_subpicture(surface, subpicture);
657         g_object_unref(subpicture);
658         if (!success)
659             return FALSE;
660     }
661
662     success = _gst_vaapi_surface_associate_subpicture(
663         surface,
664         subpicture,
665         src_rect,
666         dst_rect
667     );
668     if (!success)
669         return FALSE;
670
671     g_ptr_array_add(surface->priv->subpictures, g_object_ref(subpicture));
672     return TRUE;
673 }
674
675 gboolean
676 _gst_vaapi_surface_associate_subpicture(
677     GstVaapiSurface         *surface,
678     GstVaapiSubpicture      *subpicture,
679     const GstVaapiRectangle *src_rect,
680     const GstVaapiRectangle *dst_rect
681 )
682 {
683     GstVaapiDisplay *display;
684     GstVaapiRectangle src_rect_default, dst_rect_default;
685     GstVaapiImage *image;
686     VASurfaceID surface_id;
687     VAStatus status;
688
689     display = GST_VAAPI_OBJECT_DISPLAY(surface);
690     if (!display)
691         return FALSE;
692
693     surface_id = GST_VAAPI_OBJECT_ID(surface);
694     if (surface_id == VA_INVALID_SURFACE)
695         return FALSE;
696
697     if (!src_rect) {
698         image = gst_vaapi_subpicture_get_image(subpicture);
699         if (!image)
700             return FALSE;
701         src_rect                = &src_rect_default;
702         src_rect_default.x      = 0;
703         src_rect_default.y      = 0;
704         gst_vaapi_image_get_size(
705             image,
706             &src_rect_default.width,
707             &src_rect_default.height
708         );
709     }
710
711     if (!dst_rect) {
712         dst_rect                = &dst_rect_default;
713         dst_rect_default.x      = 0;
714         dst_rect_default.y      = 0;
715         dst_rect_default.width  = surface->priv->width;
716         dst_rect_default.height = surface->priv->height;
717     }
718
719     GST_VAAPI_DISPLAY_LOCK(display);
720     status = vaAssociateSubpicture(
721         GST_VAAPI_DISPLAY_VADISPLAY(display),
722         GST_VAAPI_OBJECT_ID(subpicture),
723         &surface_id, 1,
724         src_rect->x, src_rect->y, src_rect->width, src_rect->height,
725         dst_rect->x, dst_rect->y, dst_rect->width, dst_rect->height,
726         0
727     );
728     GST_VAAPI_DISPLAY_UNLOCK(display);
729     if (!vaapi_check_status(status, "vaAssociateSubpicture()"))
730         return FALSE;
731
732     return TRUE;
733 }
734
735 /**
736  * gst_vaapi_surface_deassociate_subpicture:
737  * @surface: a #GstVaapiSurface
738  * @subpicture: a #GstVaapiSubpicture
739  *
740  * Deassociates @subpicture from @surface. Other associations are kept.
741  *
742  * Return value: %TRUE on success
743  */
744 gboolean
745 gst_vaapi_surface_deassociate_subpicture(
746     GstVaapiSurface         *surface,
747     GstVaapiSubpicture      *subpicture
748 )
749 {
750     gboolean success;
751
752     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
753     g_return_val_if_fail(GST_VAAPI_IS_SUBPICTURE(subpicture), FALSE);
754
755     if (!surface->priv->subpictures)
756         return TRUE;
757
758     /* First, check subpicture was really associated with this surface */
759     if (!g_ptr_array_remove_fast(surface->priv->subpictures, subpicture)) {
760         GST_DEBUG("subpicture %" GST_VAAPI_ID_FORMAT " was not bound to "
761                   "surface %" GST_VAAPI_ID_FORMAT,
762                   GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(subpicture)),
763                   GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(surface)));
764         return TRUE;
765     }
766
767     success = _gst_vaapi_surface_deassociate_subpicture(surface, subpicture);
768     g_object_unref(subpicture);
769     return success;
770 }
771
772 gboolean
773 _gst_vaapi_surface_deassociate_subpicture(
774     GstVaapiSurface         *surface,
775     GstVaapiSubpicture      *subpicture
776 )
777 {
778     GstVaapiDisplay *display;
779     VASurfaceID surface_id;
780     VAStatus status;
781
782     display = GST_VAAPI_OBJECT_DISPLAY(surface);
783     if (!display)
784         return FALSE;
785
786     surface_id = GST_VAAPI_OBJECT_ID(surface);
787     if (surface_id == VA_INVALID_SURFACE)
788         return FALSE;
789
790     GST_VAAPI_DISPLAY_LOCK(display);
791     status = vaDeassociateSubpicture(
792         GST_VAAPI_DISPLAY_VADISPLAY(display),
793         GST_VAAPI_OBJECT_ID(subpicture),
794         &surface_id, 1
795     );
796     GST_VAAPI_DISPLAY_UNLOCK(display);
797     if (!vaapi_check_status(status, "vaDeassociateSubpicture()"))
798         return FALSE;
799
800     return TRUE;
801 }
802
803 /**
804  * gst_vaapi_surface_sync:
805  * @surface: a #GstVaapiSurface
806  *
807  * Blocks until all pending operations on the @surface have been
808  * completed.
809  *
810  * Return value: %TRUE on success
811  */
812 gboolean
813 gst_vaapi_surface_sync(GstVaapiSurface *surface)
814 {
815     GstVaapiDisplay *display;
816     VAStatus status;
817
818     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
819
820     display = GST_VAAPI_OBJECT_DISPLAY(surface);
821     if (!display)
822         return FALSE;
823
824     GST_VAAPI_DISPLAY_LOCK(display);
825     status = vaSyncSurface(
826         GST_VAAPI_DISPLAY_VADISPLAY(display),
827         GST_VAAPI_OBJECT_ID(surface)
828     );
829     GST_VAAPI_DISPLAY_UNLOCK(display);
830     if (!vaapi_check_status(status, "vaSyncSurface()"))
831         return FALSE;
832
833     return TRUE;
834 }
835
836 /**
837  * gst_vaapi_surface_query_status:
838  * @surface: a #GstVaapiSurface
839  * @pstatus: return location for the #GstVaapiSurfaceStatus
840  *
841  * Finds out any pending operations on the @surface. The
842  * #GstVaapiSurfaceStatus flags are returned into @pstatus.
843  *
844  * Return value: %TRUE on success
845  */
846 gboolean
847 gst_vaapi_surface_query_status(
848     GstVaapiSurface       *surface,
849     GstVaapiSurfaceStatus *pstatus
850 )
851 {
852     VASurfaceStatus surface_status;
853     VAStatus status;
854
855     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
856
857     GST_VAAPI_OBJECT_LOCK_DISPLAY(surface);
858     status = vaQuerySurfaceStatus(
859         GST_VAAPI_OBJECT_VADISPLAY(surface),
860         GST_VAAPI_OBJECT_ID(surface),
861         &surface_status
862     );
863     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(surface);
864     if (!vaapi_check_status(status, "vaQuerySurfaceStatus()"))
865         return FALSE;
866
867     if (pstatus)
868         *pstatus = to_GstVaapiSurfaceStatus(surface_status);
869     return TRUE;
870 }
871
872 /**
873  * gst_vaapi_surface_set_subpictures_from_composition:
874  * @surface: a #GstVaapiSurface
875  * @compostion: a #GstVideoOverlayCompositon
876  * @propagate_context: a flag specifying whether to apply composition
877  *     to the parent context, if any
878  *
879  * Helper to update the subpictures from #GstVideoOverlayCompositon. Sending
880  * a NULL composition will clear all the current subpictures. Note that this
881  * method will clear existing subpictures.
882  *
883  * Return value: %TRUE on success
884  */
885 gboolean
886 gst_vaapi_surface_set_subpictures_from_composition(
887     GstVaapiSurface            *surface,
888     GstVideoOverlayComposition *composition,
889     gboolean                    propagate_context
890 )
891 {
892     GstVaapiDisplay *display;
893     guint n, nb_rectangles;
894
895     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
896
897     if (propagate_context) {
898         GstVaapiContext * const context = surface->priv->parent_context;
899         if (context)
900             return gst_vaapi_context_apply_composition(context, composition);
901     }
902
903     display = GST_VAAPI_OBJECT_DISPLAY(surface);
904     if (!display)
905         return FALSE;
906
907     /* Clear current subpictures */
908     gst_vaapi_surface_destroy_subpictures(surface);
909
910     if (!composition)
911         return TRUE;
912
913     nb_rectangles = gst_video_overlay_composition_n_rectangles (composition);
914
915     /* Overlay all the rectangles cantained in the overlay composition */
916     for (n = 0; n < nb_rectangles; ++n) {
917         GstVideoOverlayRectangle *rect;
918         GstVaapiRectangle sub_rect;
919         GstVaapiSubpicture *subpicture;
920
921         rect = gst_video_overlay_composition_get_rectangle (composition, n);
922         subpicture = gst_vaapi_subpicture_new_from_overlay_rectangle (display,
923                 rect);
924
925         gst_video_overlay_rectangle_get_render_rectangle (rect,
926                 (gint *)&sub_rect.x, (gint *)&sub_rect.y,
927                 &sub_rect.width, &sub_rect.height);
928
929         if (!gst_vaapi_surface_associate_subpicture (surface, subpicture,
930                     NULL, &sub_rect)) {
931             GST_WARNING ("could not render overlay rectangle %p", rect);
932             g_object_unref (subpicture);
933             return FALSE;
934         }
935         g_object_unref (subpicture);
936     }
937     return TRUE;
938 }