1 /*
2  *  gstvaapisurface.c - VA surface 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:gstvaapisurface
27  * @short_description: VA surface abstraction
28  */
29 
30 #include "sysdeps.h"
31 #include "gstvaapicompat.h"
32 #include "gstvaapiutils.h"
33 #include "gstvaapisurface.h"
34 #include "gstvaapisurface_priv.h"
35 #include "gstvaapicontext.h"
36 #include "gstvaapiimage.h"
37 #include "gstvaapiimage_priv.h"
38 #include "gstvaapicontext_overlay.h"
39 #include "gstvaapibufferproxy_priv.h"
40 
41 #define DEBUG 1
42 #include "gstvaapidebug.h"
43 
44 static gboolean
45 _gst_vaapi_surface_associate_subpicture (GstVaapiSurface * surface,
46     GstVaapiSubpicture * subpicture, const GstVaapiRectangle * src_rect,
47     const GstVaapiRectangle * dst_rect);
48 
49 static gboolean
50 _gst_vaapi_surface_deassociate_subpicture (GstVaapiSurface * surface,
51     GstVaapiSubpicture * subpicture);
52 
53 static void
destroy_subpicture_cb(gpointer subpicture,gpointer surface)54 destroy_subpicture_cb (gpointer subpicture, gpointer surface)
55 {
56   _gst_vaapi_surface_deassociate_subpicture (surface, subpicture);
57   gst_vaapi_object_unref (subpicture);
58 }
59 
60 static void
gst_vaapi_surface_destroy_subpictures(GstVaapiSurface * surface)61 gst_vaapi_surface_destroy_subpictures (GstVaapiSurface * surface)
62 {
63   if (surface->subpictures) {
64     g_ptr_array_foreach (surface->subpictures, destroy_subpicture_cb, surface);
65     g_ptr_array_free (surface->subpictures, TRUE);
66     surface->subpictures = NULL;
67   }
68 }
69 
70 static void
gst_vaapi_surface_destroy(GstVaapiSurface * surface)71 gst_vaapi_surface_destroy (GstVaapiSurface * surface)
72 {
73   GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (surface);
74   VASurfaceID surface_id;
75   VAStatus status;
76 
77   surface_id = GST_VAAPI_OBJECT_ID (surface);
78   GST_DEBUG ("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id));
79 
80   gst_vaapi_surface_destroy_subpictures (surface);
81   gst_vaapi_surface_set_parent_context (surface, NULL);
82 
83   if (surface_id != VA_INVALID_SURFACE) {
84     GST_VAAPI_DISPLAY_LOCK (display);
85     status = vaDestroySurfaces (GST_VAAPI_DISPLAY_VADISPLAY (display),
86         &surface_id, 1);
87     GST_VAAPI_DISPLAY_UNLOCK (display);
88     if (!vaapi_check_status (status, "vaDestroySurfaces()"))
89       GST_WARNING ("failed to destroy surface %" GST_VAAPI_ID_FORMAT,
90           GST_VAAPI_ID_ARGS (surface_id));
91     GST_VAAPI_OBJECT_ID (surface) = VA_INVALID_SURFACE;
92   }
93   gst_vaapi_buffer_proxy_replace (&surface->extbuf_proxy, NULL);
94 }
95 
96 static gboolean
gst_vaapi_surface_create(GstVaapiSurface * surface,GstVaapiChromaType chroma_type,guint width,guint height)97 gst_vaapi_surface_create (GstVaapiSurface * surface,
98     GstVaapiChromaType chroma_type, guint width, guint height)
99 {
100   GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (surface);
101   VASurfaceID surface_id;
102   VAStatus status;
103   guint va_chroma_format;
104 
105   va_chroma_format = from_GstVaapiChromaType (chroma_type);
106   if (!va_chroma_format)
107     goto error_unsupported_chroma_type;
108 
109   GST_VAAPI_DISPLAY_LOCK (display);
110   status = vaCreateSurfaces (GST_VAAPI_DISPLAY_VADISPLAY (display),
111       width, height, va_chroma_format, 1, &surface_id);
112   GST_VAAPI_DISPLAY_UNLOCK (display);
113   if (!vaapi_check_status (status, "vaCreateSurfaces()"))
114     return FALSE;
115 
116   surface->format = GST_VIDEO_FORMAT_UNKNOWN;
117   surface->chroma_type = chroma_type;
118   surface->width = width;
119   surface->height = height;
120 
121   GST_DEBUG ("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id));
122   GST_VAAPI_OBJECT_ID (surface) = surface_id;
123   return TRUE;
124 
125   /* ERRORS */
126 error_unsupported_chroma_type:
127   GST_ERROR ("unsupported chroma-type %u", chroma_type);
128   return FALSE;
129 }
130 
131 static gboolean
gst_vaapi_surface_create_full(GstVaapiSurface * surface,const GstVideoInfo * vip,guint flags)132 gst_vaapi_surface_create_full (GstVaapiSurface * surface,
133     const GstVideoInfo * vip, guint flags)
134 {
135   GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (surface);
136   const GstVideoFormat format = GST_VIDEO_INFO_FORMAT (vip);
137   VASurfaceID surface_id;
138   VAStatus status;
139   guint chroma_type, va_chroma_format, i;
140   const VAImageFormat *va_format;
141   VASurfaceAttrib attribs[3], *attrib;
142   VASurfaceAttribExternalBuffers extbuf;
143   gboolean extbuf_needed = FALSE;
144 
145   va_format = gst_vaapi_video_format_to_va_format (format);
146   if (!va_format)
147     goto error_unsupported_format;
148 
149   chroma_type = gst_vaapi_video_format_get_chroma_type (format);
150   if (!chroma_type)
151     goto error_unsupported_format;
152 
153   va_chroma_format = from_GstVaapiChromaType (chroma_type);
154   if (!va_chroma_format)
155     goto error_unsupported_format;
156 
157   memset (&extbuf, 0, sizeof (extbuf));
158   extbuf.pixel_format = va_format->fourcc;
159   extbuf.width = GST_VIDEO_INFO_WIDTH (vip);
160   extbuf.height = GST_VIDEO_INFO_HEIGHT (vip);
161   if (flags & GST_VAAPI_SURFACE_ALLOC_FLAG_LINEAR_STORAGE) {
162     extbuf.flags &= ~VA_SURFACE_EXTBUF_DESC_ENABLE_TILING;
163     extbuf_needed = TRUE;
164   }
165 
166   extbuf.num_planes = GST_VIDEO_INFO_N_PLANES (vip);
167   if (flags & GST_VAAPI_SURFACE_ALLOC_FLAG_FIXED_STRIDES) {
168     for (i = 0; i < extbuf.num_planes; i++)
169       extbuf.pitches[i] = GST_VIDEO_INFO_PLANE_STRIDE (vip, i);
170     extbuf_needed = TRUE;
171   }
172   if (flags & GST_VAAPI_SURFACE_ALLOC_FLAG_FIXED_OFFSETS) {
173     for (i = 0; i < extbuf.num_planes; i++)
174       extbuf.offsets[i] = GST_VIDEO_INFO_PLANE_OFFSET (vip, i);
175     extbuf_needed = TRUE;
176   }
177 
178   attrib = attribs;
179   attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
180   attrib->type = VASurfaceAttribPixelFormat;
181   attrib->value.type = VAGenericValueTypeInteger;
182   attrib->value.value.i = va_format->fourcc;
183   attrib++;
184 
185   if (extbuf_needed) {
186     attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
187     attrib->type = VASurfaceAttribMemoryType;
188     attrib->value.type = VAGenericValueTypeInteger;
189     attrib->value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_VA;
190     attrib++;
191 
192     attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
193     attrib->type = VASurfaceAttribExternalBufferDescriptor;
194     attrib->value.type = VAGenericValueTypePointer;
195     attrib->value.value.p = &extbuf;
196     attrib++;
197   }
198 
199   GST_VAAPI_DISPLAY_LOCK (display);
200   status = vaCreateSurfaces (GST_VAAPI_DISPLAY_VADISPLAY (display),
201       va_chroma_format, extbuf.width, extbuf.height, &surface_id, 1,
202       attribs, attrib - attribs);
203   GST_VAAPI_DISPLAY_UNLOCK (display);
204   if (!vaapi_check_status (status, "vaCreateSurfaces()"))
205     return FALSE;
206 
207   surface->format = format;
208   surface->chroma_type = chroma_type;
209   surface->width = extbuf.width;
210   surface->height = extbuf.height;
211 
212   GST_DEBUG ("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id));
213   GST_VAAPI_OBJECT_ID (surface) = surface_id;
214   return TRUE;
215 
216   /* ERRORS */
217 error_unsupported_format:
218   GST_ERROR ("unsupported format %s",
219       gst_vaapi_video_format_to_string (format));
220   return FALSE;
221 }
222 
223 static gboolean
gst_vaapi_surface_create_from_buffer_proxy(GstVaapiSurface * surface,GstVaapiBufferProxy * proxy,const GstVideoInfo * vip)224 gst_vaapi_surface_create_from_buffer_proxy (GstVaapiSurface * surface,
225     GstVaapiBufferProxy * proxy, const GstVideoInfo * vip)
226 {
227   GstVaapiDisplay *const display = GST_VAAPI_OBJECT_DISPLAY (surface);
228   GstVideoFormat format;
229   VASurfaceID surface_id;
230   VAStatus status;
231   guint chroma_type, va_chroma_format;
232   const VAImageFormat *va_format;
233   VASurfaceAttrib attribs[2], *attrib;
234   VASurfaceAttribExternalBuffers extbuf;
235   unsigned long extbuf_handle;
236   guint i, width, height;
237 
238   format = GST_VIDEO_INFO_FORMAT (vip);
239   width = GST_VIDEO_INFO_WIDTH (vip);
240   height = GST_VIDEO_INFO_HEIGHT (vip);
241 
242   gst_vaapi_buffer_proxy_replace (&surface->extbuf_proxy, proxy);
243 
244   va_format = gst_vaapi_video_format_to_va_format (format);
245   if (!va_format)
246     goto error_unsupported_format;
247 
248   chroma_type = gst_vaapi_video_format_get_chroma_type (format);
249   if (!chroma_type)
250     goto error_unsupported_format;
251 
252   va_chroma_format = from_GstVaapiChromaType (chroma_type);
253   if (!va_chroma_format)
254     goto error_unsupported_format;
255 
256   extbuf_handle = GST_VAAPI_BUFFER_PROXY_HANDLE (proxy);
257   extbuf.pixel_format = va_format->fourcc;
258   extbuf.width = width;
259   extbuf.height = height;
260   extbuf.data_size = GST_VAAPI_BUFFER_PROXY_SIZE (proxy);
261   extbuf.num_planes = GST_VIDEO_INFO_N_PLANES (vip);
262   for (i = 0; i < extbuf.num_planes; i++) {
263     extbuf.pitches[i] = GST_VIDEO_INFO_PLANE_STRIDE (vip, i);
264     extbuf.offsets[i] = GST_VIDEO_INFO_PLANE_OFFSET (vip, i);
265   }
266   extbuf.buffers = (uintptr_t *) & extbuf_handle;
267   extbuf.num_buffers = 1;
268   extbuf.flags = 0;
269   extbuf.private_data = NULL;
270 
271   attrib = attribs;
272   attrib->type = VASurfaceAttribExternalBufferDescriptor;
273   attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
274   attrib->value.type = VAGenericValueTypePointer;
275   attrib->value.value.p = &extbuf;
276   attrib++;
277   attrib->type = VASurfaceAttribMemoryType;
278   attrib->flags = VA_SURFACE_ATTRIB_SETTABLE;
279   attrib->value.type = VAGenericValueTypeInteger;
280   attrib->value.value.i =
281       from_GstVaapiBufferMemoryType (GST_VAAPI_BUFFER_PROXY_TYPE (proxy));
282   attrib++;
283 
284   GST_VAAPI_DISPLAY_LOCK (display);
285   status = vaCreateSurfaces (GST_VAAPI_DISPLAY_VADISPLAY (display),
286       va_chroma_format, width, height, &surface_id, 1, attribs,
287       attrib - attribs);
288   GST_VAAPI_DISPLAY_UNLOCK (display);
289   if (!vaapi_check_status (status, "vaCreateSurfaces()"))
290     return FALSE;
291 
292   surface->format = format;
293   surface->chroma_type = chroma_type;
294   surface->width = width;
295   surface->height = height;
296 
297   GST_DEBUG ("surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id));
298   GST_VAAPI_OBJECT_ID (surface) = surface_id;
299   return TRUE;
300 
301   /* ERRORS */
302 error_unsupported_format:
303   GST_ERROR ("unsupported format %s",
304       gst_vaapi_video_format_to_string (format));
305   return FALSE;
306 }
307 
308 #define gst_vaapi_surface_finalize gst_vaapi_surface_destroy
309 GST_VAAPI_OBJECT_DEFINE_CLASS (GstVaapiSurface, gst_vaapi_surface);
310 
311 /**
312  * gst_vaapi_surface_new_from_formats:
313  * @display: a #GstVaapiDisplay
314  * @chroma_type: the surface chroma format
315  * @width: the requested surface width
316  * @height: the requested surface height
317  * @formats: the limited format list
318  *
319  * Creates a new #GstVaapiSurface with a @chroma_type valid for any
320  * format in @formats; If there aren't any, the returned surface is
321  * created forcing the passed @chroma_type.
322  *
323  * Return value: the newly allocated #GstVaapiSurface object
324  */
325 GstVaapiSurface *
gst_vaapi_surface_new_from_formats(GstVaapiDisplay * display,GstVaapiChromaType chroma_type,guint width,guint height,GArray * formats)326 gst_vaapi_surface_new_from_formats (GstVaapiDisplay * display,
327     GstVaapiChromaType chroma_type, guint width, guint height, GArray * formats)
328 {
329   GstVaapiSurface *surface;
330   guint i;
331 
332   for (i = 0; i < formats->len; i++) {
333     GstVideoFormat format = g_array_index (formats, GstVideoFormat, i);
334     if (format == gst_vaapi_video_format_from_chroma (chroma_type))
335       return gst_vaapi_surface_new (display, chroma_type, width, height);
336   }
337 
338   /* Fallback: if there's no format valid for the chroma type let's
339    * just use the passed chroma */
340   surface = gst_vaapi_object_new (gst_vaapi_surface_class (), display);
341   if (!surface)
342     return NULL;
343   if (!gst_vaapi_surface_create (surface, chroma_type, width, height))
344     goto error;
345 
346   return surface;
347 
348   /* ERRORS */
349 error:
350   {
351     gst_vaapi_object_unref (surface);
352     return NULL;
353   }
354 }
355 
356 /**
357  * gst_vaapi_surface_new:
358  * @display: a #GstVaapiDisplay
359  * @chroma_type: the surface chroma format
360  * @width: the requested surface width
361  * @height: the requested surface height
362  *
363  * Creates a new #GstVaapiSurface with the specified chroma format and
364  * dimensions.
365  *
366  * Return value: the newly allocated #GstVaapiSurface object
367  */
368 GstVaapiSurface *
gst_vaapi_surface_new(GstVaapiDisplay * display,GstVaapiChromaType chroma_type,guint width,guint height)369 gst_vaapi_surface_new (GstVaapiDisplay * display,
370     GstVaapiChromaType chroma_type, guint width, guint height)
371 {
372   GstVaapiSurface *surface;
373 
374   GST_DEBUG ("size %ux%u, chroma type 0x%x", width, height, chroma_type);
375 
376   surface = gst_vaapi_object_new (gst_vaapi_surface_class (), display);
377   if (!surface)
378     return NULL;
379 
380   /* first try a recent version of vaCreateSurface, and later use as
381    * fallback its old version */
382   {
383     GstVideoInfo vi;
384     GstVideoFormat surface_format;
385 
386     surface_format = gst_vaapi_video_format_from_chroma (chroma_type);
387     gst_video_info_set_format (&vi, surface_format, width, height);
388 
389     if (gst_vaapi_surface_create_full (surface, &vi, 0))
390       return surface;
391   }
392   if (!gst_vaapi_surface_create (surface, chroma_type, width, height))
393     goto error;
394   return surface;
395 
396   /* ERRORS */
397 error:
398   {
399     gst_vaapi_object_unref (surface);
400     return NULL;
401   }
402 }
403 
404 /**
405  * gst_vaapi_surface_new_full:
406  * @display: a #GstVaapiDisplay
407  * @vip: the pointer to a #GstVideoInfo
408  * @flags: (optional) allocation flags
409  *
410  * Creates a new #GstVaapiSurface with the specified video information
411  * and optional #GstVaapiSurfaceAllocFlags
412  *
413  * Return value: the newly allocated #GstVaapiSurface object, or %NULL
414  *   if creation of VA surface with explicit pixel format is not
415  *   supported or failed.
416  */
417 GstVaapiSurface *
gst_vaapi_surface_new_full(GstVaapiDisplay * display,const GstVideoInfo * vip,guint flags)418 gst_vaapi_surface_new_full (GstVaapiDisplay * display,
419     const GstVideoInfo * vip, guint flags)
420 {
421   GstVaapiSurface *surface;
422 
423   GST_DEBUG ("size %ux%u, format %s, flags 0x%08x", GST_VIDEO_INFO_WIDTH (vip),
424       GST_VIDEO_INFO_HEIGHT (vip),
425       gst_vaapi_video_format_to_string (GST_VIDEO_INFO_FORMAT (vip)), flags);
426 
427   surface = gst_vaapi_object_new (gst_vaapi_surface_class (), display);
428   if (!surface)
429     return NULL;
430 
431   if (!gst_vaapi_surface_create_full (surface, vip, flags))
432     goto error;
433   return surface;
434 
435   /* ERRORS */
436 error:
437   {
438     gst_vaapi_object_unref (surface);
439     return NULL;
440   }
441 }
442 
443 /**
444  * gst_vaapi_surface_new_with_format:
445  * @display: a #GstVaapiDisplay
446  * @format: the surface format
447  * @width: the requested surface width
448  * @height: the requested surface height
449  *
450  * Creates a new #GstVaapiSurface with the specified pixel format and
451  * dimensions.
452  *
453  * Return value: the newly allocated #GstVaapiSurface object, or %NULL
454  *   if creation of VA surface with explicit pixel format is not
455  *   supported or failed.
456  */
457 GstVaapiSurface *
gst_vaapi_surface_new_with_format(GstVaapiDisplay * display,GstVideoFormat format,guint width,guint height)458 gst_vaapi_surface_new_with_format (GstVaapiDisplay * display,
459     GstVideoFormat format, guint width, guint height)
460 {
461   GstVideoInfo vi;
462 
463   gst_video_info_set_format (&vi, format, width, height);
464   return gst_vaapi_surface_new_full (display, &vi, 0);
465 }
466 
467 /**
468  * gst_vaapi_surface_new_from_buffer_proxy:
469  * @display: a #GstVaapiDisplay
470  * @proxy: a #GstVaapiBufferProxy
471  * @info: the #GstVideoInfo structure defining the layout of the buffer
472  *
473  * Creates a new #GstVaapiSurface with the supplied VA buffer proxy
474  * abstraction. The underlying VA buffer memory type could be anything
475  * that is supported by the VA driver.
476  *
477  * The resulting #GstVaapiSurface object owns an extra reference to
478  * the buffer @proxy, so the caller can safely release that handle as
479  * early as on return of this call.
480  *
481  * Return value: the newly allocated #GstVaapiSurface object, or %NULL
482  *   if creation of VA surface failed or is not supported
483  */
484 GstVaapiSurface *
gst_vaapi_surface_new_from_buffer_proxy(GstVaapiDisplay * display,GstVaapiBufferProxy * proxy,const GstVideoInfo * info)485 gst_vaapi_surface_new_from_buffer_proxy (GstVaapiDisplay * display,
486     GstVaapiBufferProxy * proxy, const GstVideoInfo * info)
487 {
488   GstVaapiSurface *surface;
489 
490   g_return_val_if_fail (proxy != NULL, NULL);
491   g_return_val_if_fail (info != NULL, NULL);
492 
493   surface = gst_vaapi_object_new (gst_vaapi_surface_class (), display);
494   if (!surface)
495     return NULL;
496 
497   if (!gst_vaapi_surface_create_from_buffer_proxy (surface, proxy, info))
498     goto error;
499   return surface;
500 
501   /* ERRORS */
502 error:
503   {
504     gst_vaapi_object_unref (surface);
505     return NULL;
506   }
507 }
508 
509 /**
510  * gst_vaapi_surface_get_id:
511  * @surface: a #GstVaapiSurface
512  *
513  * Returns the underlying VASurfaceID of the @surface.
514  *
515  * Return value: the underlying VA surface id
516  */
517 GstVaapiID
gst_vaapi_surface_get_id(GstVaapiSurface * surface)518 gst_vaapi_surface_get_id (GstVaapiSurface * surface)
519 {
520   g_return_val_if_fail (surface != NULL, VA_INVALID_SURFACE);
521 
522   return GST_VAAPI_OBJECT_ID (surface);
523 }
524 
525 /**
526  * gst_vaapi_surface_get_chroma_type:
527  * @surface: a #GstVaapiSurface
528  *
529  * Returns the #GstVaapiChromaType the @surface was created with.
530  *
531  * Return value: the #GstVaapiChromaType
532  */
533 GstVaapiChromaType
gst_vaapi_surface_get_chroma_type(GstVaapiSurface * surface)534 gst_vaapi_surface_get_chroma_type (GstVaapiSurface * surface)
535 {
536   g_return_val_if_fail (surface != NULL, 0);
537 
538   return GST_VAAPI_SURFACE_CHROMA_TYPE (surface);
539 }
540 
541 /**
542  * gst_vaapi_surface_get_format:
543  * @surface: a #GstVaapiSurface
544  *
545  * Returns the #GstVideoFormat the @surface was created with.
546  *
547  * Return value: the #GstVideoFormat, or %GST_VIDEO_FORMAT_ENCODED if
548  *   the surface was not created with an explicit video format, or if
549  *   the underlying video format could not be determined
550  */
551 GstVideoFormat
gst_vaapi_surface_get_format(GstVaapiSurface * surface)552 gst_vaapi_surface_get_format (GstVaapiSurface * surface)
553 {
554   g_return_val_if_fail (surface != NULL, 0);
555 
556   /* Try to determine the underlying VA surface format */
557   if (surface->format == GST_VIDEO_FORMAT_UNKNOWN) {
558     GstVaapiImage *const image = gst_vaapi_surface_derive_image (surface);
559     if (image) {
560       surface->format = GST_VAAPI_IMAGE_FORMAT (image);
561       gst_vaapi_object_unref (image);
562     }
563     if (surface->format == GST_VIDEO_FORMAT_UNKNOWN)
564       surface->format = GST_VIDEO_FORMAT_ENCODED;
565   }
566   return GST_VAAPI_SURFACE_FORMAT (surface);
567 }
568 
569 /**
570  * gst_vaapi_surface_get_width:
571  * @surface: a #GstVaapiSurface
572  *
573  * Returns the @surface width.
574  *
575  * Return value: the surface width, in pixels
576  */
577 guint
gst_vaapi_surface_get_width(GstVaapiSurface * surface)578 gst_vaapi_surface_get_width (GstVaapiSurface * surface)
579 {
580   g_return_val_if_fail (surface != NULL, 0);
581 
582   return GST_VAAPI_SURFACE_WIDTH (surface);
583 }
584 
585 /**
586  * gst_vaapi_surface_get_height:
587  * @surface: a #GstVaapiSurface
588  *
589  * Returns the @surface height.
590  *
591  * Return value: the surface height, in pixels.
592  */
593 guint
gst_vaapi_surface_get_height(GstVaapiSurface * surface)594 gst_vaapi_surface_get_height (GstVaapiSurface * surface)
595 {
596   g_return_val_if_fail (surface != NULL, 0);
597 
598   return GST_VAAPI_SURFACE_HEIGHT (surface);
599 }
600 
601 /**
602  * gst_vaapi_surface_get_size:
603  * @surface: a #GstVaapiSurface
604  * @width_ptr: return location for the width, or %NULL
605  * @height_ptr: return location for the height, or %NULL
606  *
607  * Retrieves the dimensions of a #GstVaapiSurface.
608  */
609 void
gst_vaapi_surface_get_size(GstVaapiSurface * surface,guint * width_ptr,guint * height_ptr)610 gst_vaapi_surface_get_size (GstVaapiSurface * surface,
611     guint * width_ptr, guint * height_ptr)
612 {
613   g_return_if_fail (surface != NULL);
614 
615   if (width_ptr)
616     *width_ptr = GST_VAAPI_SURFACE_WIDTH (surface);
617 
618   if (height_ptr)
619     *height_ptr = GST_VAAPI_SURFACE_HEIGHT (surface);
620 }
621 
622 /**
623  * gst_vaapi_surface_set_parent_context:
624  * @surface: a #GstVaapiSurface
625  * @context: a #GstVaapiContext
626  *
627  * Sets new parent context, or clears any parent context if @context
628  * is %NULL. This function owns an extra reference to the context,
629  * which will be released when the surface is destroyed.
630  */
631 void
gst_vaapi_surface_set_parent_context(GstVaapiSurface * surface,GstVaapiContext * context)632 gst_vaapi_surface_set_parent_context (GstVaapiSurface * surface,
633     GstVaapiContext * context)
634 {
635   g_return_if_fail (surface != NULL);
636 
637   surface->parent_context = NULL;
638 }
639 
640 /**
641  * gst_vaapi_surface_get_parent_context:
642  * @surface: a #GstVaapiSurface
643  *
644  * Retrieves the parent #GstVaapiContext, or %NULL if there is
645  * none. The surface shall still own a reference to the context.
646  * i.e. the caller shall not unreference the returned context object.
647  *
648  * Return value: the parent context, if any.
649  */
650 GstVaapiContext *
gst_vaapi_surface_get_parent_context(GstVaapiSurface * surface)651 gst_vaapi_surface_get_parent_context (GstVaapiSurface * surface)
652 {
653   g_return_val_if_fail (surface != NULL, NULL);
654 
655   return surface->parent_context;
656 }
657 
658 /**
659  * gst_vaapi_surface_derive_image:
660  * @surface: a #GstVaapiSurface
661  *
662  * Derives a #GstVaapiImage from the @surface. This image buffer can
663  * then be mapped/unmapped for direct CPU access. This operation is
664  * only possible if the underlying implementation supports direct
665  * rendering capabilities and internal surface formats that can be
666  * represented with a #GstVaapiImage.
667  *
668  * When the operation is not possible, the function returns %NULL and
669  * the user should then fallback to using gst_vaapi_surface_get_image()
670  * or gst_vaapi_surface_put_image() to accomplish the same task in an
671  * indirect manner (additional copy).
672  *
673  * An image created with gst_vaapi_surface_derive_image() should be
674  * unreferenced when it's no longer needed. The image and image buffer
675  * data structures will be destroyed. However, the surface contents
676  * will remain unchanged until destroyed through the last call to
677  * gst_vaapi_object_unref().
678  *
679  * Return value: the newly allocated #GstVaapiImage object, or %NULL
680  *   on failure
681  */
682 GstVaapiImage *
gst_vaapi_surface_derive_image(GstVaapiSurface * surface)683 gst_vaapi_surface_derive_image (GstVaapiSurface * surface)
684 {
685   GstVaapiDisplay *display;
686   VAImage va_image;
687   VAStatus status;
688   GstVaapiImage *image;
689 
690   g_return_val_if_fail (surface != NULL, NULL);
691 
692   display = GST_VAAPI_OBJECT_DISPLAY (surface);
693   va_image.image_id = VA_INVALID_ID;
694   va_image.buf = VA_INVALID_ID;
695 
696   GST_VAAPI_DISPLAY_LOCK (display);
697   status = vaDeriveImage (GST_VAAPI_DISPLAY_VADISPLAY (display),
698       GST_VAAPI_OBJECT_ID (surface), &va_image);
699   GST_VAAPI_DISPLAY_UNLOCK (display);
700   if (!vaapi_check_status (status, "vaDeriveImage()"))
701     return NULL;
702   if (va_image.image_id == VA_INVALID_ID || va_image.buf == VA_INVALID_ID)
703     return NULL;
704 
705   image = gst_vaapi_image_new_with_image (display, &va_image);
706   if (!image)
707     vaDestroyImage (GST_VAAPI_DISPLAY_VADISPLAY (display), va_image.image_id);
708   return image;
709 }
710 
711 /**
712  * gst_vaapi_surface_get_image
713  * @surface: a #GstVaapiSurface
714  * @image: a #GstVaapiImage
715  *
716  * Retrieves surface data into a #GstVaapiImage. The @image must have
717  * a format supported by the @surface.
718  *
719  * Return value: %TRUE on success
720  */
721 gboolean
gst_vaapi_surface_get_image(GstVaapiSurface * surface,GstVaapiImage * image)722 gst_vaapi_surface_get_image (GstVaapiSurface * surface, GstVaapiImage * image)
723 {
724   GstVaapiDisplay *display;
725   VAImageID image_id;
726   VAStatus status;
727   guint width, height;
728 
729   g_return_val_if_fail (surface != NULL, FALSE);
730   g_return_val_if_fail (image != NULL, FALSE);
731 
732   display = GST_VAAPI_OBJECT_DISPLAY (surface);
733   if (!display)
734     return FALSE;
735 
736   width = GST_VAAPI_IMAGE_WIDTH (image);
737   height = GST_VAAPI_IMAGE_HEIGHT (image);
738   if (width != surface->width || height != surface->height)
739     return FALSE;
740 
741   image_id = GST_VAAPI_OBJECT_ID (image);
742   if (image_id == VA_INVALID_ID)
743     return FALSE;
744 
745   GST_VAAPI_DISPLAY_LOCK (display);
746   status = vaGetImage (GST_VAAPI_DISPLAY_VADISPLAY (display),
747       GST_VAAPI_OBJECT_ID (surface), 0, 0, width, height, image_id);
748   GST_VAAPI_DISPLAY_UNLOCK (display);
749   if (!vaapi_check_status (status, "vaGetImage()"))
750     return FALSE;
751 
752   return TRUE;
753 }
754 
755 /**
756  * gst_vaapi_surface_put_image:
757  * @surface: a #GstVaapiSurface
758  * @image: a #GstVaapiImage
759  *
760  * Copies data from a #GstVaapiImage into a @surface. The @image must
761  * have a format supported by the @surface.
762  *
763  * Return value: %TRUE on success
764  */
765 gboolean
gst_vaapi_surface_put_image(GstVaapiSurface * surface,GstVaapiImage * image)766 gst_vaapi_surface_put_image (GstVaapiSurface * surface, GstVaapiImage * image)
767 {
768   GstVaapiDisplay *display;
769   VAImageID image_id;
770   VAStatus status;
771   guint width, height;
772 
773   g_return_val_if_fail (surface != NULL, FALSE);
774   g_return_val_if_fail (image != NULL, FALSE);
775 
776   display = GST_VAAPI_OBJECT_DISPLAY (surface);
777   if (!display)
778     return FALSE;
779 
780   width = GST_VAAPI_IMAGE_WIDTH (image);
781   height = GST_VAAPI_IMAGE_HEIGHT (image);
782   if (width != surface->width || height != surface->height)
783     return FALSE;
784 
785   image_id = GST_VAAPI_OBJECT_ID (image);
786   if (image_id == VA_INVALID_ID)
787     return FALSE;
788 
789   GST_VAAPI_DISPLAY_LOCK (display);
790   status = vaPutImage (GST_VAAPI_DISPLAY_VADISPLAY (display),
791       GST_VAAPI_OBJECT_ID (surface), image_id, 0, 0, width, height,
792       0, 0, width, height);
793   GST_VAAPI_DISPLAY_UNLOCK (display);
794   if (!vaapi_check_status (status, "vaPutImage()"))
795     return FALSE;
796 
797   return TRUE;
798 }
799 
800 /**
801  * gst_vaapi_surface_associate_subpicture:
802  * @surface: a #GstVaapiSurface
803  * @subpicture: a #GstVaapiSubpicture
804  * @src_rect: the sub-rectangle of the source subpicture
805  *   image to extract and process. If %NULL, the entire image will be used.
806  * @dst_rect: the sub-rectangle of the destination
807  *   surface into which the image is rendered. If %NULL, the entire
808  *   surface will be used.
809  *
810  * Associates the @subpicture with the @surface. The @src_rect
811  * coordinates and size are relative to the source image bound to
812  * @subpicture. The @dst_rect coordinates and size are relative to the
813  * target @surface. Note that the @surface holds an additional
814  * reference to the @subpicture.
815  *
816  * Return value: %TRUE on success
817  */
818 gboolean
gst_vaapi_surface_associate_subpicture(GstVaapiSurface * surface,GstVaapiSubpicture * subpicture,const GstVaapiRectangle * src_rect,const GstVaapiRectangle * dst_rect)819 gst_vaapi_surface_associate_subpicture (GstVaapiSurface * surface,
820     GstVaapiSubpicture * subpicture,
821     const GstVaapiRectangle * src_rect, const GstVaapiRectangle * dst_rect)
822 {
823   gboolean success;
824 
825   g_return_val_if_fail (surface != NULL, FALSE);
826   g_return_val_if_fail (subpicture != NULL, FALSE);
827 
828   if (!surface->subpictures) {
829     surface->subpictures = g_ptr_array_new ();
830     if (!surface->subpictures)
831       return FALSE;
832   }
833 
834   if (g_ptr_array_remove_fast (surface->subpictures, subpicture)) {
835     success = _gst_vaapi_surface_deassociate_subpicture (surface, subpicture);
836     gst_vaapi_object_unref (subpicture);
837     if (!success)
838       return FALSE;
839   }
840 
841   success = _gst_vaapi_surface_associate_subpicture (surface,
842       subpicture, src_rect, dst_rect);
843   if (!success)
844     return FALSE;
845 
846   g_ptr_array_add (surface->subpictures, gst_vaapi_object_ref (subpicture));
847   return TRUE;
848 }
849 
850 gboolean
_gst_vaapi_surface_associate_subpicture(GstVaapiSurface * surface,GstVaapiSubpicture * subpicture,const GstVaapiRectangle * src_rect,const GstVaapiRectangle * dst_rect)851 _gst_vaapi_surface_associate_subpicture (GstVaapiSurface * surface,
852     GstVaapiSubpicture * subpicture,
853     const GstVaapiRectangle * src_rect, const GstVaapiRectangle * dst_rect)
854 {
855   GstVaapiDisplay *display;
856   GstVaapiRectangle src_rect_default, dst_rect_default;
857   GstVaapiImage *image;
858   VASurfaceID surface_id;
859   VAStatus status;
860 
861   display = GST_VAAPI_OBJECT_DISPLAY (surface);
862   if (!display)
863     return FALSE;
864 
865   surface_id = GST_VAAPI_OBJECT_ID (surface);
866   if (surface_id == VA_INVALID_SURFACE)
867     return FALSE;
868 
869   if (!src_rect) {
870     image = gst_vaapi_subpicture_get_image (subpicture);
871     if (!image)
872       return FALSE;
873     src_rect = &src_rect_default;
874     src_rect_default.x = 0;
875     src_rect_default.y = 0;
876     src_rect_default.width = GST_VAAPI_IMAGE_WIDTH (image);
877     src_rect_default.height = GST_VAAPI_IMAGE_HEIGHT (image);
878   }
879 
880   if (!dst_rect) {
881     dst_rect = &dst_rect_default;
882     dst_rect_default.x = 0;
883     dst_rect_default.y = 0;
884     dst_rect_default.width = surface->width;
885     dst_rect_default.height = surface->height;
886   }
887 
888   GST_VAAPI_DISPLAY_LOCK (display);
889   status = vaAssociateSubpicture (GST_VAAPI_DISPLAY_VADISPLAY (display),
890       GST_VAAPI_OBJECT_ID (subpicture), &surface_id, 1,
891       src_rect->x, src_rect->y, src_rect->width, src_rect->height,
892       dst_rect->x, dst_rect->y, dst_rect->width, dst_rect->height,
893       from_GstVaapiSubpictureFlags (gst_vaapi_subpicture_get_flags
894           (subpicture)));
895   GST_VAAPI_DISPLAY_UNLOCK (display);
896   if (!vaapi_check_status (status, "vaAssociateSubpicture()"))
897     return FALSE;
898 
899   return TRUE;
900 }
901 
902 /**
903  * gst_vaapi_surface_deassociate_subpicture:
904  * @surface: a #GstVaapiSurface
905  * @subpicture: a #GstVaapiSubpicture
906  *
907  * Deassociates @subpicture from @surface. Other associations are kept.
908  *
909  * Return value: %TRUE on success
910  */
911 gboolean
gst_vaapi_surface_deassociate_subpicture(GstVaapiSurface * surface,GstVaapiSubpicture * subpicture)912 gst_vaapi_surface_deassociate_subpicture (GstVaapiSurface * surface,
913     GstVaapiSubpicture * subpicture)
914 {
915   gboolean success;
916 
917   g_return_val_if_fail (surface != NULL, FALSE);
918   g_return_val_if_fail (subpicture != NULL, FALSE);
919 
920   if (!surface->subpictures)
921     return TRUE;
922 
923   /* First, check subpicture was really associated with this surface */
924   if (!g_ptr_array_remove_fast (surface->subpictures, subpicture)) {
925     GST_DEBUG ("subpicture %" GST_VAAPI_ID_FORMAT " was not bound to "
926         "surface %" GST_VAAPI_ID_FORMAT,
927         GST_VAAPI_ID_ARGS (GST_VAAPI_OBJECT_ID (subpicture)),
928         GST_VAAPI_ID_ARGS (GST_VAAPI_OBJECT_ID (surface)));
929     return TRUE;
930   }
931 
932   success = _gst_vaapi_surface_deassociate_subpicture (surface, subpicture);
933   gst_vaapi_object_unref (subpicture);
934   return success;
935 }
936 
937 gboolean
_gst_vaapi_surface_deassociate_subpicture(GstVaapiSurface * surface,GstVaapiSubpicture * subpicture)938 _gst_vaapi_surface_deassociate_subpicture (GstVaapiSurface * surface,
939     GstVaapiSubpicture * subpicture)
940 {
941   GstVaapiDisplay *display;
942   VASurfaceID surface_id;
943   VAStatus status;
944 
945   display = GST_VAAPI_OBJECT_DISPLAY (surface);
946   if (!display)
947     return FALSE;
948 
949   surface_id = GST_VAAPI_OBJECT_ID (surface);
950   if (surface_id == VA_INVALID_SURFACE)
951     return FALSE;
952 
953   GST_VAAPI_DISPLAY_LOCK (display);
954   status = vaDeassociateSubpicture (GST_VAAPI_DISPLAY_VADISPLAY (display),
955       GST_VAAPI_OBJECT_ID (subpicture), &surface_id, 1);
956   GST_VAAPI_DISPLAY_UNLOCK (display);
957   if (!vaapi_check_status (status, "vaDeassociateSubpicture()"))
958     return FALSE;
959 
960   return TRUE;
961 }
962 
963 /**
964  * gst_vaapi_surface_sync:
965  * @surface: a #GstVaapiSurface
966  *
967  * Blocks until all pending operations on the @surface have been
968  * completed.
969  *
970  * Return value: %TRUE on success
971  */
972 gboolean
gst_vaapi_surface_sync(GstVaapiSurface * surface)973 gst_vaapi_surface_sync (GstVaapiSurface * surface)
974 {
975   GstVaapiDisplay *display;
976   VAStatus status;
977 
978   g_return_val_if_fail (surface != NULL, FALSE);
979 
980   display = GST_VAAPI_OBJECT_DISPLAY (surface);
981   if (!display)
982     return FALSE;
983 
984   GST_VAAPI_DISPLAY_LOCK (display);
985   status = vaSyncSurface (GST_VAAPI_DISPLAY_VADISPLAY (display),
986       GST_VAAPI_OBJECT_ID (surface));
987   GST_VAAPI_DISPLAY_UNLOCK (display);
988   if (!vaapi_check_status (status, "vaSyncSurface()"))
989     return FALSE;
990 
991   return TRUE;
992 }
993 
994 /**
995  * gst_vaapi_surface_query_status:
996  * @surface: a #GstVaapiSurface
997  * @pstatus: return location for the #GstVaapiSurfaceStatus
998  *
999  * Finds out any pending operations on the @surface. The
1000  * #GstVaapiSurfaceStatus flags are returned into @pstatus.
1001  *
1002  * Return value: %TRUE on success
1003  */
1004 gboolean
gst_vaapi_surface_query_status(GstVaapiSurface * surface,GstVaapiSurfaceStatus * pstatus)1005 gst_vaapi_surface_query_status (GstVaapiSurface * surface,
1006     GstVaapiSurfaceStatus * pstatus)
1007 {
1008   VASurfaceStatus surface_status;
1009   VAStatus status;
1010 
1011   g_return_val_if_fail (surface != NULL, FALSE);
1012 
1013   GST_VAAPI_OBJECT_LOCK_DISPLAY (surface);
1014   status = vaQuerySurfaceStatus (GST_VAAPI_OBJECT_VADISPLAY (surface),
1015       GST_VAAPI_OBJECT_ID (surface), &surface_status);
1016   GST_VAAPI_OBJECT_UNLOCK_DISPLAY (surface);
1017   if (!vaapi_check_status (status, "vaQuerySurfaceStatus()"))
1018     return FALSE;
1019 
1020   if (pstatus)
1021     *pstatus = to_GstVaapiSurfaceStatus (surface_status);
1022   return TRUE;
1023 }
1024 
1025 /**
1026  * gst_vaapi_surface_set_subpictures_from_composition:
1027  * @surface: a #GstVaapiSurface
1028  * @compostion: a #GstVideoOverlayCompositon
1029  * @propagate_context: a flag specifying whether to apply composition
1030  *     to the parent context, if any
1031  *
1032  * Helper to update the subpictures from #GstVideoOverlayCompositon. Sending
1033  * a NULL composition will clear all the current subpictures. Note that this
1034  * method will clear existing subpictures.
1035  *
1036  * Return value: %TRUE on success
1037  */
1038 gboolean
gst_vaapi_surface_set_subpictures_from_composition(GstVaapiSurface * surface,GstVideoOverlayComposition * composition,gboolean propagate_context)1039 gst_vaapi_surface_set_subpictures_from_composition (GstVaapiSurface * surface,
1040     GstVideoOverlayComposition * composition, gboolean propagate_context)
1041 {
1042   GstVaapiDisplay *display;
1043   guint n, nb_rectangles;
1044 
1045   g_return_val_if_fail (surface != NULL, FALSE);
1046 
1047   if (propagate_context && surface->parent_context)
1048     return gst_vaapi_context_apply_composition (surface->parent_context,
1049         composition);
1050 
1051   display = GST_VAAPI_OBJECT_DISPLAY (surface);
1052   if (!display)
1053     return FALSE;
1054 
1055   /* Clear current subpictures */
1056   gst_vaapi_surface_destroy_subpictures (surface);
1057 
1058   if (!composition)
1059     return TRUE;
1060 
1061   nb_rectangles = gst_video_overlay_composition_n_rectangles (composition);
1062 
1063   /* Overlay all the rectangles cantained in the overlay composition */
1064   for (n = 0; n < nb_rectangles; ++n) {
1065     GstVideoOverlayRectangle *rect;
1066     GstVaapiRectangle sub_rect;
1067     GstVaapiSubpicture *subpicture;
1068 
1069     rect = gst_video_overlay_composition_get_rectangle (composition, n);
1070     subpicture = gst_vaapi_subpicture_new_from_overlay_rectangle (display,
1071         rect);
1072 
1073     gst_video_overlay_rectangle_get_render_rectangle (rect,
1074         (gint *) & sub_rect.x, (gint *) & sub_rect.y,
1075         &sub_rect.width, &sub_rect.height);
1076 
1077     /* ensure that the overlay is not bigger than the surface */
1078     sub_rect.y = MIN (sub_rect.y, surface->height);
1079     sub_rect.width = MIN (sub_rect.width, surface->width);
1080 
1081     if (!gst_vaapi_surface_associate_subpicture (surface, subpicture,
1082             NULL, &sub_rect)) {
1083       GST_WARNING ("could not render overlay rectangle %p", rect);
1084       gst_vaapi_object_unref (subpicture);
1085       return FALSE;
1086     }
1087     gst_vaapi_object_unref (subpicture);
1088   }
1089   return TRUE;
1090 }
1091 
1092 /**
1093  * gst_vaapi_surface_set_buffer_proxy:
1094  * @surface: a #GstVaapiSurface
1095  * @proxy: an external #GstVaapiBufferProxy
1096  *
1097  * Replaces the external buffer proxy in @surface with @proxy.
1098  *
1099  * This is useful when a dmabuf-based memory is instantiated in order
1100  * to relate the generated buffer @proxy with the processed @surface.
1101  **/
1102 void
gst_vaapi_surface_set_buffer_proxy(GstVaapiSurface * surface,GstVaapiBufferProxy * proxy)1103 gst_vaapi_surface_set_buffer_proxy (GstVaapiSurface * surface,
1104     GstVaapiBufferProxy * proxy)
1105 {
1106   gst_vaapi_buffer_proxy_replace (&surface->extbuf_proxy, proxy);
1107 }
1108 
1109 /**
1110  * gst_vaapi_surface_peek_buffer_proxy:
1111  * @surface: a #GstVaapiSurface
1112  *
1113  * This is useful when a dmabuf-based memory is instantiated in order
1114  * to relate the generated buffer @proxy with the processed @surface.
1115  *
1116  * Returns: (transfer none): the associated external
1117  * #GstVaapiBufferProxy
1118  **/
1119 GstVaapiBufferProxy *
gst_vaapi_surface_peek_buffer_proxy(GstVaapiSurface * surface)1120 gst_vaapi_surface_peek_buffer_proxy (GstVaapiSurface * surface)
1121 {
1122   return surface->extbuf_proxy;
1123 }
1124