9a5f11680b684c0f553e69fe32815060e2f95310
[profile/ivi/gstreamer-vaapi.git] / gst-libs / gst / vaapi / gstvaapicontext.c
1 /*
2  *  gstvaapicontext.c - VA context abstraction
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011-2012 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:gstvaapicontext
25  * @short_description: VA context abstraction
26  */
27
28 #include "sysdeps.h"
29 #include <assert.h>
30 #include "gstvaapicompat.h"
31 #include "gstvaapicontext.h"
32 #include "gstvaapisurface.h"
33 #include "gstvaapisurface_priv.h"
34 #include "gstvaapisurfacepool.h"
35 #include "gstvaapiimage.h"
36 #include "gstvaapisubpicture.h"
37 #include "gstvaapiutils.h"
38 #include "gstvaapi_priv.h"
39
40 #define DEBUG 1
41 #include "gstvaapidebug.h"
42
43 G_DEFINE_TYPE(GstVaapiContext, gst_vaapi_context, GST_VAAPI_TYPE_OBJECT)
44
45 #define GST_VAAPI_CONTEXT_GET_PRIVATE(obj)                      \
46     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                         \
47                                  GST_VAAPI_TYPE_CONTEXT,        \
48                                  GstVaapiContextPrivate))
49
50 typedef struct _GstVaapiOverlayRectangle GstVaapiOverlayRectangle;
51 struct _GstVaapiOverlayRectangle {
52     GstVaapiContext    *context;
53     GstVaapiSubpicture *subpicture;
54     GstVaapiRectangle   rect;
55     guint               seq_num;
56 };
57
58 /* XXX: optimize for the effective number of reference frames */
59 struct _GstVaapiContextPrivate {
60     VAConfigID          config_id;
61     GPtrArray          *surfaces;
62     GstVaapiVideoPool  *surfaces_pool;
63     GPtrArray          *overlay;
64     GstVaapiProfile     profile;
65     GstVaapiEntrypoint  entrypoint;
66     guint               width;
67     guint               height;
68     guint               ref_frames;
69     guint               is_constructed  : 1;
70 };
71
72 enum {
73     PROP_0,
74
75     PROP_PROFILE,
76     PROP_ENTRYPOINT,
77     PROP_WIDTH,
78     PROP_HEIGHT,
79     PROP_REF_FRAMES
80 };
81
82 static guint
83 get_max_ref_frames(GstVaapiProfile profile)
84 {
85     guint ref_frames;
86
87     switch (gst_vaapi_profile_get_codec(profile)) {
88     case GST_VAAPI_CODEC_H264:  ref_frames = 16; break;
89     case GST_VAAPI_CODEC_JPEG:  ref_frames =  0; break;
90     default:                    ref_frames =  2; break;
91     }
92     return ref_frames;
93 }
94
95 static GstVaapiOverlayRectangle *
96 overlay_rectangle_new(GstVaapiContext *context)
97 {
98     GstVaapiOverlayRectangle *overlay;
99
100     overlay = g_slice_new0(GstVaapiOverlayRectangle);
101     if (!overlay)
102         return NULL;
103
104     overlay->context = context;
105     return overlay;
106 }
107
108 static void
109 overlay_rectangle_destroy(GstVaapiOverlayRectangle *overlay)
110 {
111     GstVaapiContextPrivate *priv;
112     guint i;
113
114     if (!overlay)
115         return;
116     priv = overlay->context->priv;
117
118     if (overlay->subpicture) {
119         if (priv->surfaces) {
120             GstVaapiSubpicture * const subpicture = overlay->subpicture;
121             for (i = 0; i < priv->surfaces->len; i++) {
122                 GstVaapiSurface * const surface =
123                     g_ptr_array_index(priv->surfaces, i);
124                 gst_vaapi_surface_deassociate_subpicture(surface, subpicture);
125             }
126         }
127         g_object_unref(overlay->subpicture);
128         overlay->subpicture = NULL;
129     }
130     g_slice_free(GstVaapiOverlayRectangle, overlay);
131 }
132
133 static void
134 destroy_overlay_cb(gpointer data, gpointer user_data)
135 {
136     GstVaapiOverlayRectangle * const overlay = data;
137
138     overlay_rectangle_destroy(overlay);
139 }
140
141 static void
142 gst_vaapi_context_destroy_overlay(GstVaapiContext *context)
143 {
144     GstVaapiContextPrivate * const priv = context->priv;
145
146     if (!priv->overlay)
147         return;
148
149     g_ptr_array_foreach(priv->overlay, destroy_overlay_cb, priv);
150     g_ptr_array_free(priv->overlay, TRUE);
151     priv->overlay = NULL;
152 }
153
154 static void
155 unref_surface_cb(gpointer data, gpointer user_data)
156 {
157     GstVaapiSurface * const surface = GST_VAAPI_SURFACE(data);
158
159     gst_vaapi_surface_set_parent_context(surface, NULL);
160     g_object_unref(surface);
161 }
162
163 static void
164 gst_vaapi_context_destroy_surfaces(GstVaapiContext *context)
165 {
166     GstVaapiContextPrivate * const priv = context->priv;
167
168     gst_vaapi_context_destroy_overlay(context);
169
170     if (priv->surfaces) {
171         g_ptr_array_foreach(priv->surfaces, unref_surface_cb, NULL);
172         g_ptr_array_free(priv->surfaces, TRUE);
173         priv->surfaces = NULL;
174     }
175
176     g_clear_object(&priv->surfaces_pool);
177 }
178
179 static void
180 gst_vaapi_context_destroy(GstVaapiContext *context)
181 {
182     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(context);
183     GstVaapiContextPrivate * const priv = context->priv;
184     VAContextID context_id;
185     VAStatus status;
186
187     context_id = GST_VAAPI_OBJECT_ID(context);
188     GST_DEBUG("context %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(context_id));
189
190     if (context_id != VA_INVALID_ID) {
191         GST_VAAPI_DISPLAY_LOCK(display);
192         status = vaDestroyContext(
193             GST_VAAPI_DISPLAY_VADISPLAY(display),
194             context_id
195         );
196         GST_VAAPI_DISPLAY_UNLOCK(display);
197         if (!vaapi_check_status(status, "vaDestroyContext()"))
198             g_warning("failed to destroy context %" GST_VAAPI_ID_FORMAT,
199                       GST_VAAPI_ID_ARGS(context_id));
200         GST_VAAPI_OBJECT_ID(context) = VA_INVALID_ID;
201     }
202
203     if (priv->config_id != VA_INVALID_ID) {
204         GST_VAAPI_DISPLAY_LOCK(display);
205         status = vaDestroyConfig(
206             GST_VAAPI_DISPLAY_VADISPLAY(display),
207             priv->config_id
208         );
209         GST_VAAPI_DISPLAY_UNLOCK(display);
210         if (!vaapi_check_status(status, "vaDestroyConfig()"))
211             g_warning("failed to destroy config %" GST_VAAPI_ID_FORMAT,
212                       GST_VAAPI_ID_ARGS(priv->config_id));
213         priv->config_id = VA_INVALID_ID;
214     }
215 }
216
217 static gboolean
218 gst_vaapi_context_create_overlay(GstVaapiContext *context)
219 {
220     GstVaapiContextPrivate * const priv = context->priv;
221
222     if (!priv->overlay) {
223         priv->overlay = g_ptr_array_new();
224         if (!priv->overlay)
225             return FALSE;
226     }
227     return TRUE;
228 }
229
230 static gboolean
231 gst_vaapi_context_create_surfaces(GstVaapiContext *context)
232 {
233     GstVaapiContextPrivate * const priv = context->priv;
234     GstCaps *caps;
235     GstVaapiSurface *surface;
236     guint i, num_surfaces;
237
238     /* Number of scratch surfaces beyond those used as reference */
239     const guint SCRATCH_SURFACES_COUNT = 4;
240
241     if (!gst_vaapi_context_create_overlay(context))
242         return FALSE;
243
244     if (!priv->surfaces) {
245         priv->surfaces = g_ptr_array_new();
246         if (!priv->surfaces)
247             return FALSE;
248     }
249
250     if (!priv->surfaces_pool) {
251         caps = gst_caps_new_simple(
252             GST_VAAPI_SURFACE_CAPS_NAME,
253             "type", G_TYPE_STRING, "vaapi",
254             "width",  G_TYPE_INT, priv->width,
255             "height", G_TYPE_INT, priv->height,
256             NULL
257         );
258         if (!caps)
259             return FALSE;
260         priv->surfaces_pool = gst_vaapi_surface_pool_new(
261             GST_VAAPI_OBJECT_DISPLAY(context),
262             caps
263         );
264         gst_caps_unref(caps);
265         if (!priv->surfaces_pool)
266             return FALSE;
267     }
268
269     num_surfaces = priv->ref_frames + SCRATCH_SURFACES_COUNT;
270     gst_vaapi_video_pool_set_capacity(priv->surfaces_pool, num_surfaces);
271
272     for (i = priv->surfaces->len; i < num_surfaces; i++) {
273         surface = gst_vaapi_surface_new(
274             GST_VAAPI_OBJECT_DISPLAY(context),
275             GST_VAAPI_CHROMA_TYPE_YUV420,
276             priv->width, priv->height
277         );
278         if (!surface)
279             return FALSE;
280         g_ptr_array_add(priv->surfaces, surface);
281         if (!gst_vaapi_video_pool_add_object(priv->surfaces_pool, surface))
282             return FALSE;
283     }
284     return TRUE;
285 }
286
287 static gboolean
288 gst_vaapi_context_create(GstVaapiContext *context)
289 {
290     GstVaapiDisplay * const display = GST_VAAPI_OBJECT_DISPLAY(context);
291     GstVaapiContextPrivate * const priv = context->priv;
292     VAProfile va_profile;
293     VAEntrypoint va_entrypoint;
294     VAConfigAttrib attribs[2];
295     guint attribs_num;
296     VAContextID context_id;
297     VASurfaceID surface_id;
298     VAStatus status;
299     GArray *surfaces = NULL;
300     gboolean success = FALSE;
301     guint i;
302
303     if (!priv->surfaces && !gst_vaapi_context_create_surfaces(context))
304         goto end;
305
306     surfaces = g_array_sized_new(
307         FALSE,
308         FALSE,
309         sizeof(VASurfaceID),
310         priv->surfaces->len
311     );
312     if (!surfaces)
313         goto end;
314
315     for (i = 0; i < priv->surfaces->len; i++) {
316         GstVaapiSurface * const surface = g_ptr_array_index(priv->surfaces, i);
317         if (!surface)
318             goto end;
319         surface_id = GST_VAAPI_OBJECT_ID(surface);
320         g_array_append_val(surfaces, surface_id);
321     }
322     assert(surfaces->len == priv->surfaces->len);
323
324     if (!priv->profile || !priv->entrypoint)
325         goto end;
326     va_profile    = gst_vaapi_profile_get_va_profile(priv->profile);
327     va_entrypoint = gst_vaapi_entrypoint_get_va_entrypoint(priv->entrypoint);
328
329     GST_VAAPI_DISPLAY_LOCK(display);
330     attribs[0].type = VAConfigAttribRTFormat;
331     attribs[1].type = VAConfigAttribRateControl;
332     if (VAEntrypointEncSlice == va_entrypoint)
333       attribs_num = 2;
334     else
335       attribs_num = 1;
336
337     status = vaGetConfigAttributes(
338         GST_VAAPI_DISPLAY_VADISPLAY(display),
339         va_profile,
340         va_entrypoint,
341         attribs, attribs_num
342     );
343     GST_VAAPI_DISPLAY_UNLOCK(display);
344     if (!vaapi_check_status(status, "vaGetConfigAttributes()"))
345         goto end;
346     if (!(attribs[0].value & VA_RT_FORMAT_YUV420))
347         goto end;
348     if (attribs_num > 1 && !(attribs[1].value & VA_RC_NONE)
349                         && !(attribs[1].value & VA_RC_CBR)
350                         && !(attribs[1].value & VA_RC_VBR))
351         goto end;
352
353     GST_VAAPI_DISPLAY_LOCK(display);
354     status = vaCreateConfig(
355         GST_VAAPI_DISPLAY_VADISPLAY(display),
356         va_profile,
357         va_entrypoint,
358         attribs, attribs_num,
359         &priv->config_id
360     );
361     GST_VAAPI_DISPLAY_UNLOCK(display);
362     if (!vaapi_check_status(status, "vaCreateConfig()"))
363         goto end;
364
365     VASurfaceID *surface_ids = (VASurfaceID*)surfaces->data;
366     int          surface_num = surfaces->len;
367
368     if (VAEntrypointEncSlice == va_entrypoint) {
369         surface_ids = NULL;
370         surface_num = 0;
371     }
372
373     GST_VAAPI_DISPLAY_LOCK(display);
374     status = vaCreateContext(
375         GST_VAAPI_DISPLAY_VADISPLAY(display),
376         priv->config_id,
377         priv->width, priv->height,
378         VA_PROGRESSIVE,
379         (VASurfaceID *)surface_ids, surface_num,
380         &context_id
381     );
382     GST_VAAPI_DISPLAY_UNLOCK(display);
383     if (!vaapi_check_status(status, "vaCreateContext()"))
384         goto end;
385
386     GST_DEBUG("context %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS(context_id));
387     GST_VAAPI_OBJECT_ID(context) = context_id;
388     success = TRUE;
389 end:
390     if (surfaces)
391         g_array_free(surfaces, TRUE);
392     return success;
393 }
394
395 static void
396 gst_vaapi_context_finalize(GObject *object)
397 {
398     GstVaapiContext * const context = GST_VAAPI_CONTEXT(object);
399
400     gst_vaapi_context_destroy(context);
401     gst_vaapi_context_destroy_surfaces(context);
402
403     G_OBJECT_CLASS(gst_vaapi_context_parent_class)->finalize(object);
404 }
405
406 static void
407 gst_vaapi_context_set_property(
408     GObject      *object,
409     guint         prop_id,
410     const GValue *value,
411     GParamSpec   *pspec
412 )
413 {
414     GstVaapiContext        * const context = GST_VAAPI_CONTEXT(object);
415     GstVaapiContextPrivate * const priv    = context->priv;
416
417     switch (prop_id) {
418     case PROP_PROFILE:
419         gst_vaapi_context_set_profile(context, g_value_get_uint(value));
420         break;
421     case PROP_ENTRYPOINT:
422         priv->entrypoint = g_value_get_uint(value);
423         break;
424     case PROP_WIDTH:
425         priv->width = g_value_get_uint(value);
426         break;
427     case PROP_HEIGHT:
428         priv->height = g_value_get_uint(value);
429         break;
430     case PROP_REF_FRAMES:
431         priv->ref_frames = g_value_get_uint(value);
432         break;
433     default:
434         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
435         break;
436     }
437 }
438
439 static void
440 gst_vaapi_context_get_property(
441     GObject    *object,
442     guint       prop_id,
443     GValue     *value,
444     GParamSpec *pspec
445 )
446 {
447     GstVaapiContext        * const context = GST_VAAPI_CONTEXT(object);
448     GstVaapiContextPrivate * const priv    = context->priv;
449
450     switch (prop_id) {
451     case PROP_PROFILE:
452         g_value_set_uint(value, gst_vaapi_context_get_profile(context));
453         break;
454     case PROP_ENTRYPOINT:
455         g_value_set_uint(value, gst_vaapi_context_get_entrypoint(context));
456         break;
457     case PROP_WIDTH:
458         g_value_set_uint(value, priv->width);
459         break;
460     case PROP_HEIGHT:
461         g_value_set_uint(value, priv->height);
462         break;
463     case PROP_REF_FRAMES:
464         g_value_set_uint(value, priv->ref_frames);
465         break;
466     default:
467         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
468         break;
469     }
470 }
471
472 static void
473 gst_vaapi_context_class_init(GstVaapiContextClass *klass)
474 {
475     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
476
477     g_type_class_add_private(klass, sizeof(GstVaapiContextPrivate));
478
479     object_class->finalize     = gst_vaapi_context_finalize;
480     object_class->set_property = gst_vaapi_context_set_property;
481     object_class->get_property = gst_vaapi_context_get_property;
482
483     g_object_class_install_property
484         (object_class,
485          PROP_PROFILE,
486          g_param_spec_uint("profile",
487                            "Profile",
488                            "The profile used for decoding",
489                            0, G_MAXUINT32, 0,
490                            G_PARAM_READWRITE));
491
492     g_object_class_install_property
493         (object_class,
494          PROP_ENTRYPOINT,
495          g_param_spec_uint("entrypoint",
496                            "Entrypoint",
497                            "The decoder entrypoint",
498                            0, G_MAXUINT32, 0,
499                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
500
501     g_object_class_install_property
502         (object_class,
503          PROP_WIDTH,
504          g_param_spec_uint("width",
505                            "Width",
506                            "The width of decoded surfaces",
507                            0, G_MAXINT32, 0,
508                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
509
510     g_object_class_install_property
511         (object_class,
512          PROP_HEIGHT,
513          g_param_spec_uint("height",
514                            "Height",
515                            "The height of the decoded surfaces",
516                            0, G_MAXINT32, 0,
517                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
518
519     g_object_class_install_property
520         (object_class,
521          PROP_REF_FRAMES,
522          g_param_spec_uint("ref-frames",
523                            "Reference Frames",
524                            "The number of reference frames",
525                            0, G_MAXINT32, 0,
526                            G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
527 }
528
529 static void
530 gst_vaapi_context_init(GstVaapiContext *context)
531 {
532     GstVaapiContextPrivate *priv = GST_VAAPI_CONTEXT_GET_PRIVATE(context);
533
534     context->priv       = priv;
535     priv->config_id     = VA_INVALID_ID;
536     priv->surfaces      = NULL;
537     priv->surfaces_pool = NULL;
538     priv->overlay       = NULL;
539     priv->profile       = 0;
540     priv->entrypoint    = 0;
541     priv->width         = 0;
542     priv->height        = 0;
543     priv->ref_frames    = 0;
544 }
545
546 /**
547  * gst_vaapi_context_new:
548  * @display: a #GstVaapiDisplay
549  * @profile: a #GstVaapiProfile
550  * @entrypoint: a #GstVaapiEntrypoint
551  * @width: coded width from the bitstream
552  * @height: coded height from the bitstream
553  *
554  * Creates a new #GstVaapiContext with the specified codec @profile
555  * and @entrypoint.
556  *
557  * Return value: the newly allocated #GstVaapiContext object
558  */
559 GstVaapiContext *
560 gst_vaapi_context_new(
561     GstVaapiDisplay    *display,
562     GstVaapiProfile     profile,
563     GstVaapiEntrypoint  entrypoint,
564     guint               width,
565     guint               height
566 )
567 {
568     GstVaapiContextInfo info;
569
570     info.profile    = profile;
571     info.entrypoint = entrypoint;
572     info.width      = width;
573     info.height     = height;
574     info.ref_frames = get_max_ref_frames(profile);
575     return gst_vaapi_context_new_full(display, &info);
576 }
577
578 /**
579  * gst_vaapi_context_new_full:
580  * @display: a #GstVaapiDisplay
581  * @cip: a pointer to the #GstVaapiContextInfo
582  *
583  * Creates a new #GstVaapiContext with the configuration specified by
584  * @cip, thus including profile, entry-point, encoded size and maximum
585  * number of reference frames reported by the bitstream.
586  *
587  * Return value: the newly allocated #GstVaapiContext object
588  */
589 GstVaapiContext *
590 gst_vaapi_context_new_full(GstVaapiDisplay *display, GstVaapiContextInfo *cip)
591 {
592     GstVaapiContext *context;
593
594     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), NULL);
595     g_return_val_if_fail(cip->profile, NULL);
596     g_return_val_if_fail(cip->entrypoint, NULL);
597     g_return_val_if_fail(cip->width > 0, NULL);
598     g_return_val_if_fail(cip->height > 0, NULL);
599
600     context = g_object_new(
601         GST_VAAPI_TYPE_CONTEXT,
602         "display",      display,
603         "id",           GST_VAAPI_ID(VA_INVALID_ID),
604         "profile",      cip->profile,
605         "entrypoint",   cip->entrypoint,
606         "width",        cip->width,
607         "height",       cip->height,
608         "ref-frames",   cip->ref_frames,
609         NULL
610     );
611     if (!context->priv->is_constructed) {
612         g_object_unref(context);
613         return NULL;
614     }
615     return context;
616 }
617
618 /**
619  * gst_vaapi_context_reset:
620  * @context: a #GstVaapiContext
621  * @profile: a #GstVaapiProfile
622  * @entrypoint: a #GstVaapiEntrypoint
623  * @width: coded width from the bitstream
624  * @height: coded height from the bitstream
625  *
626  * Resets @context to the specified codec @profile and @entrypoint.
627  * The surfaces will be reallocated if the coded size changed.
628  *
629  * Return value: %TRUE on success
630  */
631 gboolean
632 gst_vaapi_context_reset(
633     GstVaapiContext    *context,
634     GstVaapiProfile     profile,
635     GstVaapiEntrypoint  entrypoint,
636     unsigned int        width,
637     unsigned int        height
638 )
639 {
640     GstVaapiContextPrivate * const priv = context->priv;
641     GstVaapiContextInfo info;
642
643     info.profile    = profile;
644     info.entrypoint = entrypoint;
645     info.width      = width;
646     info.height     = height;
647     info.ref_frames = priv->ref_frames;
648
649     return gst_vaapi_context_reset_full(context, &info);
650 }
651
652 /**
653  * gst_vaapi_context_reset_full:
654  * @context: a #GstVaapiContext
655  * @cip: a pointer to the new #GstVaapiContextInfo details
656  *
657  * Resets @context to the configuration specified by @cip, thus
658  * including profile, entry-point, encoded size and maximum number of
659  * reference frames reported by the bitstream.
660  *
661  * Return value: %TRUE on success
662  */
663 gboolean
664 gst_vaapi_context_reset_full(GstVaapiContext *context, GstVaapiContextInfo *cip)
665 {
666     GstVaapiContextPrivate * const priv = context->priv;
667     gboolean size_changed, codec_changed;
668
669     size_changed = priv->width != cip->width || priv->height != cip->height;
670     if (size_changed) {
671         gst_vaapi_context_destroy_surfaces(context);
672         priv->width  = cip->width;
673         priv->height = cip->height;
674     }
675
676     codec_changed = priv->profile != cip->profile || priv->entrypoint != cip->entrypoint;
677     if (codec_changed) {
678         gst_vaapi_context_destroy(context);
679         priv->profile    = cip->profile;
680         priv->entrypoint = cip->entrypoint;
681     }
682
683     if (size_changed && !gst_vaapi_context_create_surfaces(context))
684         return FALSE;
685
686     if (codec_changed && !gst_vaapi_context_create(context))
687         return FALSE;
688
689     priv->is_constructed = TRUE;
690     return TRUE;
691 }
692
693 /**
694  * gst_vaapi_context_get_id:
695  * @context: a #GstVaapiContext
696  *
697  * Returns the underlying VAContextID of the @context.
698  *
699  * Return value: the underlying VA context id
700  */
701 GstVaapiID
702 gst_vaapi_context_get_id(GstVaapiContext *context)
703 {
704     g_return_val_if_fail(GST_VAAPI_IS_CONTEXT(context), VA_INVALID_ID);
705
706     return GST_VAAPI_OBJECT_ID(context);
707 }
708
709 /**
710  * gst_vaapi_context_get_profile:
711  * @context: a #GstVaapiContext
712  *
713  * Returns the VA profile used by the @context.
714  *
715  * Return value: the VA profile used by the @context
716  */
717 GstVaapiProfile
718 gst_vaapi_context_get_profile(GstVaapiContext *context)
719 {
720     g_return_val_if_fail(GST_VAAPI_IS_CONTEXT(context), 0);
721
722     return context->priv->profile;
723 }
724
725 /**
726  * gst_vaapi_context_set_profile:
727  * @context: a #GstVaapiContext
728  * @profile: the new #GstVaapiProfile to use
729  *
730  * Sets the new @profile to use with the @context. If @profile matches
731  * the previous profile, this call has no effect. Otherwise, the
732  * underlying VA context is recreated, while keeping the previously
733  * allocated surfaces.
734  *
735  * Return value: %TRUE on success
736  */
737 gboolean
738 gst_vaapi_context_set_profile(GstVaapiContext *context, GstVaapiProfile profile)
739 {
740     g_return_val_if_fail(GST_VAAPI_IS_CONTEXT(context), FALSE);
741     g_return_val_if_fail(profile, FALSE);
742
743     return gst_vaapi_context_reset(context,
744                                    profile,
745                                    context->priv->entrypoint,
746                                    context->priv->width,
747                                    context->priv->height);
748 }
749
750 /**
751  * gst_vaapi_context_get_entrypoint:
752  * @context: a #GstVaapiContext
753  *
754  * Returns the VA entrypoint used by the @context
755  *
756  * Return value: the VA entrypoint used by the @context
757  */
758 GstVaapiEntrypoint
759 gst_vaapi_context_get_entrypoint(GstVaapiContext *context)
760 {
761     g_return_val_if_fail(GST_VAAPI_IS_CONTEXT(context), 0);
762
763     return context->priv->entrypoint;
764 }
765
766 /**
767  * gst_vaapi_context_get_size:
768  * @context: a #GstVaapiContext
769  * @pwidth: return location for the width, or %NULL
770  * @pheight: return location for the height, or %NULL
771  *
772  * Retrieves the size of the surfaces attached to @context.
773  */
774 void
775 gst_vaapi_context_get_size(
776     GstVaapiContext *context,
777     guint           *pwidth,
778     guint           *pheight
779 )
780 {
781     g_return_if_fail(GST_VAAPI_IS_CONTEXT(context));
782
783     if (pwidth)
784         *pwidth = context->priv->width;
785
786     if (pheight)
787         *pheight = context->priv->height;
788 }
789
790 /**
791  * gst_vaapi_context_get_surface:
792  * @context: a #GstVaapiContext
793  *
794  * Acquires a free surface. The returned surface but be released with
795  * gst_vaapi_context_put_surface(). This function returns %NULL if
796  * there is no free surface available in the pool. The surfaces are
797  * pre-allocated during context creation though.
798  *
799  * Return value: a free surface, or %NULL if none is available
800  */
801 GstVaapiSurface *
802 gst_vaapi_context_get_surface(GstVaapiContext *context)
803 {
804     GstVaapiSurface *surface;
805
806     g_return_val_if_fail(GST_VAAPI_IS_CONTEXT(context), NULL);
807
808     surface = gst_vaapi_video_pool_get_object(context->priv->surfaces_pool);
809     if (!surface)
810         return NULL;
811
812     gst_vaapi_surface_set_parent_context(surface, context);
813     return surface;
814 }
815
816 /**
817  * gst_vaapi_context_get_surface_pool:
818  * @context: a #GstVaapiContext
819  *
820  * Reference the surface pool. The returned surface pool should be released with
821  * g_object_unref(). This function returns %NULL if
822  * there is the surface pool is empty. The surface pool is
823  * created during context creation though.
824  *
825  * Return value: surface pool, or %NULL if it is not created.
826  */
827
828 GstVaapiSurfacePool *
829 gst_vaapi_context_get_surface_pool(GstVaapiContext *context)
830 {
831   g_return_val_if_fail(GST_VAAPI_IS_CONTEXT(context), NULL);
832   return (GstVaapiSurfacePool*)g_object_ref(context->priv->surfaces_pool);
833 }
834
835
836 /**
837  * gst_vaapi_context_get_surface_count:
838  * @context: a #GstVaapiContext
839  *
840  * Retrieves the number of free surfaces left in the pool.
841  *
842  * Return value: the number of free surfaces available in the pool
843  */
844 guint
845 gst_vaapi_context_get_surface_count(GstVaapiContext *context)
846 {
847     g_return_val_if_fail(GST_VAAPI_IS_CONTEXT(context), 0);
848
849     return gst_vaapi_video_pool_get_size(context->priv->surfaces_pool);
850 }
851
852 /**
853  * gst_vaapi_context_put_surface:
854  * @context: a #GstVaapiContext
855  * @surface: the #GstVaapiSurface to release
856  *
857  * Releases a surface acquired by gst_vaapi_context_get_surface().
858  */
859 void
860 gst_vaapi_context_put_surface(GstVaapiContext *context, GstVaapiSurface *surface)
861 {
862     g_return_if_fail(GST_VAAPI_IS_CONTEXT(context));
863     g_return_if_fail(GST_VAAPI_IS_SURFACE(surface));
864
865     gst_vaapi_surface_set_parent_context(surface, NULL);
866     gst_vaapi_video_pool_put_object(context->priv->surfaces_pool, surface);
867 }
868
869 /**
870  * gst_vaapi_context_find_surface_by_id:
871  * @context: a #GstVaapiContext
872  * @id: the VA surface id to find
873  *
874  * Finds VA surface by @id in the list of surfaces attached to the @context.
875  *
876  * Return value: the matching #GstVaapiSurface object, or %NULL if
877  *   none was found
878  */
879 GstVaapiSurface *
880 gst_vaapi_context_find_surface_by_id(GstVaapiContext *context, GstVaapiID id)
881 {
882     GstVaapiContextPrivate *priv;
883     GstVaapiSurface *surface;
884     guint i;
885
886     g_return_val_if_fail(GST_VAAPI_IS_CONTEXT(context), NULL);
887
888     priv = context->priv;
889     g_return_val_if_fail(priv->surfaces, NULL);
890
891     for (i = 0; i < priv->surfaces->len; i++) {
892         surface = g_ptr_array_index(priv->surfaces, i);
893         if (GST_VAAPI_OBJECT_ID(surface) == id)
894             return surface;
895     }
896     return NULL;
897 }
898
899 /* Check if composition changed */
900 static gboolean
901 gst_vaapi_context_composition_changed(
902     GstVaapiContext            *context,
903     GstVideoOverlayComposition *composition
904 )
905 {
906     GstVaapiContextPrivate * const priv = context->priv;
907     GstVaapiOverlayRectangle *overlay;
908     GstVideoOverlayRectangle *rect;
909     guint i, n_rectangles;
910
911     if (!priv->overlay || !composition)
912         return TRUE;
913
914     n_rectangles = gst_video_overlay_composition_n_rectangles(composition);
915     if (priv->overlay->len != n_rectangles)
916         return TRUE;
917
918     for (i = 0; i < n_rectangles; i++) {
919         rect = gst_video_overlay_composition_get_rectangle(composition, i);
920         g_return_val_if_fail(rect, TRUE);
921         overlay = g_ptr_array_index(priv->overlay, i);
922         g_return_val_if_fail(overlay, TRUE);
923         if (overlay->seq_num != gst_video_overlay_rectangle_get_seqnum(rect))
924             return TRUE;
925     }
926     return FALSE;
927 }
928
929 /**
930  * gst_vaapi_context_apply_composition:
931  * @context: a #GstVaapiContext
932  * @composition: a #GstVideoOverlayComposition
933  *
934  * Applies video composition planes to all surfaces bound to @context.
935  * This helper function resets any additional subpictures the user may
936  * have associated himself. A %NULL @composition will also clear all
937  * the existing subpictures.
938  *
939  * Return value: %TRUE if all composition planes could be applied,
940  *   %FALSE otherwise
941  */
942 gboolean
943 gst_vaapi_context_apply_composition(
944     GstVaapiContext            *context,
945     GstVideoOverlayComposition *composition
946 )
947 {
948     GstVaapiContextPrivate *priv;
949     GstVideoOverlayRectangle *rect;
950     GstVaapiOverlayRectangle *overlay = NULL;
951     GstVaapiDisplay *display;
952     guint i, j, n_rectangles;
953
954     g_return_val_if_fail(GST_VAAPI_IS_CONTEXT(context), FALSE);
955
956     priv = context->priv;
957     if (!priv->surfaces)
958         return FALSE;
959
960     display = GST_VAAPI_OBJECT_DISPLAY(context);
961     if (!display)
962         return FALSE;
963
964     if (!gst_vaapi_context_composition_changed(context, composition))
965         return TRUE;
966     gst_vaapi_context_destroy_overlay(context);
967
968     if (!composition)
969         return TRUE;
970     if (!gst_vaapi_context_create_overlay(context))
971         return FALSE;
972
973     n_rectangles = gst_video_overlay_composition_n_rectangles(composition);
974     for (i = 0; i < n_rectangles; i++) {
975         rect = gst_video_overlay_composition_get_rectangle(composition, i);
976
977         overlay = overlay_rectangle_new(context);
978         if (!overlay) {
979             GST_WARNING("could not create VA overlay rectangle");
980             return FALSE;
981         }
982         overlay->seq_num = gst_video_overlay_rectangle_get_seqnum(rect);
983
984         overlay->subpicture = gst_vaapi_subpicture_new_from_overlay_rectangle(
985             display,
986             rect
987         );
988         if (!overlay->subpicture) {
989             overlay_rectangle_destroy(overlay);
990             return FALSE;
991         }
992
993         gst_video_overlay_rectangle_get_render_rectangle(
994             rect,
995             (gint *)&overlay->rect.x,
996             (gint *)&overlay->rect.y,
997             &overlay->rect.width,
998             &overlay->rect.height
999         );
1000
1001         for (j = 0; j < priv->surfaces->len; j++) {
1002             GstVaapiSurface * const surface =
1003                 g_ptr_array_index(priv->surfaces, j);
1004             if (!gst_vaapi_surface_associate_subpicture(surface,
1005                          overlay->subpicture, NULL, &overlay->rect)) {
1006                 GST_WARNING("could not render overlay rectangle %p", rect);
1007                 overlay_rectangle_destroy(overlay);
1008                 return FALSE;
1009             }
1010         }
1011         g_ptr_array_add(priv->overlay, overlay);
1012     }
1013     return TRUE;
1014 }