plugins: drop gstvaapipluginbuffer.[ch] helper files.
[vaapi:gstreamer-vaapi.git] / gst / vaapi / gstvaapiuploader.c
1 /*
2  *  gstvaapiuploader.c - VA-API video upload helper
3  *
4  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
5  *  Copyright (C) 2011-2013 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 #include "gst/vaapi/sysdeps.h"
24 #include <string.h>
25 #include <gst/video/video.h>
26 #include <gst/vaapi/gstvaapisurface.h>
27 #include <gst/vaapi/gstvaapiimagepool.h>
28 #include <gst/vaapi/gstvaapisurfacepool.h>
29 #include <gst/vaapi/gstvaapivideometa.h>
30
31 #include "gstvaapiuploader.h"
32 #include "gstvaapivideobuffer.h"
33
34 #define GST_HELPER_NAME "vaapiupload"
35 #define GST_HELPER_DESC "VA-API video uploader"
36
37 GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapi_uploader);
38 #define GST_CAT_DEFAULT gst_debug_vaapi_uploader
39
40 G_DEFINE_TYPE(GstVaapiUploader, gst_vaapi_uploader, G_TYPE_OBJECT)
41
42 #define GST_VAAPI_UPLOADER_CAST(obj) \
43     ((GstVaapiUploader *)(obj))
44
45 #define GST_VAAPI_UPLOADER_GET_PRIVATE(obj)                     \
46     (G_TYPE_INSTANCE_GET_PRIVATE((obj),                         \
47                                  GST_VAAPI_TYPE_UPLOADER,       \
48                                  GstVaapiUploaderPrivate))
49
50 struct _GstVaapiUploaderPrivate {
51     GstVaapiDisplay    *display;
52     GstCaps            *allowed_caps;
53     GstVaapiVideoPool  *images;
54     GstCaps            *image_caps;
55     guint               image_width;
56     guint               image_height;
57     GstVaapiVideoPool  *surfaces;
58     guint               surface_width;
59     guint               surface_height;
60     guint               direct_rendering;
61 };
62
63 enum {
64     PROP_0,
65
66     PROP_DISPLAY,
67 };
68
69 static void
70 gst_vaapi_uploader_destroy(GstVaapiUploader *uploader)
71 {
72     GstVaapiUploaderPrivate * const priv = uploader->priv;
73
74     gst_caps_replace(&priv->image_caps, NULL);
75     gst_caps_replace(&priv->allowed_caps, NULL);
76
77     g_clear_object(&priv->images);
78     g_clear_object(&priv->surfaces);
79     g_clear_object(&priv->display);
80 }
81
82 static gboolean
83 ensure_display(GstVaapiUploader *uploader, GstVaapiDisplay *display)
84 {
85     GstVaapiUploaderPrivate * const priv = uploader->priv;
86
87     if (priv->display == display)
88         return TRUE;
89
90     g_clear_object(&priv->display);
91     if (display)
92         priv->display = g_object_ref(display);
93     return TRUE;
94 }
95
96 static gboolean
97 ensure_image(GstVaapiImage *image)
98 {
99     guint i, num_planes, width, height;
100
101     /* Make the image fully dirty */
102     if (!gst_vaapi_image_map(image))
103         return FALSE;
104
105     gst_vaapi_image_get_size(image, &width, &height);
106
107     num_planes = gst_vaapi_image_get_plane_count(image);
108     for (i = 0; i < num_planes; i++) {
109         guchar * const plane = gst_vaapi_image_get_plane(image, i);
110         if (plane)
111             memset(plane, 0, gst_vaapi_image_get_pitch(image, i));
112     }
113
114     if (!gst_vaapi_image_unmap(image))
115         gst_vaapi_image_unmap(image);
116     return TRUE;
117 }
118
119 static gboolean
120 ensure_allowed_caps(GstVaapiUploader *uploader)
121 {
122     GstVaapiUploaderPrivate * const priv = uploader->priv;
123     GstVaapiSurface *surface = NULL;
124     GstCaps *out_caps, *image_caps = NULL;
125     guint i, n_structures;
126     gboolean success = FALSE;
127
128     enum { WIDTH = 64, HEIGHT = 64 };
129
130     if (priv->allowed_caps)
131         return TRUE;
132
133     out_caps = gst_caps_new_empty();
134     if (!out_caps)
135         return FALSE;
136
137     image_caps = gst_vaapi_display_get_image_caps(priv->display);
138     if (!image_caps)
139         goto end;
140
141     surface = gst_vaapi_surface_new(priv->display,
142         GST_VAAPI_CHROMA_TYPE_YUV420, WIDTH, HEIGHT);
143     if (!surface)
144         goto end;
145
146     n_structures = gst_caps_get_size(image_caps);
147     for (i = 0; i < n_structures; i++) {
148         GstStructure * const structure = gst_caps_get_structure(image_caps, i);
149         GstVaapiImage *image;
150         GstVaapiImageFormat format;
151
152         format = gst_vaapi_image_format_from_structure(structure);
153         if (!format)
154             continue;
155         image = gst_vaapi_image_new(priv->display, format, WIDTH, HEIGHT);
156         if (!image)
157             continue;
158         if (ensure_image(image) && gst_vaapi_surface_put_image(surface, image))
159             gst_caps_append_structure(out_caps, gst_structure_copy(structure));
160         gst_object_unref(image);
161     }
162
163     gst_caps_replace(&priv->allowed_caps, out_caps);
164     success = TRUE;
165
166 end:
167     gst_caps_unref(out_caps);
168     if (image_caps)
169         gst_caps_unref(image_caps);
170     if (surface)
171         gst_object_unref(surface);
172     return success;
173 }
174
175 static gboolean
176 ensure_image_pool(GstVaapiUploader *uploader, GstCaps *caps)
177 {
178     GstVaapiUploaderPrivate * const priv = uploader->priv;
179     GstStructure * const structure = gst_caps_get_structure(caps, 0);
180     gint width, height;
181
182     gst_structure_get_int(structure, "width",  &width);
183     gst_structure_get_int(structure, "height", &height);
184
185     if (width != priv->image_width || height != priv->image_height) {
186         priv->image_width  = width;
187         priv->image_height = height;
188         g_clear_object(&priv->images);
189         priv->images = gst_vaapi_image_pool_new(priv->display, caps);
190         if (!priv->images)
191             return FALSE;
192         gst_caps_replace(&priv->image_caps, caps);
193     }
194     return TRUE;
195 }
196
197 static gboolean
198 ensure_surface_pool(GstVaapiUploader *uploader, GstCaps *caps)
199 {
200     GstVaapiUploaderPrivate * const priv = uploader->priv;
201     GstStructure * const structure = gst_caps_get_structure(caps, 0);
202     gint width, height;
203
204     gst_structure_get_int(structure, "width",  &width);
205     gst_structure_get_int(structure, "height", &height);
206
207     if (width != priv->surface_width || height != priv->surface_height) {
208         priv->surface_width  = width;
209         priv->surface_height = height;
210         g_clear_object(&priv->surfaces);
211         priv->surfaces = gst_vaapi_surface_pool_new(priv->display, caps);
212         if (!priv->surfaces)
213             return FALSE;
214     }
215     return TRUE;
216 }
217
218 static void
219 gst_vaapi_uploader_finalize(GObject *object)
220 {
221     gst_vaapi_uploader_destroy(GST_VAAPI_UPLOADER_CAST(object));
222
223     G_OBJECT_CLASS(gst_vaapi_uploader_parent_class)->finalize(object);
224 }
225
226 static void
227 gst_vaapi_uploader_set_property(GObject *object, guint prop_id,
228     const GValue *value, GParamSpec *pspec)
229 {
230     GstVaapiUploader * const uploader = GST_VAAPI_UPLOADER_CAST(object);
231
232     switch (prop_id) {
233     case PROP_DISPLAY:
234         ensure_display(uploader, g_value_get_object(value));
235         break;
236     default:
237         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
238         break;
239     }
240 }
241
242 static void
243 gst_vaapi_uploader_get_property(GObject *object, guint prop_id,
244     GValue *value, GParamSpec *pspec)
245 {
246     GstVaapiUploader * const uploader = GST_VAAPI_UPLOADER_CAST(object);
247
248     switch (prop_id) {
249     case PROP_DISPLAY:
250         g_value_set_object(value, uploader->priv->display);
251         break;
252     default:
253         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
254         break;
255     }
256 }
257
258 static void
259 gst_vaapi_uploader_class_init(GstVaapiUploaderClass *klass)
260 {
261     GObjectClass * const object_class = G_OBJECT_CLASS(klass);
262
263     GST_DEBUG_CATEGORY_INIT(gst_debug_vaapi_uploader,
264         GST_HELPER_NAME, 0, GST_HELPER_DESC);
265
266     g_type_class_add_private(klass, sizeof(GstVaapiUploaderPrivate));
267
268     object_class->finalize      = gst_vaapi_uploader_finalize;
269     object_class->set_property = gst_vaapi_uploader_set_property;
270     object_class->get_property = gst_vaapi_uploader_get_property;
271
272     g_object_class_install_property(
273         object_class,
274         PROP_DISPLAY,
275         g_param_spec_object(
276             "display",
277             "Display",
278             "The GstVaapiDisplay this object is bound to",
279             GST_VAAPI_TYPE_DISPLAY,
280             G_PARAM_READWRITE));
281 }
282
283 static void
284 gst_vaapi_uploader_init(GstVaapiUploader *uploader)
285 {
286     GstVaapiUploaderPrivate *priv;
287
288     priv                = GST_VAAPI_UPLOADER_GET_PRIVATE(uploader);
289     uploader->priv      = priv;
290 }
291
292 GstVaapiUploader *
293 gst_vaapi_uploader_new(GstVaapiDisplay *display)
294 {
295     return g_object_new(GST_VAAPI_TYPE_UPLOADER, "display", display, NULL);
296 }
297
298 gboolean
299 gst_vaapi_uploader_ensure_display(
300     GstVaapiUploader *uploader,
301     GstVaapiDisplay  *display
302 )
303 {
304     g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), FALSE);
305     g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
306
307     return ensure_display(uploader,display);
308 }
309
310 gboolean
311 gst_vaapi_uploader_ensure_caps(
312     GstVaapiUploader *uploader,
313     GstCaps          *src_caps,
314     GstCaps          *out_caps
315 )
316 {
317     GstVaapiUploaderPrivate *priv;
318     GstVaapiImage *image;
319     GstVaapiImageFormat vaformat;
320     GstVideoInfo vi;
321
322     g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), FALSE);
323     g_return_val_if_fail(src_caps != NULL, FALSE);
324
325     if (!ensure_image_pool(uploader, src_caps))
326         return FALSE;
327     if (!ensure_surface_pool(uploader, out_caps ? out_caps : src_caps))
328         return FALSE;
329
330     priv = uploader->priv;
331     priv->direct_rendering = 0;
332
333     /* Translate from Gst video format to VA image format */
334     if (!gst_video_info_from_caps(&vi, src_caps))
335         return FALSE;
336     if (!GST_VIDEO_INFO_IS_YUV(&vi))
337         return FALSE;
338     vaformat = gst_vaapi_image_format_from_video(GST_VIDEO_INFO_FORMAT(&vi));
339     if (!vaformat)
340         return FALSE;
341
342     /* Check if we can alias source and output buffers (same data_size) */
343     image = gst_vaapi_video_pool_get_object(priv->images);
344     if (image) {
345         if (gst_vaapi_image_get_format(image) == vaformat &&
346             gst_vaapi_image_is_linear(image) &&
347             gst_vaapi_image_get_data_size(image) == GST_VIDEO_INFO_SIZE(&vi))
348             priv->direct_rendering = 1;
349         gst_vaapi_video_pool_put_object(priv->images, image);
350     }
351     return TRUE;
352 }
353
354 gboolean
355 gst_vaapi_uploader_process(
356     GstVaapiUploader *uploader,
357     GstBuffer        *src_buffer,
358     GstBuffer        *out_buffer
359 )
360 {
361     GstVaapiVideoMeta *src_meta, *out_meta;
362     GstVaapiSurface *surface;
363     GstVaapiImage *image;
364
365     g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), FALSE);
366
367     out_meta = gst_buffer_get_vaapi_video_meta(out_buffer);
368     if (!out_meta) {
369         GST_WARNING("expected an output video buffer");
370         return FALSE;
371     }
372
373     surface = gst_vaapi_video_meta_get_surface(out_meta);
374     g_return_val_if_fail(surface != NULL, FALSE);
375
376     src_meta = gst_buffer_get_vaapi_video_meta(src_buffer);
377     if (src_meta) {
378         /* GstVaapiVideoBuffer with mapped VA image */
379         image = gst_vaapi_video_meta_get_image(src_meta);
380         if (!image || !gst_vaapi_image_unmap(image))
381             return FALSE;
382     }
383     else {
384         /* Regular GstBuffer that needs to be uploaded to a VA image */
385         image = gst_vaapi_video_meta_get_image(out_meta);
386         if (!image) {
387             image = gst_vaapi_video_pool_get_object(uploader->priv->images);
388             if (!image)
389                 return FALSE;
390             gst_vaapi_video_meta_set_image(out_meta, image);
391         }
392         if (!gst_vaapi_image_update_from_buffer(image, src_buffer, NULL))
393             return FALSE;
394     }
395     g_return_val_if_fail(image != NULL, FALSE);
396
397     if (!gst_vaapi_surface_put_image(surface, image)) {
398         GST_WARNING("failed to upload YUV buffer to VA surface");
399         return FALSE;
400     }
401
402     /* Map again for next uploads */
403     if (!gst_vaapi_image_map(image))
404         return FALSE;
405     return TRUE;
406 }
407
408 GstCaps *
409 gst_vaapi_uploader_get_caps(GstVaapiUploader *uploader)
410 {
411     g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), NULL);
412
413     if (!ensure_allowed_caps(uploader))
414         return NULL;
415     return uploader->priv->allowed_caps;
416 }
417
418 GstBuffer *
419 gst_vaapi_uploader_get_buffer(GstVaapiUploader *uploader)
420 {
421     GstVaapiUploaderPrivate *priv;
422     GstVaapiSurface *surface;
423     GstVaapiImage *image;
424     GstVaapiVideoMeta *meta;
425     GstBuffer *buffer;
426
427     g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), NULL);
428
429     priv = uploader->priv;
430
431     buffer = gst_vaapi_video_buffer_new_from_pool(priv->images);
432     if (!buffer) {
433         GST_WARNING("failed to allocate video buffer");
434         goto error;
435     }
436
437     meta = gst_buffer_get_vaapi_video_meta(buffer);
438
439     surface = gst_vaapi_video_pool_get_object(priv->surfaces);
440     if (!surface) {
441         GST_WARNING("failed to allocate VA surface");
442         goto error;
443     }
444
445     gst_vaapi_video_meta_set_surface(meta, surface);
446
447     image = gst_vaapi_video_meta_get_image(meta);
448     if (!gst_vaapi_image_map(image)) {
449         GST_WARNING("failed to map VA image");
450         goto error;
451     }
452
453     GST_BUFFER_DATA(buffer) = gst_vaapi_image_get_plane(image, 0);
454     GST_BUFFER_SIZE(buffer) = gst_vaapi_image_get_data_size(image);
455
456     gst_buffer_set_caps(buffer, priv->image_caps);
457     return buffer;
458
459 error:
460     gst_buffer_unref(buffer);
461     return buffer;
462 }
463
464 gboolean
465 gst_vaapi_uploader_has_direct_rendering(GstVaapiUploader *uploader)
466 {
467     g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), FALSE);
468
469     return uploader->priv->direct_rendering;
470 }