context: reset VA context if VA surfaces set changed.
[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  *    Author: Gwenole Beauchesne <gwenole.beauchesne@splitted-desktop.com>
6  *  Copyright (C) 2011-2014 Intel Corporation
7  *    Author: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public License
11  *  as published by the Free Software Foundation; either version 2.1
12  *  of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free
21  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  *  Boston, MA 02110-1301 USA
23  */
24
25 /**
26  * SECTION:gstvaapicontext
27  * @short_description: VA context abstraction
28  */
29
30 #include "sysdeps.h"
31 #include "gstvaapicompat.h"
32 #include "gstvaapicontext.h"
33 #include "gstvaapicontext_overlay.h"
34 #include "gstvaapidisplay_priv.h"
35 #include "gstvaapiobject_priv.h"
36 #include "gstvaapisurface.h"
37 #include "gstvaapisurface_priv.h"
38 #include "gstvaapisurfacepool.h"
39 #include "gstvaapisurfaceproxy.h"
40 #include "gstvaapivideopool_priv.h"
41 #include "gstvaapiutils.h"
42 #include "gstvaapiutils_core.h"
43
44 #define DEBUG 1
45 #include "gstvaapidebug.h"
46
47 /* Define default VA surface chroma format to YUV 4:2:0 */
48 #define DEFAULT_CHROMA_TYPE (GST_VAAPI_CHROMA_TYPE_YUV420)
49
50 static void
51 unref_surface_cb (GstVaapiSurface * surface)
52 {
53   gst_vaapi_surface_set_parent_context (surface, NULL);
54   gst_vaapi_object_unref (surface);
55 }
56
57 static inline gboolean
58 context_get_attribute (GstVaapiContext * context, VAConfigAttribType type,
59     guint * out_value_ptr)
60 {
61   return gst_vaapi_get_config_attribute (GST_VAAPI_OBJECT_DISPLAY (context),
62       context->va_profile, context->va_entrypoint, type, out_value_ptr);
63 }
64
65 static void
66 context_destroy_surfaces (GstVaapiContext * context)
67 {
68   gst_vaapi_context_overlay_reset (context);
69
70   if (context->surfaces) {
71     g_ptr_array_unref (context->surfaces);
72     context->surfaces = NULL;
73   }
74   gst_vaapi_video_pool_replace (&context->surfaces_pool, NULL);
75 }
76
77 static void
78 context_destroy (GstVaapiContext * context)
79 {
80   GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (context);
81   VAContextID context_id;
82   VAStatus status;
83
84   context_id = GST_VAAPI_OBJECT_ID (context);
85   GST_DEBUG ("context 0x%08x", context_id);
86
87   if (context_id != VA_INVALID_ID) {
88     GST_VAAPI_DISPLAY_LOCK (display);
89     status = vaDestroyContext (GST_VAAPI_DISPLAY_VADISPLAY (display),
90         context_id);
91     GST_VAAPI_DISPLAY_UNLOCK (display);
92     if (!vaapi_check_status (status, "vaDestroyContext()"))
93       GST_WARNING ("failed to destroy context 0x%08x", context_id);
94     GST_VAAPI_OBJECT_ID (context) = VA_INVALID_ID;
95   }
96
97   if (context->va_config != VA_INVALID_ID) {
98     GST_VAAPI_DISPLAY_LOCK (display);
99     status = vaDestroyConfig (GST_VAAPI_DISPLAY_VADISPLAY (display),
100         context->va_config);
101     GST_VAAPI_DISPLAY_UNLOCK (display);
102     if (!vaapi_check_status (status, "vaDestroyConfig()"))
103       GST_WARNING ("failed to destroy config 0x%08x", context->va_config);
104     context->va_config = VA_INVALID_ID;
105   }
106 }
107
108 static gboolean
109 context_create_surfaces (GstVaapiContext * context)
110 {
111   const GstVaapiContextInfo *const cip = &context->info;
112   GstVideoInfo vi;
113   GstVaapiSurface *surface;
114   guint i, num_surfaces;
115
116   /* Number of scratch surfaces beyond those used as reference */
117   const guint SCRATCH_SURFACES_COUNT = 4;
118
119   if (!gst_vaapi_context_overlay_reset (context))
120     return FALSE;
121
122   num_surfaces = cip->ref_frames + SCRATCH_SURFACES_COUNT;
123   if (!context->surfaces) {
124     context->surfaces = g_ptr_array_new_full (num_surfaces,
125         (GDestroyNotify) unref_surface_cb);
126     if (!context->surfaces)
127       return FALSE;
128   }
129
130   if (!context->surfaces_pool) {
131     gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_ENCODED,
132         cip->width, cip->height);
133     context->surfaces_pool =
134         gst_vaapi_surface_pool_new (GST_VAAPI_OBJECT_DISPLAY (context), &vi);
135     if (!context->surfaces_pool)
136       return FALSE;
137   }
138   gst_vaapi_video_pool_set_capacity (context->surfaces_pool, num_surfaces);
139
140   for (i = context->surfaces->len; i < num_surfaces; i++) {
141     surface = gst_vaapi_surface_new (GST_VAAPI_OBJECT_DISPLAY (context),
142         cip->chroma_type, cip->width, cip->height);
143     if (!surface)
144       return FALSE;
145     gst_vaapi_surface_set_parent_context (surface, context);
146     g_ptr_array_add (context->surfaces, surface);
147     if (!gst_vaapi_video_pool_add_object (context->surfaces_pool, surface))
148       return FALSE;
149   }
150   return TRUE;
151 }
152
153 static gboolean
154 context_create (GstVaapiContext * context)
155 {
156   const GstVaapiContextInfo *const cip = &context->info;
157   GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (context);
158   VAConfigAttrib attribs[3], *attrib = attribs;
159   VAContextID context_id;
160   VASurfaceID surface_id;
161   VAStatus status;
162   GArray *surfaces = NULL;
163   gboolean success = FALSE;
164   guint i, value, va_chroma_format;
165
166   if (!context->surfaces && !context_create_surfaces (context))
167     goto cleanup;
168
169   /* Create VA surfaces list for vaCreateContext() */
170   surfaces = g_array_sized_new (FALSE,
171       FALSE, sizeof (VASurfaceID), context->surfaces->len);
172   if (!surfaces)
173     goto cleanup;
174
175   for (i = 0; i < context->surfaces->len; i++) {
176     GstVaapiSurface *const surface = g_ptr_array_index (context->surfaces, i);
177     if (!surface)
178       goto cleanup;
179     surface_id = GST_VAAPI_OBJECT_ID (surface);
180     g_array_append_val (surfaces, surface_id);
181   }
182   g_assert (surfaces->len == context->surfaces->len);
183
184   /* Reset profile and entrypoint */
185   if (!cip->profile || !cip->entrypoint)
186     goto cleanup;
187   context->va_profile = gst_vaapi_profile_get_va_profile (cip->profile);
188   context->va_entrypoint =
189       gst_vaapi_entrypoint_get_va_entrypoint (cip->entrypoint);
190
191   /* Validate VA surface format */
192   va_chroma_format = from_GstVaapiChromaType (cip->chroma_type);
193   if (!va_chroma_format)
194     goto cleanup;
195   attrib->type = VAConfigAttribRTFormat;
196   if (!context_get_attribute (context, attrib->type, &value))
197     goto cleanup;
198   if (!(value & va_chroma_format)) {
199     GST_ERROR ("unsupported chroma format (%s)",
200         string_of_va_chroma_format (va_chroma_format));
201     goto cleanup;
202   }
203   attrib->value = va_chroma_format;
204   attrib++;
205
206   switch (cip->usage) {
207 #if USE_ENCODERS
208     case GST_VAAPI_CONTEXT_USAGE_ENCODE:
209     {
210       const GstVaapiConfigInfoEncoder *const config = &cip->config.encoder;
211       guint va_rate_control;
212
213       /* Rate control */
214       attrib->type = VAConfigAttribRateControl;
215       if (!context_get_attribute (context, attrib->type, &value))
216         goto cleanup;
217
218       va_rate_control = from_GstVaapiRateControl (config->rc_mode);
219       if ((value & va_rate_control) != va_rate_control) {
220         GST_ERROR ("unsupported %s rate control",
221             string_of_VARateControl (va_rate_control));
222         goto cleanup;
223       }
224       attrib->value = va_rate_control;
225       attrib++;
226
227       /* Packed headers */
228       if (config->packed_headers) {
229         attrib->type = VAConfigAttribEncPackedHeaders;
230         if (!context_get_attribute (context, attrib->type, &value))
231           goto cleanup;
232
233         if ((value & config->packed_headers) != config->packed_headers) {
234           GST_ERROR ("unsupported packed headers 0x%08x",
235               config->packed_headers & ~(value & config->packed_headers));
236           goto cleanup;
237         }
238         attrib->value = config->packed_headers;
239         attrib++;
240       }
241       break;
242     }
243 #endif
244     default:
245       break;
246   }
247
248   GST_VAAPI_DISPLAY_LOCK (display);
249   status = vaCreateConfig (GST_VAAPI_DISPLAY_VADISPLAY (display),
250       context->va_profile, context->va_entrypoint, attribs, attrib - attribs,
251       &context->va_config);
252   GST_VAAPI_DISPLAY_UNLOCK (display);
253   if (!vaapi_check_status (status, "vaCreateConfig()"))
254     goto cleanup;
255
256   GST_VAAPI_DISPLAY_LOCK (display);
257   status = vaCreateContext (GST_VAAPI_DISPLAY_VADISPLAY (display),
258       context->va_config, cip->width, cip->height, VA_PROGRESSIVE,
259       (VASurfaceID *) surfaces->data, surfaces->len, &context_id);
260   GST_VAAPI_DISPLAY_UNLOCK (display);
261   if (!vaapi_check_status (status, "vaCreateContext()"))
262     goto cleanup;
263
264   GST_DEBUG ("context 0x%08x", context_id);
265   GST_VAAPI_OBJECT_ID (context) = context_id;
266   success = TRUE;
267
268 cleanup:
269   if (surfaces)
270     g_array_free (surfaces, TRUE);
271   return success;
272 }
273
274 /** Updates config for encoding. Returns %TRUE if config changed */
275 static gboolean
276 context_update_config_encoder (GstVaapiContext * context,
277     const GstVaapiConfigInfoEncoder * new_config)
278 {
279   GstVaapiConfigInfoEncoder *const config = &context->info.config.encoder;
280   gboolean config_changed = FALSE;
281
282   g_assert (context->info.usage == GST_VAAPI_CONTEXT_USAGE_ENCODE);
283
284   if (config->rc_mode != new_config->rc_mode) {
285     config->rc_mode = new_config->rc_mode;
286     config_changed = TRUE;
287   }
288
289   if (config->packed_headers != new_config->packed_headers) {
290     config->packed_headers = new_config->packed_headers;
291     config_changed = TRUE;
292   }
293   return config_changed;
294 }
295
296 static inline void
297 gst_vaapi_context_init (GstVaapiContext * context,
298     const GstVaapiContextInfo * new_cip)
299 {
300   GstVaapiContextInfo *const cip = &context->info;
301
302   *cip = *new_cip;
303   if (!cip->chroma_type)
304     cip->chroma_type = DEFAULT_CHROMA_TYPE;
305
306   context->va_config = VA_INVALID_ID;
307   gst_vaapi_context_overlay_init (context);
308 }
309
310 static void
311 gst_vaapi_context_finalize (GstVaapiContext * context)
312 {
313   context_destroy (context);
314   context_destroy_surfaces (context);
315   gst_vaapi_context_overlay_finalize (context);
316 }
317
318 GST_VAAPI_OBJECT_DEFINE_CLASS (GstVaapiContext, gst_vaapi_context);
319
320 /**
321  * gst_vaapi_context_new:
322  * @display: a #GstVaapiDisplay
323  * @cip: a pointer to the #GstVaapiContextInfo
324  *
325  * Creates a new #GstVaapiContext with the configuration specified by
326  * @cip, thus including profile, entry-point, encoded size and maximum
327  * number of reference frames reported by the bitstream.
328  *
329  * Return value: the newly allocated #GstVaapiContext object
330  */
331 GstVaapiContext *
332 gst_vaapi_context_new (GstVaapiDisplay * display,
333     const GstVaapiContextInfo * cip)
334 {
335   GstVaapiContext *context;
336
337   g_return_val_if_fail (cip->profile, NULL);
338   g_return_val_if_fail (cip->entrypoint, NULL);
339   g_return_val_if_fail (cip->width > 0, NULL);
340   g_return_val_if_fail (cip->height > 0, NULL);
341
342   context = gst_vaapi_object_new (gst_vaapi_context_class (), display);
343   if (!context)
344     return NULL;
345
346   gst_vaapi_context_init (context, cip);
347   if (!context_create (context))
348     goto error;
349   return context;
350
351 error:
352   gst_vaapi_object_unref (context);
353   return NULL;
354 }
355
356 /**
357  * gst_vaapi_context_reset:
358  * @context: a #GstVaapiContext
359  * @new_cip: a pointer to the new #GstVaapiContextInfo details
360  *
361  * Resets @context to the configuration specified by @new_cip, thus
362  * including profile, entry-point, encoded size and maximum number of
363  * reference frames reported by the bitstream.
364  *
365  * Return value: %TRUE on success
366  */
367 gboolean
368 gst_vaapi_context_reset (GstVaapiContext * context,
369     const GstVaapiContextInfo * new_cip)
370 {
371   GstVaapiContextInfo *const cip = &context->info;
372   gboolean reset_surfaces = FALSE, reset_config = FALSE;
373   GstVaapiChromaType chroma_type;
374
375   chroma_type = new_cip->chroma_type ? new_cip->chroma_type :
376       DEFAULT_CHROMA_TYPE;
377   if (cip->chroma_type != chroma_type) {
378     cip->chroma_type = chroma_type;
379     reset_surfaces = TRUE;
380   }
381
382   if (cip->width != new_cip->width || cip->height != new_cip->height) {
383     cip->width = new_cip->width;
384     cip->height = new_cip->height;
385     reset_surfaces = TRUE;
386   }
387
388   if (cip->profile != new_cip->profile ||
389       cip->entrypoint != new_cip->entrypoint) {
390     cip->profile = new_cip->profile;
391     cip->entrypoint = new_cip->entrypoint;
392     reset_config = TRUE;
393   }
394
395   if (cip->usage != new_cip->usage) {
396     cip->usage = new_cip->usage;
397     reset_config = TRUE;
398     memcpy (&cip->config, &new_cip->config, sizeof (cip->config));
399   } else if (new_cip->usage == GST_VAAPI_CONTEXT_USAGE_ENCODE) {
400     if (context_update_config_encoder (context, &new_cip->config.encoder))
401       reset_config = TRUE;
402   } else if (new_cip->usage == GST_VAAPI_CONTEXT_USAGE_DECODE) {
403     if (reset_surfaces)
404       reset_config = TRUE;
405   }
406
407   if (reset_surfaces)
408     context_destroy_surfaces (context);
409   if (reset_config)
410     context_destroy (context);
411
412   if (reset_surfaces && !context_create_surfaces (context))
413     return FALSE;
414   if (reset_config && !context_create (context))
415     return FALSE;
416   return TRUE;
417 }
418
419 /**
420  * gst_vaapi_context_get_id:
421  * @context: a #GstVaapiContext
422  *
423  * Returns the underlying VAContextID of the @context.
424  *
425  * Return value: the underlying VA context id
426  */
427 GstVaapiID
428 gst_vaapi_context_get_id (GstVaapiContext * context)
429 {
430   g_return_val_if_fail (context != NULL, VA_INVALID_ID);
431
432   return GST_VAAPI_OBJECT_ID (context);
433 }
434
435 /**
436  * gst_vaapi_context_get_surface_proxy:
437  * @context: a #GstVaapiContext
438  *
439  * Acquires a free surface, wrapped into a #GstVaapiSurfaceProxy. The
440  * returned surface will be automatically released when the proxy is
441  * destroyed. So, it is enough to call gst_vaapi_surface_proxy_unref()
442  * after usage.
443  *
444  * This function returns %NULL if there is no free surface available
445  * in the pool. The surfaces are pre-allocated during context creation
446  * though.
447  *
448  * Return value: a free surface, or %NULL if none is available
449  */
450 GstVaapiSurfaceProxy *
451 gst_vaapi_context_get_surface_proxy (GstVaapiContext * context)
452 {
453   g_return_val_if_fail (context != NULL, NULL);
454
455   return
456       gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
457       (context->surfaces_pool));
458 }
459
460 /**
461  * gst_vaapi_context_get_surface_count:
462  * @context: a #GstVaapiContext
463  *
464  * Retrieves the number of free surfaces left in the pool.
465  *
466  * Return value: the number of free surfaces available in the pool
467  */
468 guint
469 gst_vaapi_context_get_surface_count (GstVaapiContext * context)
470 {
471   g_return_val_if_fail (context != NULL, 0);
472
473   return gst_vaapi_video_pool_get_size (context->surfaces_pool);
474 }