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