Tizen 2.0 Release
[framework/multimedia/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     GstVaapiSurfaceClass * const klass = GST_VAAPI_SURFACE_GET_CLASS(object);
237     GObjectClass *parent_class;
238
239     if (klass->create)
240         klass->create(surface);
241
242     parent_class = G_OBJECT_CLASS(gst_vaapi_surface_parent_class);
243     if (parent_class->constructed)
244         parent_class->constructed(object);
245 }
246
247 static void
248 gst_vaapi_surface_class_init(GstVaapiSurfaceClass *klass)
249 {
250     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
251
252     g_type_class_add_private(klass, sizeof(GstVaapiSurfacePrivate));
253
254     object_class->finalize     = gst_vaapi_surface_finalize;
255     object_class->set_property = gst_vaapi_surface_set_property;
256     object_class->get_property = gst_vaapi_surface_get_property;
257     object_class->constructed  = gst_vaapi_surface_constructed;
258
259     klass->create = gst_vaapi_surface_create;
260
261     g_object_class_install_property
262         (object_class,
263          PROP_WIDTH,
264          g_param_spec_uint("width",
265                            "Width",
266                            "The width of the surface",
267                            0, G_MAXINT32, 0,
268                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
269
270     g_object_class_install_property
271         (object_class,
272          PROP_HEIGHT,
273          g_param_spec_uint("height",
274                            "Height",
275                            "The height of the surface",
276                            0, G_MAXINT32, 0,
277                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
278
279     g_object_class_install_property
280         (object_class,
281          PROP_CHROMA_TYPE,
282          g_param_spec_uint("chroma-type",
283                            "Chroma type",
284                            "The chroma type of the surface",
285                            0, G_MAXUINT32, 0,
286                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
287
288     g_object_class_install_property
289         (object_class,
290          PROP_PARENT_CONTEXT,
291          g_param_spec_object("parent-context",
292                              "Parent Context",
293                              "The parent context, if any",
294                              GST_VAAPI_TYPE_CONTEXT,
295                              G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
296 }
297
298 static void
299 gst_vaapi_surface_init(GstVaapiSurface *surface)
300 {
301     GstVaapiSurfacePrivate *priv = GST_VAAPI_SURFACE_GET_PRIVATE(surface);
302
303     surface->priv        = priv;
304     priv->width          = 0;
305     priv->height         = 0;
306     priv->chroma_type    = 0;
307     priv->subpictures    = NULL;
308     priv->parent_context = NULL;
309 }
310
311 /**
312  * gst_vaapi_surface_new:
313  * @display: a #GstVaapiDisplay
314  * @chroma_type: the surface chroma format
315  * @width: the requested surface width
316  * @height: the requested surface height
317  *
318  * Creates a new #GstVaapiSurface with the specified chroma format and
319  * dimensions.
320  *
321  * Return value: the newly allocated #GstVaapiSurface object
322  */
323 GstVaapiSurface *
324 gst_vaapi_surface_new(
325     GstVaapiDisplay    *display,
326     GstVaapiChromaType  chroma_type,
327     guint               width,
328     guint               height
329 )
330 {
331     GST_DEBUG("size %ux%u, chroma type 0x%x", width, height, chroma_type);
332
333     return g_object_new(GST_VAAPI_TYPE_SURFACE,
334                         "display",      display,
335                         "id",           GST_VAAPI_ID(VA_INVALID_ID),
336                         "width",        width,
337                         "height",       height,
338                         "chroma-type",  chroma_type,
339                         NULL);
340 }
341
342 /**
343  * gst_vaapi_surface_get_id:
344  * @surface: a #GstVaapiSurface
345  *
346  * Returns the underlying VASurfaceID of the @surface.
347  *
348  * Return value: the underlying VA surface id
349  */
350 GstVaapiID
351 gst_vaapi_surface_get_id(GstVaapiSurface *surface)
352 {
353     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), VA_INVALID_SURFACE);
354
355     return GST_VAAPI_OBJECT_ID(surface);
356 }
357
358 /**
359  * gst_vaapi_surface_get_chroma_type:
360  * @surface: a #GstVaapiSurface
361  *
362  * Returns the #GstVaapiChromaType the @surface was created with.
363  *
364  * Return value: the #GstVaapiChromaType
365  */
366 GstVaapiChromaType
367 gst_vaapi_surface_get_chroma_type(GstVaapiSurface *surface)
368 {
369     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), 0);
370
371     return surface->priv->chroma_type;
372 }
373
374 /**
375  * gst_vaapi_surface_get_width:
376  * @surface: a #GstVaapiSurface
377  *
378  * Returns the @surface width.
379  *
380  * Return value: the surface width, in pixels
381  */
382 guint
383 gst_vaapi_surface_get_width(GstVaapiSurface *surface)
384 {
385     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), 0);
386
387     return surface->priv->width;
388 }
389
390 /**
391  * gst_vaapi_surface_get_height:
392  * @surface: a #GstVaapiSurface
393  *
394  * Returns the @surface height.
395  *
396  * Return value: the surface height, in pixels.
397  */
398 guint
399 gst_vaapi_surface_get_height(GstVaapiSurface *surface)
400 {
401     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), 0);
402
403     return surface->priv->height;
404 }
405
406 /**
407  * gst_vaapi_surface_get_size:
408  * @surface: a #GstVaapiSurface
409  * @pwidth: return location for the width, or %NULL
410  * @pheight: return location for the height, or %NULL
411  *
412  * Retrieves the dimensions of a #GstVaapiSurface.
413  */
414 void
415 gst_vaapi_surface_get_size(
416     GstVaapiSurface *surface,
417     guint           *pwidth,
418     guint           *pheight
419 )
420 {
421     g_return_if_fail(GST_VAAPI_IS_SURFACE(surface));
422
423     if (pwidth)
424         *pwidth = gst_vaapi_surface_get_width(surface);
425
426     if (pheight)
427         *pheight = gst_vaapi_surface_get_height(surface);
428 }
429
430 /**
431  * gst_vaapi_surface_set_parent_context:
432  * @surface: a #GstVaapiSurface
433  * @context: a #GstVaapiContext
434  *
435  * Sets new parent context, or clears any parent context if @context
436  * is %NULL. This function owns an extra reference to the context,
437  * which will be released when the surface is destroyed.
438  */
439 void
440 gst_vaapi_surface_set_parent_context(
441     GstVaapiSurface *surface,
442     GstVaapiContext *context
443 )
444 {
445     GstVaapiSurfacePrivate *priv;
446
447     g_return_if_fail(GST_VAAPI_IS_SURFACE(surface));
448
449     priv = surface->priv;
450
451     g_clear_object(&priv->parent_context);
452
453     if (context)
454         priv->parent_context = g_object_ref(context);
455 }
456
457 /**
458  * gst_vaapi_surface_get_parent_context:
459  * @surface: a #GstVaapiSurface
460  *
461  * Retrieves the parent #GstVaapiContext, or %NULL if there is
462  * none. The surface shall still own a reference to the context.
463  * i.e. the caller shall not unreference the returned context object.
464  *
465  * Return value: the parent context, if any.
466  */
467 GstVaapiContext *
468 gst_vaapi_surface_get_parent_context(GstVaapiSurface *surface)
469 {
470     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), NULL);
471
472     return surface->priv->parent_context;
473 }
474
475 /**
476  * gst_vaapi_surface_derive_image:
477  * @surface: a #GstVaapiSurface
478  *
479  * Derives a #GstVaapiImage from the @surface. This image buffer can
480  * then be mapped/unmapped for direct CPU access. This operation is
481  * only possible if the underlying implementation supports direct
482  * rendering capabilities and internal surface formats that can be
483  * represented with a #GstVaapiImage.
484  *
485  * When the operation is not possible, the function returns %NULL and
486  * the user should then fallback to using gst_vaapi_surface_get_image()
487  * or gst_vaapi_surface_put_image() to accomplish the same task in an
488  * indirect manner (additional copy).
489  *
490  * An image created with gst_vaapi_surface_derive_image() should be
491  * unreferenced when it's no longer needed. The image and image buffer
492  * data structures will be destroyed. However, the surface contents
493  * will remain unchanged until destroyed through the last call to
494  * g_object_unref().
495  *
496  * Return value: the newly allocated #GstVaapiImage object, or %NULL
497  *   on failure
498  */
499 GstVaapiImage *
500 gst_vaapi_surface_derive_image(GstVaapiSurface *surface)
501 {
502     GstVaapiDisplay *display;
503     VAImage va_image;
504     VAStatus status;
505
506     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), NULL);
507
508     display           = GST_VAAPI_OBJECT_DISPLAY(surface);
509     va_image.image_id = VA_INVALID_ID;
510     va_image.buf      = VA_INVALID_ID;
511
512     GST_VAAPI_DISPLAY_LOCK(display);
513     status = vaDeriveImage(
514         GST_VAAPI_DISPLAY_VADISPLAY(display),
515         GST_VAAPI_OBJECT_ID(surface),
516         &va_image
517     );
518     GST_VAAPI_DISPLAY_UNLOCK(display);
519     if (!vaapi_check_status(status, "vaDeriveImage()"))
520         return NULL;
521     if (va_image.image_id == VA_INVALID_ID || va_image.buf == VA_INVALID_ID)
522         return NULL;
523
524     return gst_vaapi_image_new_with_image(display, &va_image);
525 }
526
527 /**
528  * gst_vaapi_surface_get_image
529  * @surface: a #GstVaapiSurface
530  * @image: a #GstVaapiImage
531  *
532  * Retrieves surface data into a #GstVaapiImage. The @image must have
533  * a format supported by the @surface.
534  *
535  * Return value: %TRUE on success
536  */
537 gboolean
538 gst_vaapi_surface_get_image(GstVaapiSurface *surface, GstVaapiImage *image)
539 {
540     GstVaapiDisplay *display;
541     VAImageID image_id;
542     VAStatus status;
543     guint width, height;
544
545     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
546     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
547
548     display = GST_VAAPI_OBJECT_DISPLAY(surface);
549     if (!display)
550         return FALSE;
551
552     gst_vaapi_image_get_size(image, &width, &height);
553     if (width != surface->priv->width || height != surface->priv->height)
554         return FALSE;
555
556     image_id = GST_VAAPI_OBJECT_ID(image);
557     if (image_id == VA_INVALID_ID)
558         return FALSE;
559
560     GST_VAAPI_DISPLAY_LOCK(display);
561     status = vaGetImage(
562         GST_VAAPI_DISPLAY_VADISPLAY(display),
563         GST_VAAPI_OBJECT_ID(surface),
564         0, 0, width, height,
565         image_id
566     );
567     GST_VAAPI_DISPLAY_UNLOCK(display);
568     if (!vaapi_check_status(status, "vaGetImage()"))
569         return FALSE;
570
571     return TRUE;
572 }
573
574 /**
575  * gst_vaapi_surface_put_image:
576  * @surface: a #GstVaapiSurface
577  * @image: a #GstVaapiImage
578  *
579  * Copies data from a #GstVaapiImage into a @surface. The @image must
580  * have a format supported by the @surface.
581  *
582  * Return value: %TRUE on success
583  */
584 gboolean
585 gst_vaapi_surface_put_image(GstVaapiSurface *surface, GstVaapiImage *image)
586 {
587     GstVaapiDisplay *display;
588     VAImageID image_id;
589     VAStatus status;
590     guint width, height;
591
592     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
593     g_return_val_if_fail(GST_VAAPI_IS_IMAGE(image), FALSE);
594
595     display = GST_VAAPI_OBJECT_DISPLAY(surface);
596     if (!display)
597         return FALSE;
598
599     gst_vaapi_image_get_size(image, &width, &height);
600     if (width != surface->priv->width || height != surface->priv->height)
601         return FALSE;
602
603     image_id = GST_VAAPI_OBJECT_ID(image);
604     if (image_id == VA_INVALID_ID)
605         return FALSE;
606
607     GST_VAAPI_DISPLAY_LOCK(display);
608     status = vaPutImage(
609         GST_VAAPI_DISPLAY_VADISPLAY(display),
610         GST_VAAPI_OBJECT_ID(surface),
611         image_id,
612         0, 0, width, height,
613         0, 0, width, height
614     );
615     GST_VAAPI_DISPLAY_UNLOCK(display);
616     if (!vaapi_check_status(status, "vaPutImage()"))
617         return FALSE;
618
619     return TRUE;
620 }
621
622 /**
623  * gst_vaapi_surface_associate_subpicture:
624  * @surface: a #GstVaapiSurface
625  * @subpicture: a #GstVaapiSubpicture
626  * @src_rect: the sub-rectangle of the source subpicture
627  *   image to extract and process. If %NULL, the entire image will be used.
628  * @dst_rect: the sub-rectangle of the destination
629  *   surface into which the image is rendered. If %NULL, the entire
630  *   surface will be used.
631  *
632  * Associates the @subpicture with the @surface. The @src_rect
633  * coordinates and size are relative to the source image bound to
634  * @subpicture. The @dst_rect coordinates and size are relative to the
635  * target @surface. Note that the @surface holds an additional
636  * reference to the @subpicture.
637  *
638  * Return value: %TRUE on success
639  */
640 gboolean
641 gst_vaapi_surface_associate_subpicture(
642     GstVaapiSurface         *surface,
643     GstVaapiSubpicture      *subpicture,
644     const GstVaapiRectangle *src_rect,
645     const GstVaapiRectangle *dst_rect
646 )
647 {
648     gboolean success;
649
650     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
651     g_return_val_if_fail(GST_VAAPI_IS_SUBPICTURE(subpicture), FALSE);
652
653     if (!surface->priv->subpictures) {
654         surface->priv->subpictures = g_ptr_array_new();
655         if (!surface->priv->subpictures)
656             return FALSE;
657     }
658
659     if (g_ptr_array_remove_fast(surface->priv->subpictures, subpicture)) {
660         success = _gst_vaapi_surface_deassociate_subpicture(surface, subpicture);
661         g_object_unref(subpicture);
662         if (!success)
663             return FALSE;
664     }
665
666     success = _gst_vaapi_surface_associate_subpicture(
667         surface,
668         subpicture,
669         src_rect,
670         dst_rect
671     );
672     if (!success)
673         return FALSE;
674
675     g_ptr_array_add(surface->priv->subpictures, g_object_ref(subpicture));
676     return TRUE;
677 }
678
679 gboolean
680 _gst_vaapi_surface_associate_subpicture(
681     GstVaapiSurface         *surface,
682     GstVaapiSubpicture      *subpicture,
683     const GstVaapiRectangle *src_rect,
684     const GstVaapiRectangle *dst_rect
685 )
686 {
687     GstVaapiDisplay *display;
688     GstVaapiRectangle src_rect_default, dst_rect_default;
689     GstVaapiImage *image;
690     VASurfaceID surface_id;
691     VAStatus status;
692
693     display = GST_VAAPI_OBJECT_DISPLAY(surface);
694     if (!display)
695         return FALSE;
696
697     surface_id = GST_VAAPI_OBJECT_ID(surface);
698     if (surface_id == VA_INVALID_SURFACE)
699         return FALSE;
700
701     if (!src_rect) {
702         image = gst_vaapi_subpicture_get_image(subpicture);
703         if (!image)
704             return FALSE;
705         src_rect                = &src_rect_default;
706         src_rect_default.x      = 0;
707         src_rect_default.y      = 0;
708         gst_vaapi_image_get_size(
709             image,
710             &src_rect_default.width,
711             &src_rect_default.height
712         );
713     }
714
715     if (!dst_rect) {
716         dst_rect                = &dst_rect_default;
717         dst_rect_default.x      = 0;
718         dst_rect_default.y      = 0;
719         dst_rect_default.width  = surface->priv->width;
720         dst_rect_default.height = surface->priv->height;
721     }
722
723     GST_VAAPI_DISPLAY_LOCK(display);
724     status = vaAssociateSubpicture(
725         GST_VAAPI_DISPLAY_VADISPLAY(display),
726         GST_VAAPI_OBJECT_ID(subpicture),
727         &surface_id, 1,
728         src_rect->x, src_rect->y, src_rect->width, src_rect->height,
729         dst_rect->x, dst_rect->y, dst_rect->width, dst_rect->height,
730         0
731     );
732     GST_VAAPI_DISPLAY_UNLOCK(display);
733     if (!vaapi_check_status(status, "vaAssociateSubpicture()"))
734         return FALSE;
735
736     return TRUE;
737 }
738
739 /**
740  * gst_vaapi_surface_deassociate_subpicture:
741  * @surface: a #GstVaapiSurface
742  * @subpicture: a #GstVaapiSubpicture
743  *
744  * Deassociates @subpicture from @surface. Other associations are kept.
745  *
746  * Return value: %TRUE on success
747  */
748 gboolean
749 gst_vaapi_surface_deassociate_subpicture(
750     GstVaapiSurface         *surface,
751     GstVaapiSubpicture      *subpicture
752 )
753 {
754     gboolean success;
755
756     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
757     g_return_val_if_fail(GST_VAAPI_IS_SUBPICTURE(subpicture), FALSE);
758
759     if (!surface->priv->subpictures)
760         return TRUE;
761
762     /* First, check subpicture was really associated with this surface */
763     if (!g_ptr_array_remove_fast(surface->priv->subpictures, subpicture)) {
764         GST_DEBUG("subpicture %" GST_VAAPI_ID_FORMAT " was not bound to "
765                   "surface %" GST_VAAPI_ID_FORMAT,
766                   GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(subpicture)),
767                   GST_VAAPI_ID_ARGS(GST_VAAPI_OBJECT_ID(surface)));
768         return TRUE;
769     }
770
771     success = _gst_vaapi_surface_deassociate_subpicture(surface, subpicture);
772     g_object_unref(subpicture);
773     return success;
774 }
775
776 gboolean
777 _gst_vaapi_surface_deassociate_subpicture(
778     GstVaapiSurface         *surface,
779     GstVaapiSubpicture      *subpicture
780 )
781 {
782     GstVaapiDisplay *display;
783     VASurfaceID surface_id;
784     VAStatus status;
785
786     display = GST_VAAPI_OBJECT_DISPLAY(surface);
787     if (!display)
788         return FALSE;
789
790     surface_id = GST_VAAPI_OBJECT_ID(surface);
791     if (surface_id == VA_INVALID_SURFACE)
792         return FALSE;
793
794     GST_VAAPI_DISPLAY_LOCK(display);
795     status = vaDeassociateSubpicture(
796         GST_VAAPI_DISPLAY_VADISPLAY(display),
797         GST_VAAPI_OBJECT_ID(subpicture),
798         &surface_id, 1
799     );
800     GST_VAAPI_DISPLAY_UNLOCK(display);
801     if (!vaapi_check_status(status, "vaDeassociateSubpicture()"))
802         return FALSE;
803
804     return TRUE;
805 }
806
807 /**
808  * gst_vaapi_surface_sync:
809  * @surface: a #GstVaapiSurface
810  *
811  * Blocks until all pending operations on the @surface have been
812  * completed.
813  *
814  * Return value: %TRUE on success
815  */
816 gboolean
817 gst_vaapi_surface_sync(GstVaapiSurface *surface)
818 {
819     GstVaapiDisplay *display;
820     VAStatus status;
821
822     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
823
824     display = GST_VAAPI_OBJECT_DISPLAY(surface);
825     if (!display)
826         return FALSE;
827
828     GST_VAAPI_DISPLAY_LOCK(display);
829     status = vaSyncSurface(
830         GST_VAAPI_DISPLAY_VADISPLAY(display),
831         GST_VAAPI_OBJECT_ID(surface)
832     );
833     GST_VAAPI_DISPLAY_UNLOCK(display);
834     if (!vaapi_check_status(status, "vaSyncSurface()"))
835         return FALSE;
836
837     return TRUE;
838 }
839
840 /**
841  * gst_vaapi_surface_query_status:
842  * @surface: a #GstVaapiSurface
843  * @pstatus: return location for the #GstVaapiSurfaceStatus
844  *
845  * Finds out any pending operations on the @surface. The
846  * #GstVaapiSurfaceStatus flags are returned into @pstatus.
847  *
848  * Return value: %TRUE on success
849  */
850 gboolean
851 gst_vaapi_surface_query_status(
852     GstVaapiSurface       *surface,
853     GstVaapiSurfaceStatus *pstatus
854 )
855 {
856     VASurfaceStatus surface_status;
857     VAStatus status;
858
859     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
860
861     GST_VAAPI_OBJECT_LOCK_DISPLAY(surface);
862     status = vaQuerySurfaceStatus(
863         GST_VAAPI_OBJECT_VADISPLAY(surface),
864         GST_VAAPI_OBJECT_ID(surface),
865         &surface_status
866     );
867     GST_VAAPI_OBJECT_UNLOCK_DISPLAY(surface);
868     if (!vaapi_check_status(status, "vaQuerySurfaceStatus()"))
869         return FALSE;
870
871     if (pstatus)
872         *pstatus = to_GstVaapiSurfaceStatus(surface_status);
873     return TRUE;
874 }
875
876 /**
877  * gst_vaapi_surface_set_subpictures_from_composition:
878  * @surface: a #GstVaapiSurface
879  * @compostion: a #GstVideoOverlayCompositon
880  * @propagate_context: a flag specifying whether to apply composition
881  *     to the parent context, if any
882  *
883  * Helper to update the subpictures from #GstVideoOverlayCompositon. Sending
884  * a NULL composition will clear all the current subpictures. Note that this
885  * method will clear existing subpictures.
886  *
887  * Return value: %TRUE on success
888  */
889 gboolean
890 gst_vaapi_surface_set_subpictures_from_composition(
891     GstVaapiSurface            *surface,
892     GstVideoOverlayComposition *composition,
893     gboolean                    propagate_context
894 )
895 {
896     GstVaapiDisplay *display;
897     guint n, nb_rectangles;
898
899     g_return_val_if_fail(GST_VAAPI_IS_SURFACE(surface), FALSE);
900
901     if (propagate_context) {
902         GstVaapiContext * const context = surface->priv->parent_context;
903         if (context)
904             return gst_vaapi_context_apply_composition(context, composition);
905     }
906
907     display = GST_VAAPI_OBJECT_DISPLAY(surface);
908     if (!display)
909         return FALSE;
910
911     /* Clear current subpictures */
912     gst_vaapi_surface_destroy_subpictures(surface);
913
914     if (!composition)
915         return TRUE;
916
917     nb_rectangles = gst_video_overlay_composition_n_rectangles (composition);
918
919     /* Overlay all the rectangles cantained in the overlay composition */
920     for (n = 0; n < nb_rectangles; ++n) {
921         GstVideoOverlayRectangle *rect;
922         GstVaapiRectangle sub_rect;
923         GstVaapiSubpicture *subpicture;
924
925         rect = gst_video_overlay_composition_get_rectangle (composition, n);
926         subpicture = gst_vaapi_subpicture_new_from_overlay_rectangle (display,
927                 rect);
928
929         gst_video_overlay_rectangle_get_render_rectangle (rect,
930                 (gint *)&sub_rect.x, (gint *)&sub_rect.y,
931                 &sub_rect.width, &sub_rect.height);
932
933         if (!gst_vaapi_surface_associate_subpicture (surface, subpicture,
934                     NULL, &sub_rect)) {
935             GST_WARNING ("could not render overlay rectangle %p", rect);
936             g_object_unref (subpicture);
937             return FALSE;
938         }
939         g_object_unref (subpicture);
940     }
941     return TRUE;
942 }