2 * gstvaapifilter.c - Video processing abstraction
4 * Copyright (C) 2013 Intel Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1
9 * of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
23 #include "gstvaapifilter.h"
24 #include "gstvaapiutils.h"
25 #include "gstvaapiminiobject.h"
26 #include "gstvaapidisplay_priv.h"
27 #include "gstvaapisurface_priv.h"
30 # include <va/va_vpp.h>
34 #include "gstvaapidebug.h"
36 #define GST_VAAPI_FILTER(obj) \
37 ((GstVaapiFilter *)(obj))
39 typedef struct _GstVaapiFilterOpData GstVaapiFilterOpData;
40 struct _GstVaapiFilterOpData {
43 volatile gint ref_count;
54 struct _GstVaapiFilter {
56 GstVaapiMiniObject parent_instance;
58 GstVaapiDisplay *display;
61 VAContextID va_context;
62 GPtrArray *operations;
63 GstVideoFormat format;
67 /* ------------------------------------------------------------------------- */
68 /* --- VPP Helpers --- */
69 /* ------------------------------------------------------------------------- */
72 static VAProcFilterType *
73 vpp_get_filters_unlocked(GstVaapiFilter *filter, guint *num_filters_ptr)
75 VAProcFilterType *filters = NULL;
76 guint num_filters = 0;
79 num_filters = VAProcFilterCount;
80 filters = g_malloc_n(num_filters, sizeof(*filters));
84 va_status = vaQueryVideoProcFilters(filter->va_display, filter->va_context,
85 filters, &num_filters);
87 // Try to reallocate to the expected number of filters
88 if (va_status == VA_STATUS_ERROR_MAX_NUM_EXCEEDED) {
89 VAProcFilterType * const new_filters =
90 g_try_realloc_n(filters, num_filters, sizeof(*new_filters));
93 filters = new_filters;
95 va_status = vaQueryVideoProcFilters(filter->va_display,
96 filter->va_context, filters, &num_filters);
98 if (!vaapi_check_status(va_status, "vaQueryVideoProcFilters()"))
101 *num_filters_ptr = num_filters;
109 static VAProcFilterType *
110 vpp_get_filters(GstVaapiFilter *filter, guint *num_filters_ptr)
112 VAProcFilterType *filters;
114 GST_VAAPI_DISPLAY_LOCK(filter->display);
115 filters = vpp_get_filters_unlocked(filter, num_filters_ptr);
116 GST_VAAPI_DISPLAY_UNLOCK(filter->display);
121 /* ------------------------------------------------------------------------- */
122 /* --- VPP Operations --- */
123 /* ------------------------------------------------------------------------- */
126 #define DEFAULT_FORMAT GST_VIDEO_FORMAT_UNKNOWN
131 PROP_FORMAT = GST_VAAPI_FILTER_OP_FORMAT,
136 static GParamSpec *g_properties[N_PROPERTIES] = { NULL, };
137 static gsize g_properties_initialized = FALSE;
140 init_properties(void)
143 * GstVaapiFilter:format:
145 * The forced output pixel format, expressed as a #GstVideoFormat.
147 g_properties[PROP_FORMAT] =
148 g_param_spec_enum("format",
150 "The forced output pixel format",
151 GST_TYPE_VIDEO_FORMAT,
157 ensure_properties(void)
159 if (g_once_init_enter(&g_properties_initialized)) {
161 g_once_init_leave(&g_properties_initialized, TRUE);
166 op_data_free(GstVaapiFilterOpData *op_data)
168 g_free(op_data->va_caps);
169 g_slice_free(GstVaapiFilterOpData, op_data);
172 static inline gpointer
173 op_data_new(GstVaapiFilterOp op, GParamSpec *pspec)
175 GstVaapiFilterOpData *op_data;
177 op_data = g_slice_new0(GstVaapiFilterOpData);
182 op_data->pspec = pspec;
183 op_data->ref_count = 1;
184 op_data->va_buffer = VA_INVALID_ID;
187 case GST_VAAPI_FILTER_OP_FORMAT:
188 op_data->va_type = VAProcFilterNone;
191 g_assert(0 && "unsupported operation");
197 op_data_free(op_data);
201 static inline gpointer
202 op_data_ref(gpointer data)
204 GstVaapiFilterOpData * const op_data = data;
206 g_return_val_if_fail(op_data != NULL, NULL);
208 g_atomic_int_inc(&op_data->ref_count);
213 op_data_unref(gpointer data)
215 GstVaapiFilterOpData * const op_data = data;
217 g_return_if_fail(op_data != NULL);
218 g_return_if_fail(op_data->ref_count > 0);
220 if (g_atomic_int_dec_and_test(&op_data->ref_count))
221 op_data_free(op_data);
224 /* Get default list of operations supported by the library */
226 get_operations_default(void)
231 ops = g_ptr_array_new_full(N_PROPERTIES, op_data_unref);
237 for (i = 0; i < N_PROPERTIES; i++) {
238 GParamSpec * const pspec = g_properties[i];
242 GstVaapiFilterOpData * const op_data = op_data_new(i, pspec);
245 g_ptr_array_add(ops, op_data);
250 g_ptr_array_unref(ops);
254 /* Get the ordered list of operations, based on VA/VPP queries */
256 get_operations_ordered(GstVaapiFilter *filter, GPtrArray *default_ops)
259 VAProcFilterType *filters;
260 guint i, j, num_filters;
262 ops = g_ptr_array_new_full(default_ops->len, op_data_unref);
266 filters = vpp_get_filters(filter, &num_filters);
270 // Append virtual ops first, i.e. those without an associated VA filter
271 for (i = 0; i < default_ops->len; i++) {
272 GstVaapiFilterOpData * const op_data =
273 g_ptr_array_index(default_ops, i);
274 if (op_data->va_type == VAProcFilterNone)
275 g_ptr_array_add(ops, op_data_ref(op_data));
278 // Append ops, while preserving the VA filters ordering
279 for (i = 0; i < num_filters; i++) {
280 const VAProcFilterType va_type = filters[i];
281 if (va_type == VAProcFilterNone)
284 for (j = 0; j < default_ops->len; j++) {
285 GstVaapiFilterOpData * const op_data =
286 g_ptr_array_index(default_ops, j);
287 if (op_data->va_type != va_type)
289 g_ptr_array_add(ops, op_data_ref(op_data));
293 if (filter->operations)
294 g_ptr_array_unref(filter->operations);
295 filter->operations = g_ptr_array_ref(ops);
298 g_ptr_array_unref(default_ops);
303 g_ptr_array_unref(ops);
304 g_ptr_array_unref(default_ops);
308 /* Determine the set of supported VPP operations by the specific
309 filter, or known to this library if filter is NULL */
311 ensure_operations(GstVaapiFilter *filter)
315 if (filter && filter->operations)
316 return g_ptr_array_ref(filter->operations);
318 ops = get_operations_default();
321 return filter ? get_operations_ordered(filter, ops) : ops;
325 /* Find whether the VPP operation is supported or not */
326 GstVaapiFilterOpData *
327 find_operation(GstVaapiFilter *filter, GstVaapiFilterOp op)
331 if (!filter->operations)
334 for (i = 0; i < filter->operations->len; i++) {
335 GstVaapiFilterOpData * const op_data =
336 g_ptr_array_index(filter->operations, i);
337 if (op_data->op == op)
343 /* ------------------------------------------------------------------------- */
344 /* --- Surface Formats --- */
345 /* ------------------------------------------------------------------------- */
348 ensure_formats(GstVaapiFilter *filter)
350 VASurfaceAttrib *surface_attribs = NULL;
351 guint i, num_surface_attribs = 0;
354 if (G_LIKELY(filter->formats))
355 return filter->formats;
357 #if VA_CHECK_VERSION(0,34,0)
358 GST_VAAPI_DISPLAY_LOCK(filter->display);
359 va_status = vaQuerySurfaceAttributes(filter->va_display, filter->va_config,
360 NULL, &num_surface_attribs);
361 GST_VAAPI_DISPLAY_UNLOCK(filter->display);
362 if (!vaapi_check_status(va_status, "vaQuerySurfaceAttributes()"))
365 surface_attribs = g_malloc(num_surface_attribs * sizeof(*surface_attribs));
366 if (!surface_attribs)
369 GST_VAAPI_DISPLAY_LOCK(filter->display);
370 va_status = vaQuerySurfaceAttributes(filter->va_display, filter->va_config,
371 surface_attribs, &num_surface_attribs);
372 GST_VAAPI_DISPLAY_UNLOCK(filter->display);
373 if (!vaapi_check_status(va_status, "vaQuerySurfaceAttributes()"))
376 filter->formats = g_array_sized_new(FALSE, FALSE, sizeof(GstVideoFormat),
377 num_surface_attribs);
378 if (!filter->formats)
381 for (i = 0; i < num_surface_attribs; i++) {
382 const VASurfaceAttrib * const surface_attrib = &surface_attribs[i];
383 GstVideoFormat format;
385 if (surface_attrib->type != VASurfaceAttribPixelFormat)
387 if (!(surface_attrib->flags & VA_SURFACE_ATTRIB_SETTABLE))
390 format = gst_vaapi_video_format_from_va_fourcc(
391 surface_attrib->value.value.i);
392 if (format == GST_VIDEO_FORMAT_UNKNOWN)
394 g_array_append_val(filter->formats, format);
398 g_free(surface_attribs);
399 return filter->formats;
402 g_free(surface_attribs);
406 static inline gboolean
407 is_special_format(GstVideoFormat format)
409 return format == GST_VIDEO_FORMAT_UNKNOWN ||
410 format == GST_VIDEO_FORMAT_ENCODED;
414 find_format(GstVaapiFilter *filter, GstVideoFormat format)
418 if (is_special_format(format) || !filter->formats)
421 for (i = 0; i < filter->formats->len; i++) {
422 if (g_array_index(filter->formats, GstVideoFormat, i) == format)
428 /* ------------------------------------------------------------------------- */
429 /* --- Interface --- */
430 /* ------------------------------------------------------------------------- */
434 gst_vaapi_filter_init(GstVaapiFilter *filter, GstVaapiDisplay *display)
438 filter->display = gst_vaapi_display_ref(display);
439 filter->va_display = GST_VAAPI_DISPLAY_VADISPLAY(display);
440 filter->va_config = VA_INVALID_ID;
441 filter->va_context = VA_INVALID_ID;
442 filter->format = DEFAULT_FORMAT;
444 if (!GST_VAAPI_DISPLAY_HAS_VPP(display))
447 va_status = vaCreateConfig(filter->va_display, VAProfileNone,
448 VAEntrypointVideoProc, NULL, 0, &filter->va_config);
449 if (!vaapi_check_status(va_status, "vaCreateConfig() [VPP]"))
452 va_status = vaCreateContext(filter->va_display, filter->va_config, 0, 0, 0,
453 NULL, 0, &filter->va_context);
454 if (!vaapi_check_status(va_status, "vaCreateContext() [VPP]"))
460 gst_vaapi_filter_finalize(GstVaapiFilter *filter)
464 GST_VAAPI_DISPLAY_LOCK(filter->display);
465 if (filter->operations) {
466 for (i = 0; i < filter->operations->len; i++) {
467 GstVaapiFilterOpData * const op_data =
468 g_ptr_array_index(filter->operations, i);
469 vaapi_destroy_buffer(filter->va_display, &op_data->va_buffer);
471 g_ptr_array_unref(filter->operations);
472 filter->operations = NULL;
475 if (filter->va_context != VA_INVALID_ID) {
476 vaDestroyContext(filter->va_display, filter->va_context);
477 filter->va_context = VA_INVALID_ID;
480 if (filter->va_config != VA_INVALID_ID) {
481 vaDestroyConfig(filter->va_display, filter->va_config);
482 filter->va_config = VA_INVALID_ID;
484 GST_VAAPI_DISPLAY_UNLOCK(filter->display);
485 gst_vaapi_display_replace(&filter->display, NULL);
487 if (filter->formats) {
488 g_array_unref(filter->formats);
489 filter->formats = NULL;
493 static inline const GstVaapiMiniObjectClass *
494 gst_vaapi_filter_class(void)
496 static const GstVaapiMiniObjectClass GstVaapiFilterClass = {
497 sizeof(GstVaapiFilter),
498 (GDestroyNotify)gst_vaapi_filter_finalize
500 return &GstVaapiFilterClass;
505 * gst_vaapi_filter_new:
506 * @display: a #GstVaapiDisplay
508 * Creates a new #GstVaapiFilter set up to operate in "identity"
509 * mode. This means that no other operation than scaling is performed.
511 * Return value: the newly created #GstVaapiFilter object
514 gst_vaapi_filter_new(GstVaapiDisplay *display)
517 GstVaapiFilter *filter;
519 filter = (GstVaapiFilter *)
520 gst_vaapi_mini_object_new0(gst_vaapi_filter_class());
524 if (!gst_vaapi_filter_init(filter, display))
529 gst_vaapi_filter_unref(filter);
532 GST_WARNING("video processing is not supported, "
533 "please consider an upgrade to VA-API >= 0.34");
539 * gst_vaapi_filter_ref:
540 * @filter: a #GstVaapiFilter
542 * Atomically increases the reference count of the given @filter by one.
544 * Returns: The same @filter argument
547 gst_vaapi_filter_ref(GstVaapiFilter *filter)
549 g_return_val_if_fail(filter != NULL, NULL);
551 return GST_VAAPI_FILTER(gst_vaapi_mini_object_ref(
552 GST_VAAPI_MINI_OBJECT(filter)));
556 * gst_vaapi_filter_unref:
557 * @filter: a #GstVaapiFilter
559 * Atomically decreases the reference count of the @filter by one. If
560 * the reference count reaches zero, the filter will be free'd.
563 gst_vaapi_filter_unref(GstVaapiFilter *filter)
565 g_return_if_fail(filter != NULL);
567 gst_vaapi_mini_object_unref(GST_VAAPI_MINI_OBJECT(filter));
571 * gst_vaapi_filter_replace:
572 * @old_filter_ptr: a pointer to a #GstVaapiFilter
573 * @new_filter: a #GstVaapiFilter
575 * Atomically replaces the filter held in @old_filter_ptr with
576 * @new_filter. This means that @old_filter_ptr shall reference a
577 * valid filter. However, @new_filter can be NULL.
580 gst_vaapi_filter_replace(GstVaapiFilter **old_filter_ptr,
581 GstVaapiFilter *new_filter)
583 g_return_if_fail(old_filter_ptr != NULL);
585 gst_vaapi_mini_object_replace((GstVaapiMiniObject **)old_filter_ptr,
586 GST_VAAPI_MINI_OBJECT(new_filter));
590 * gst_vaapi_filter_get_operations:
591 * @filter: a #GstVaapiFilter, or %NULL
593 * Determines the set of supported operations for video processing.
594 * The caller owns an extra reference to the resulting array of
595 * #GstVaapiFilterOpInfo elements, so it shall be released with
596 * g_ptr_array_unref() after usage.
598 * If @filter is %NULL, then this function returns the video
599 * processing operations supported by this library.
601 * Return value: the set of supported operations, or %NULL if an error
605 gst_vaapi_filter_get_operations(GstVaapiFilter *filter)
608 return ensure_operations(filter);
615 * gst_vaapi_filter_set_operation:
616 * @filter: a #GstVaapiFilter
617 * @op: a #GstVaapiFilterOp
618 * @value: the @op settings
620 * Enable the specified operation @op to be performed during video
621 * processing, i.e. in gst_vaapi_filter_process(). The @value argument
622 * specifies the operation settings. e.g. deinterlacing method for
623 * deinterlacing, denoising level for noise reduction, etc.
625 * If @value is %NULL, then this function resets the operation
626 * settings to their default values.
628 * Return value: %TRUE if the specified operation may be supported,
632 gst_vaapi_filter_set_operation(GstVaapiFilter *filter, GstVaapiFilterOp op,
636 GstVaapiFilterOpData *op_data;
638 g_return_val_if_fail(filter != NULL, FALSE);
640 op_data = find_operation(filter, op);
644 if (value && !G_VALUE_HOLDS(value, G_PARAM_SPEC_VALUE_TYPE(op_data->pspec)))
648 case GST_VAAPI_FILTER_OP_FORMAT:
649 return gst_vaapi_filter_set_format(filter, value ?
650 g_value_get_enum(value) : DEFAULT_FORMAT);
659 * gst_vaapi_filter_process:
660 * @filter: a #GstVaapiFilter
661 * @src_surface: the source @GstVaapiSurface
662 * @dst_surface: the destination @GstVaapiSurface
663 * @flags: #GstVaapiSurfaceRenderFlags that apply to @src_surface
665 * Applies the operations currently defined in the @filter to
666 * @src_surface and return the output in @dst_surface. The order of
667 * operations is determined in a way that suits best the underlying
668 * hardware. i.e. the only guarantee held is the generated outcome,
669 * not any specific order of operations.
671 * Return value: a #GstVaapiFilterStatus
673 static GstVaapiFilterStatus
674 gst_vaapi_filter_process_unlocked(GstVaapiFilter *filter,
675 GstVaapiSurface *src_surface, GstVaapiSurface *dst_surface, guint flags)
678 VAProcPipelineParameterBuffer *pipeline_param = NULL;
679 VABufferID pipeline_param_buf_id;
680 VABufferID filters[N_PROPERTIES];
681 guint i, num_filters = 0;
683 VARectangle src_rect, dst_rect;
685 if (!ensure_operations(filter))
686 return GST_VAAPI_FILTER_STATUS_ERROR_ALLOCATION_FAILED;
690 src_rect.width = GST_VAAPI_SURFACE_WIDTH(src_surface);
691 src_rect.height = GST_VAAPI_SURFACE_HEIGHT(src_surface);
695 dst_rect.width = GST_VAAPI_SURFACE_WIDTH(dst_surface);
696 dst_rect.height = GST_VAAPI_SURFACE_HEIGHT(dst_surface);
698 for (i = 0, num_filters = 0; i < filter->operations->len; i++) {
699 GstVaapiFilterOpData * const op_data =
700 g_ptr_array_index(filter->operations, i);
701 if (!op_data->is_enabled)
703 if (op_data->va_buffer == VA_INVALID_ID) {
704 GST_ERROR("invalid VA buffer for operation %s",
705 g_param_spec_get_name(op_data->pspec));
708 filters[num_filters++] = op_data->va_buffer;
711 if (!vaapi_create_buffer(filter->va_display, filter->va_context,
712 VAProcPipelineParameterBufferType, sizeof(*pipeline_param),
713 NULL, &pipeline_param_buf_id, (gpointer *)&pipeline_param))
716 memset(pipeline_param, 0, sizeof(*pipeline_param));
717 pipeline_param->surface = GST_VAAPI_OBJECT_ID(src_surface);
718 pipeline_param->surface_region = &src_rect;
719 pipeline_param->surface_color_standard = VAProcColorStandardNone;
720 pipeline_param->output_region = &dst_rect;
721 pipeline_param->output_color_standard = VAProcColorStandardNone;
722 pipeline_param->output_background_color = 0xff000000;
723 pipeline_param->filter_flags = from_GstVaapiSurfaceRenderFlags(flags);
724 pipeline_param->filters = filters;
725 pipeline_param->num_filters = num_filters;
727 vaapi_unmap_buffer(filter->va_display, pipeline_param_buf_id, NULL);
729 va_status = vaBeginPicture(filter->va_display, filter->va_context,
730 GST_VAAPI_OBJECT_ID(dst_surface));
731 if (!vaapi_check_status(va_status, "vaBeginPicture()"))
734 va_status = vaRenderPicture(filter->va_display, filter->va_context,
735 &pipeline_param_buf_id, 1);
736 if (!vaapi_check_status(va_status, "vaRenderPicture()"))
739 va_status = vaEndPicture(filter->va_display, filter->va_context);
740 if (!vaapi_check_status(va_status, "vaEndPicture()"))
742 return GST_VAAPI_FILTER_STATUS_SUCCESS;
745 vaDestroyBuffer(filter->va_display, pipeline_param_buf_id);
746 return GST_VAAPI_FILTER_STATUS_ERROR_OPERATION_FAILED;
748 return GST_VAAPI_FILTER_STATUS_ERROR_UNSUPPORTED_OPERATION;
752 gst_vaapi_filter_process(GstVaapiFilter *filter, GstVaapiSurface *src_surface,
753 GstVaapiSurface *dst_surface, guint flags)
755 GstVaapiFilterStatus status;
757 g_return_val_if_fail(filter != NULL,
758 GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
759 g_return_val_if_fail(src_surface != NULL,
760 GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
761 g_return_val_if_fail(dst_surface != NULL,
762 GST_VAAPI_FILTER_STATUS_ERROR_INVALID_PARAMETER);
764 GST_VAAPI_DISPLAY_LOCK(filter->display);
765 status = gst_vaapi_filter_process_unlocked(filter,
766 src_surface, dst_surface, flags);
767 GST_VAAPI_DISPLAY_UNLOCK(filter->display);
772 * gst_vaapi_filter_get_formats:
773 * @filter: a #GstVaapiFilter
775 * Determines the set of supported source or target formats for video
776 * processing. The caller owns an extra reference to the resulting
777 * array of #GstVideoFormat elements, so it shall be released with
778 * g_array_unref() after usage.
780 * Return value: the set of supported target formats for video processing.
783 gst_vaapi_filter_get_formats(GstVaapiFilter *filter)
785 g_return_val_if_fail(filter != NULL, NULL);
787 return ensure_formats(filter);
791 * gst_vaapi_filter_set_format:
792 * @filter: a #GstVaapiFilter
793 * @format: the target surface format
795 * Sets the desired pixel format of the resulting video processing
798 * If @format is #GST_VIDEO_FORMAT_UNKNOWN, the filter will assume iso
799 * format conversion, i.e. no color conversion at all and the target
800 * surface format shall match the source surface format.
802 * If @format is #GST_VIDEO_FORMAT_ENCODED, the filter will use the pixel
803 * format of the target surface passed to gst_vaapi_filter_process().
805 * Return value: %TRUE if the color conversion to the specified @format
806 * may be supported, %FALSE otherwise.
809 gst_vaapi_filter_set_format(GstVaapiFilter *filter, GstVideoFormat format)
811 g_return_val_if_fail(filter != NULL, FALSE);
813 if (!ensure_formats(filter))
816 if (!is_special_format(format) && !find_format(filter, format))
819 filter->format = format;