cuda: Add convertscale element
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / nvcodec / gstcudabasefilter.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2005-2012 David Schleef <ds@schleef.org>
4  * Copyright (C) <2019> Seungha Yang <seungha.yang@navercorp.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
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  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * GstCudaBaseFilter:
24  *
25  * Base class for CUDA filters
26  *
27  * Since: 1.20
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #  include <config.h>
32 #endif
33
34 #include "gstcudabasefilter.h"
35 #include "gstcudaformat.h"
36 #include <gst/cuda/gstcudautils.h>
37
38 #include <string.h>
39
40 GST_DEBUG_CATEGORY_STATIC (gst_cuda_base_filter_debug);
41 #define GST_CAT_DEFAULT gst_cuda_base_filter_debug
42
43 #define GST_CUDA_FILTER_FORMATS \
44     "{ I420, YV12, NV12, NV21, P010_10LE, P016_LE, I420_10LE, Y444, Y444_16LE, " \
45     "BGRA, RGBA, RGBx, BGRx, ARGB, ABGR, RGB, BGR, BGR10A2_LE, RGB10A2_LE, Y42B, I422_10LE, I422_12LE }"
46
47 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
48     GST_PAD_SINK,
49     GST_PAD_ALWAYS,
50     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
51         (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, GST_CUDA_FILTER_FORMATS))
52     );
53
54 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
55     GST_PAD_SRC,
56     GST_PAD_ALWAYS,
57     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
58         (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, GST_CUDA_FILTER_FORMATS))
59     );
60
61 #define gst_cuda_base_filter_parent_class parent_class
62 G_DEFINE_ABSTRACT_TYPE (GstCudaBaseFilter,
63     gst_cuda_base_filter, GST_TYPE_CUDA_BASE_TRANSFORM);
64
65 static void gst_cuda_base_filter_dispose (GObject * object);
66 static gboolean
67 gst_cuda_base_filter_propose_allocation (GstBaseTransform * trans,
68     GstQuery * decide_query, GstQuery * query);
69 static gboolean gst_cuda_base_filter_decide_allocation (GstBaseTransform *
70     trans, GstQuery * query);
71 static GstFlowReturn gst_cuda_base_filter_transform (GstBaseTransform * trans,
72     GstBuffer * inbuf, GstBuffer * outbuf);
73 static gboolean gst_cuda_base_filter_set_info (GstCudaBaseTransform * btrans,
74     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
75     GstVideoInfo * out_info);
76
77 static void
78 gst_cuda_base_filter_class_init (GstCudaBaseFilterClass * klass)
79 {
80   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
81   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
82   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
83   GstCudaBaseTransformClass *btrans_class =
84       GST_CUDA_BASE_TRANSFORM_CLASS (klass);
85
86   gobject_class->dispose = gst_cuda_base_filter_dispose;
87
88   gst_element_class_add_static_pad_template (element_class, &sink_template);
89   gst_element_class_add_static_pad_template (element_class, &src_template);
90
91   trans_class->passthrough_on_same_caps = TRUE;
92
93   trans_class->propose_allocation =
94       GST_DEBUG_FUNCPTR (gst_cuda_base_filter_propose_allocation);
95   trans_class->decide_allocation =
96       GST_DEBUG_FUNCPTR (gst_cuda_base_filter_decide_allocation);
97   trans_class->transform = GST_DEBUG_FUNCPTR (gst_cuda_base_filter_transform);
98
99   btrans_class->set_info = GST_DEBUG_FUNCPTR (gst_cuda_base_filter_set_info);
100
101   GST_DEBUG_CATEGORY_INIT (gst_cuda_base_filter_debug,
102       "cudabasefilter", 0, "CUDA Base Filter");
103
104   gst_type_mark_as_plugin_api (GST_TYPE_CUDA_BASE_FILTER, 0);
105 }
106
107 static void
108 gst_cuda_base_filter_init (GstCudaBaseFilter * convert)
109 {
110 }
111
112 static void
113 gst_cuda_base_filter_dispose (GObject * object)
114 {
115   GstCudaBaseFilter *filter = GST_CUDA_BASE_FILTER (object);
116
117   if (filter->converter) {
118     gst_cuda_converter_free (filter->converter);
119     filter->converter = NULL;
120   }
121
122   G_OBJECT_CLASS (parent_class)->dispose (object);
123 }
124
125 static gboolean
126 gst_cuda_base_filter_set_info (GstCudaBaseTransform * btrans, GstCaps * incaps,
127     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
128 {
129   GstCudaBaseFilter *filter = GST_CUDA_BASE_FILTER (btrans);
130
131   if (filter->converter)
132     gst_cuda_converter_free (filter->converter);
133
134   filter->converter =
135       gst_cuda_converter_new (in_info, out_info, btrans->context);
136
137   if (!filter->converter) {
138     GST_ERROR_OBJECT (filter, "could not create converter");
139     return FALSE;
140   }
141
142   GST_DEBUG_OBJECT (filter, "reconfigured %d %d",
143       GST_VIDEO_INFO_FORMAT (in_info), GST_VIDEO_INFO_FORMAT (out_info));
144
145   return TRUE;
146 }
147
148 static gboolean
149 gst_cuda_base_filter_propose_allocation (GstBaseTransform * trans,
150     GstQuery * decide_query, GstQuery * query)
151 {
152   GstCudaBaseTransform *ctrans = GST_CUDA_BASE_TRANSFORM (trans);
153   GstVideoInfo info;
154   GstBufferPool *pool;
155   GstCaps *caps;
156   guint size;
157
158   if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
159           decide_query, query))
160     return FALSE;
161
162   /* passthrough, we're done */
163   if (decide_query == NULL)
164     return TRUE;
165
166   gst_query_parse_allocation (query, &caps, NULL);
167
168   if (caps == NULL)
169     return FALSE;
170
171   if (!gst_video_info_from_caps (&info, caps))
172     return FALSE;
173
174   if (gst_query_get_n_allocation_pools (query) == 0) {
175     GstStructure *config;
176
177     pool = gst_cuda_buffer_pool_new (ctrans->context);
178
179     config = gst_buffer_pool_get_config (pool);
180
181     gst_buffer_pool_config_add_option (config,
182         GST_BUFFER_POOL_OPTION_VIDEO_META);
183
184     size = GST_VIDEO_INFO_SIZE (&info);
185     gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
186
187     if (!gst_buffer_pool_set_config (pool, config)) {
188       GST_ERROR_OBJECT (ctrans, "failed to set config");
189       gst_object_unref (pool);
190       return FALSE;
191     }
192
193     /* Get updated size by cuda buffer pool */
194     config = gst_buffer_pool_get_config (pool);
195     gst_buffer_pool_config_get_params (config, NULL, &size, NULL, NULL);
196     gst_structure_free (config);
197
198     gst_query_add_allocation_pool (query, pool, size, 0, 0);
199
200     gst_object_unref (pool);
201   }
202
203   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
204
205   return TRUE;
206 }
207
208 static gboolean
209 gst_cuda_base_filter_decide_allocation (GstBaseTransform * trans,
210     GstQuery * query)
211 {
212   GstCudaBaseTransform *ctrans = GST_CUDA_BASE_TRANSFORM (trans);
213   GstCaps *outcaps = NULL;
214   GstBufferPool *pool = NULL;
215   guint size, min, max;
216   GstStructure *config;
217   gboolean update_pool = FALSE;
218
219   gst_query_parse_allocation (query, &outcaps, NULL);
220
221   if (!outcaps)
222     return FALSE;
223
224   if (gst_query_get_n_allocation_pools (query) > 0) {
225     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
226     if (pool) {
227       if (!GST_IS_CUDA_BUFFER_POOL (pool)) {
228         gst_clear_object (&pool);
229       } else {
230         GstCudaBufferPool *cpool = GST_CUDA_BUFFER_POOL (pool);
231
232         if (cpool->context != ctrans->context) {
233           gst_clear_object (&pool);
234         }
235       }
236     }
237
238     update_pool = TRUE;
239   } else {
240     GstVideoInfo vinfo;
241     gst_video_info_from_caps (&vinfo, outcaps);
242     size = GST_VIDEO_INFO_SIZE (&vinfo);
243     min = max = 0;
244   }
245
246   if (!pool) {
247     GST_DEBUG_OBJECT (ctrans, "create our pool");
248
249     pool = gst_cuda_buffer_pool_new (ctrans->context);
250   }
251
252   config = gst_buffer_pool_get_config (pool);
253   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
254   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
255   gst_buffer_pool_set_config (pool, config);
256
257   /* Get updated size by cuda buffer pool */
258   config = gst_buffer_pool_get_config (pool);
259   gst_buffer_pool_config_get_params (config, NULL, &size, NULL, NULL);
260   gst_structure_free (config);
261
262   if (update_pool)
263     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
264   else
265     gst_query_add_allocation_pool (query, pool, size, min, max);
266
267   gst_object_unref (pool);
268
269   return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
270       query);
271 }
272
273 static GstFlowReturn
274 gst_cuda_base_filter_transform (GstBaseTransform * trans,
275     GstBuffer * inbuf, GstBuffer * outbuf)
276 {
277   GstCudaBaseFilter *self = GST_CUDA_BASE_FILTER (trans);
278   GstCudaBaseTransform *ctrans = GST_CUDA_BASE_TRANSFORM (trans);
279   GstVideoFrame in_frame, out_frame;
280   GstFlowReturn ret = GST_FLOW_OK;
281   GstMemory *mem;
282
283   if (gst_buffer_n_memory (inbuf) != 1) {
284     GST_ERROR_OBJECT (self, "Invalid input buffer");
285     return GST_FLOW_ERROR;
286   }
287
288   mem = gst_buffer_peek_memory (inbuf, 0);
289   if (!gst_is_cuda_memory (mem)) {
290     GST_ERROR_OBJECT (self, "Input buffer is not CUDA");
291     return GST_FLOW_ERROR;
292   }
293
294   if (gst_buffer_n_memory (outbuf) != 1) {
295     GST_ERROR_OBJECT (self, "Invalid output buffer");
296     return GST_FLOW_ERROR;
297   }
298
299   mem = gst_buffer_peek_memory (outbuf, 0);
300   if (!gst_is_cuda_memory (mem)) {
301     GST_ERROR_OBJECT (self, "Input buffer is not CUDA");
302     return GST_FLOW_ERROR;
303   }
304
305   if (!gst_video_frame_map (&in_frame, &ctrans->in_info, inbuf,
306           GST_MAP_READ | GST_MAP_CUDA)) {
307     GST_ERROR_OBJECT (self, "Failed to map input buffer");
308     return GST_FLOW_ERROR;
309   }
310
311   if (!gst_video_frame_map (&out_frame, &ctrans->out_info, outbuf,
312           GST_MAP_WRITE | GST_MAP_CUDA)) {
313     gst_video_frame_unmap (&in_frame);
314     GST_ERROR_OBJECT (self, "Failed to map output buffer");
315     return GST_FLOW_ERROR;
316   }
317
318   if (!gst_cuda_converter_convert_frame (self->converter, &in_frame, &out_frame,
319           ctrans->cuda_stream)) {
320     GST_ERROR_OBJECT (self, "Failed to convert frame");
321     ret = GST_FLOW_ERROR;
322   }
323
324   gst_video_frame_unmap (&out_frame);
325   gst_video_frame_unmap (&in_frame);
326
327   return ret;
328 }