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