Userful gstreamer-vaapi
[vaapi:cpu-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 static void
48 unref_surface_cb (GstVaapiSurface * surface)
49 {
50   gst_vaapi_surface_set_parent_context (surface, NULL);
51   gst_vaapi_object_unref (surface);
52 }
53
54 static inline gboolean
55 context_get_attribute (GstVaapiContext * context, VAConfigAttribType type,
56     guint * out_value_ptr)
57 {
58   return gst_vaapi_get_config_attribute (GST_VAAPI_OBJECT_DISPLAY (context),
59       context->va_profile, context->va_entrypoint, type, out_value_ptr);
60 }
61
62 static void
63 context_destroy_surfaces (GstVaapiContext * context)
64 {
65   gst_vaapi_context_overlay_reset (context);
66
67   if (context->surfaces) {
68     g_ptr_array_unref (context->surfaces);
69     context->surfaces = NULL;
70   }
71   gst_vaapi_video_pool_replace (&context->surfaces_pool, NULL);
72 }
73
74 static void
75 context_destroy (GstVaapiContext * context)
76 {
77   GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (context);
78   VAContextID context_id;
79   VAStatus status;
80
81   context_id = GST_VAAPI_OBJECT_ID (context);
82   GST_DEBUG ("context 0x%08x", context_id);
83
84   if (context_id != VA_INVALID_ID) {
85     GST_VAAPI_DISPLAY_LOCK (display);
86     status = vaDestroyContext (GST_VAAPI_DISPLAY_VADISPLAY (display),
87         context_id);
88     GST_VAAPI_DISPLAY_UNLOCK (display);
89     if (!vaapi_check_status (status, "vaDestroyContext()"))
90       GST_WARNING ("failed to destroy context 0x%08x", context_id);
91     GST_VAAPI_OBJECT_ID (context) = VA_INVALID_ID;
92   }
93
94   if (context->va_config != VA_INVALID_ID) {
95     GST_VAAPI_DISPLAY_LOCK (display);
96     status = vaDestroyConfig (GST_VAAPI_DISPLAY_VADISPLAY (display),
97         context->va_config);
98     GST_VAAPI_DISPLAY_UNLOCK (display);
99     if (!vaapi_check_status (status, "vaDestroyConfig()"))
100       GST_WARNING ("failed to destroy config 0x%08x", context->va_config);
101     context->va_config = VA_INVALID_ID;
102   }
103 }
104
105 static gboolean
106 context_create_surfaces (GstVaapiContext * context)
107 {
108   const GstVaapiContextInfo *const cip = &context->info;
109   GstVideoInfo vi;
110   GstVaapiSurface *surface;
111   guint i, num_surfaces;
112
113   /* Number of scratch surfaces beyond those used as reference */
114   const guint SCRATCH_SURFACES_COUNT = 4;
115
116   if (!gst_vaapi_context_overlay_reset (context))
117     return FALSE;
118
119   num_surfaces = cip->ref_frames + SCRATCH_SURFACES_COUNT;
120   if (!context->surfaces) {
121     context->surfaces = g_ptr_array_new_full (num_surfaces,
122         (GDestroyNotify) unref_surface_cb);
123     if (!context->surfaces)
124       return FALSE;
125   }
126
127   if (!context->surfaces_pool) {
128     gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_ENCODED,
129         cip->width, cip->height);
130     context->surfaces_pool =
131         gst_vaapi_surface_pool_new (GST_VAAPI_OBJECT_DISPLAY (context), &vi);
132     if (!context->surfaces_pool)
133       return FALSE;
134   }
135   gst_vaapi_video_pool_set_capacity (context->surfaces_pool, num_surfaces);
136
137   for (i = context->surfaces->len; i < num_surfaces; i++) {
138     surface = gst_vaapi_surface_new (GST_VAAPI_OBJECT_DISPLAY (context),
139         GST_VAAPI_CHROMA_TYPE_YUV420, cip->width, cip->height);
140     if (!surface)
141       return FALSE;
142     gst_vaapi_surface_set_parent_context (surface, context);
143     g_ptr_array_add (context->surfaces, surface);
144     if (!gst_vaapi_video_pool_add_object (context->surfaces_pool, surface))
145       return FALSE;
146   }
147   return TRUE;
148 }
149
150 static gboolean
151 context_create (GstVaapiContext * context)
152 {
153   const GstVaapiContextInfo *const cip = &context->info;
154   GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (context);
155   VAConfigAttrib attribs[3], *attrib = attribs;
156   VAContextID context_id;
157   VASurfaceID surface_id;
158   VAStatus status;
159   GArray *surfaces = NULL;
160   gboolean success = FALSE;
161   guint i, value;
162
163   if (!context->surfaces && !context_create_surfaces (context))
164     goto cleanup;
165
166   /* Create VA surfaces list for vaCreateContext() */
167   surfaces = g_array_sized_new (FALSE,
168       FALSE, sizeof (VASurfaceID), context->surfaces->len);
169   if (!surfaces)
170     goto cleanup;
171
172   for (i = 0; i < context->surfaces->len; i++) {
173     GstVaapiSurface *const surface = g_ptr_array_index (context->surfaces, i);
174     if (!surface)
175       goto cleanup;
176     surface_id = GST_VAAPI_OBJECT_ID (surface);
177     g_array_append_val (surfaces, surface_id);
178   }
179   g_assert (surfaces->len == context->surfaces->len);
180
181   /* Reset profile and entrypoint */
182   if (!cip->profile || !cip->entrypoint)
183     goto cleanup;
184   context->va_profile = gst_vaapi_profile_get_va_profile (cip->profile);
185   context->va_entrypoint =
186       gst_vaapi_entrypoint_get_va_entrypoint (cip->entrypoint);
187
188   /* Validate VA surface format */
189   attrib->type = VAConfigAttribRTFormat;
190   if (!context_get_attribute (context, attrib->type, &value))
191     goto cleanup;
192   if (!(value & VA_RT_FORMAT_YUV420))
193     goto cleanup;
194   attrib->value = VA_RT_FORMAT_YUV420;
195   attrib++;
196
197   switch (cip->usage) {
198 #if USE_ENCODERS
199     case GST_VAAPI_CONTEXT_USAGE_ENCODE:
200     {
201       const GstVaapiConfigInfoEncoder *const config = &cip->config.encoder;
202       guint va_rate_control;
203
204       /* Rate control */
205       attrib->type = VAConfigAttribRateControl;
206       if (!context_get_attribute (context, attrib->type, &value))
207         goto cleanup;
208
209       va_rate_control = from_GstVaapiRateControl (config->rc_mode);
210       if ((value & va_rate_control) != va_rate_control) {
211         GST_ERROR ("unsupported %s rate control",
212             string_of_VARateControl (va_rate_control));
213         goto cleanup;
214       }
215       attrib->value = va_rate_control;
216       attrib++;
217
218       /* Packed headers */
219       if (config->packed_headers) {
220         attrib->type = VAConfigAttribEncPackedHeaders;
221         if (!context_get_attribute (context, attrib->type, &value))
222           goto cleanup;
223
224         if ((value & config->packed_headers) != config->packed_headers) {
225           GST_ERROR ("unsupported packed headers 0x%08x",
226               config->packed_headers & ~(value & config->packed_headers));
227           goto cleanup;
228         }
229         attrib->value = config->packed_headers;
230         attrib++;
231       }
232       break;
233     }
234 #endif
235     default:
236       break;
237   }
238
239   GST_VAAPI_DISPLAY_LOCK (display);
240   status = vaCreateConfig (GST_VAAPI_DISPLAY_VADISPLAY (display),
241       context->va_profile, context->va_entrypoint, attribs, attrib - attribs,
242       &context->va_config);
243   GST_VAAPI_DISPLAY_UNLOCK (display);
244   if (!vaapi_check_status (status, "vaCreateConfig()"))
245     goto cleanup;
246
247   GST_VAAPI_DISPLAY_LOCK (display);
248   status = vaCreateContext (GST_VAAPI_DISPLAY_VADISPLAY (display),
249       context->va_config, cip->width, cip->height, VA_PROGRESSIVE,
250       (VASurfaceID *) surfaces->data, surfaces->len, &context_id);
251   GST_VAAPI_DISPLAY_UNLOCK (display);
252   if (!vaapi_check_status (status, "vaCreateContext()"))
253     goto cleanup;
254
255   GST_DEBUG ("context 0x%08x", context_id);
256   GST_VAAPI_OBJECT_ID (context) = context_id;
257   success = TRUE;
258
259 cleanup:
260   if (surfaces)
261     g_array_free (surfaces, TRUE);
262   return success;
263 }
264
265 /** Updates config for encoding. Returns %TRUE if config changed */
266 static gboolean
267 context_update_config_encoder (GstVaapiContext * context,
268     const GstVaapiConfigInfoEncoder * new_config)
269 {
270   GstVaapiConfigInfoEncoder *const config = &context->info.config.encoder;
271   gboolean config_changed = FALSE;
272
273   g_assert (context->info.usage == GST_VAAPI_CONTEXT_USAGE_ENCODE);
274
275   if (config->rc_mode != new_config->rc_mode) {
276     config->rc_mode = new_config->rc_mode;
277     config_changed = TRUE;
278   }
279
280   if (config->packed_headers != new_config->packed_headers) {
281     config->packed_headers = new_config->packed_headers;
282     config_changed = TRUE;
283   }
284   return config_changed;
285 }
286
287 static inline void
288 gst_vaapi_context_init (GstVaapiContext * context,
289     const GstVaapiContextInfo * cip)
290 {
291   context->info = *cip;
292   context->va_config = VA_INVALID_ID;
293   gst_vaapi_context_overlay_init (context);
294 }
295
296 static void
297 gst_vaapi_context_finalize (GstVaapiContext * context)
298 {
299   context_destroy (context);
300   context_destroy_surfaces (context);
301   gst_vaapi_context_overlay_finalize (context);
302 }
303
304 GST_VAAPI_OBJECT_DEFINE_CLASS (GstVaapiContext, gst_vaapi_context);
305
306 /**
307  * gst_vaapi_context_new:
308  * @display: a #GstVaapiDisplay
309  * @cip: a pointer to the #GstVaapiContextInfo
310  *
311  * Creates a new #GstVaapiContext with the configuration specified by
312  * @cip, thus including profile, entry-point, encoded size and maximum
313  * number of reference frames reported by the bitstream.
314  *
315  * Return value: the newly allocated #GstVaapiContext object
316  */
317 GstVaapiContext *
318 gst_vaapi_context_new (GstVaapiDisplay * display,
319     const GstVaapiContextInfo * cip)
320 {
321   GstVaapiContext *context;
322
323   g_return_val_if_fail (cip->profile, NULL);
324   g_return_val_if_fail (cip->entrypoint, NULL);
325   g_return_val_if_fail (cip->width > 0, NULL);
326   g_return_val_if_fail (cip->height > 0, NULL);
327
328   context = gst_vaapi_object_new (gst_vaapi_context_class (), display);
329   if (!context)
330     return NULL;
331
332   gst_vaapi_context_init (context, cip);
333   if (!context_create (context))
334     goto error;
335   return context;
336
337 error:
338   gst_vaapi_object_unref (context);
339   return NULL;
340 }
341
342 /**
343  * gst_vaapi_context_reset:
344  * @context: a #GstVaapiContext
345  * @new_cip: a pointer to the new #GstVaapiContextInfo details
346  *
347  * Resets @context to the configuration specified by @new_cip, thus
348  * including profile, entry-point, encoded size and maximum number of
349  * reference frames reported by the bitstream.
350  *
351  * Return value: %TRUE on success
352  */
353 gboolean
354 gst_vaapi_context_reset (GstVaapiContext * context,
355     const GstVaapiContextInfo * new_cip)
356 {
357   GstVaapiContextInfo *const cip = &context->info;
358   gboolean size_changed, config_changed;
359
360   size_changed = cip->width != new_cip->width || cip->height != new_cip->height;
361   if (size_changed) {
362     cip->width = new_cip->width;
363     cip->height = new_cip->height;
364   }
365
366   config_changed = cip->profile != new_cip->profile ||
367       cip->entrypoint != new_cip->entrypoint;
368   if (config_changed) {
369     cip->profile = new_cip->profile;
370     cip->entrypoint = new_cip->entrypoint;
371   }
372
373   if (cip->usage != new_cip->usage) {
374     cip->usage = new_cip->usage;
375     config_changed = TRUE;
376     memcpy (&cip->config, &new_cip->config, sizeof (cip->config));
377   } else if (new_cip->usage == GST_VAAPI_CONTEXT_USAGE_ENCODE) {
378     if (context_update_config_encoder (context, &new_cip->config.encoder))
379       config_changed = TRUE;
380   }
381
382   if (size_changed)
383     context_destroy_surfaces (context);
384   if (config_changed)
385     context_destroy (context);
386
387   if (size_changed && !context_create_surfaces (context))
388     return FALSE;
389   if (config_changed && !context_create (context))
390     return FALSE;
391   return TRUE;
392 }
393
394 /**
395  * gst_vaapi_context_get_id:
396  * @context: a #GstVaapiContext
397  *
398  * Returns the underlying VAContextID of the @context.
399  *
400  * Return value: the underlying VA context id
401  */
402 GstVaapiID
403 gst_vaapi_context_get_id (GstVaapiContext * context)
404 {
405   g_return_val_if_fail (context != NULL, VA_INVALID_ID);
406
407   return GST_VAAPI_OBJECT_ID (context);
408 }
409
410 /**
411  * gst_vaapi_context_get_surface_proxy:
412  * @context: a #GstVaapiContext
413  *
414  * Acquires a free surface, wrapped into a #GstVaapiSurfaceProxy. The
415  * returned surface will be automatically released when the proxy is
416  * destroyed. So, it is enough to call gst_vaapi_surface_proxy_unref()
417  * after usage.
418  *
419  * This function returns %NULL if there is no free surface available
420  * in the pool. The surfaces are pre-allocated during context creation
421  * though.
422  *
423  * Return value: a free surface, or %NULL if none is available
424  */
425 GstVaapiSurfaceProxy *
426 gst_vaapi_context_get_surface_proxy (GstVaapiContext * context)
427 {
428   g_return_val_if_fail (context != NULL, NULL);
429
430   return
431       gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL
432       (context->surfaces_pool));
433 }
434
435 /**
436  * gst_vaapi_context_get_surface_count:
437  * @context: a #GstVaapiContext
438  *
439  * Retrieves the number of free surfaces left in the pool.
440  *
441  * Return value: the number of free surfaces available in the pool
442  */
443 guint
444 gst_vaapi_context_get_surface_count (GstVaapiContext * context)
445 {
446   g_return_val_if_fail (context != NULL, 0);
447
448   return gst_vaapi_video_pool_get_size (context->surfaces_pool);
449 }