Merging gst-plugins-bad
[platform/upstream/gstreamer.git] / 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 #ifdef HAVE_CONFIG_H
23 #  include <config.h>
24 #endif
25
26 #include "gstcudabasefilter.h"
27 #include "gstcudautils.h"
28 #include <string.h>
29
30 GST_DEBUG_CATEGORY_STATIC (gst_cuda_base_filter_debug);
31 #define GST_CAT_DEFAULT gst_cuda_base_filter_debug
32
33 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
34     GST_PAD_SINK,
35     GST_PAD_ALWAYS,
36     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
37         (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, GST_CUDA_CONVERTER_FORMATS))
38     );
39
40 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
41     GST_PAD_SRC,
42     GST_PAD_ALWAYS,
43     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
44         (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, GST_CUDA_CONVERTER_FORMATS))
45     );
46
47 #define gst_cuda_base_filter_parent_class parent_class
48 G_DEFINE_ABSTRACT_TYPE (GstCudaBaseFilter,
49     gst_cuda_base_filter, GST_TYPE_CUDA_BASE_TRANSFORM);
50
51 static void gst_cuda_base_filter_dispose (GObject * object);
52 static GstFlowReturn
53 gst_cuda_base_filter_transform_frame (GstCudaBaseTransform * btrans,
54     GstVideoFrame * in_frame, GstCudaMemory * in_cuda_mem,
55     GstVideoFrame * out_frame, GstCudaMemory * out_cuda_mem);
56 static gboolean gst_cuda_base_filter_set_info (GstCudaBaseTransform * btrans,
57     GstCaps * incaps, GstVideoInfo * in_info, GstCaps * outcaps,
58     GstVideoInfo * out_info);
59
60 static void
61 gst_cuda_base_filter_class_init (GstCudaBaseFilterClass * klass)
62 {
63   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
64   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
65   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
66   GstCudaBaseTransformClass *btrans_class =
67       GST_CUDA_BASE_TRANSFORM_CLASS (klass);
68
69   gobject_class->dispose = gst_cuda_base_filter_dispose;
70
71   gst_element_class_add_static_pad_template (element_class, &sink_template);
72   gst_element_class_add_static_pad_template (element_class, &src_template);
73
74   trans_class->passthrough_on_same_caps = TRUE;
75
76   btrans_class->set_info = GST_DEBUG_FUNCPTR (gst_cuda_base_filter_set_info);
77   btrans_class->transform_frame =
78       GST_DEBUG_FUNCPTR (gst_cuda_base_filter_transform_frame);
79
80   GST_DEBUG_CATEGORY_INIT (gst_cuda_base_filter_debug,
81       "cudabasefilter", 0, "CUDA Base Filter");
82 }
83
84 static void
85 gst_cuda_base_filter_init (GstCudaBaseFilter * convert)
86 {
87 }
88
89 static void
90 gst_cuda_base_filter_dispose (GObject * object)
91 {
92   GstCudaBaseFilter *filter = GST_CUDA_BASE_FILTER (object);
93
94   if (filter->converter) {
95     gst_cuda_converter_free (filter->converter);
96     filter->converter = NULL;
97   }
98
99   if (filter->in_fallback) {
100     gst_memory_unref (GST_MEMORY_CAST (filter->in_fallback));
101     filter->in_fallback = NULL;
102   }
103
104   if (filter->out_fallback) {
105     gst_memory_unref (GST_MEMORY_CAST (filter->out_fallback));
106     filter->out_fallback = NULL;
107   }
108
109   gst_clear_object (&filter->allocator);
110
111   G_OBJECT_CLASS (parent_class)->dispose (object);
112 }
113
114 static gboolean
115 gst_cuda_base_filter_configure (GstCudaBaseFilter * filter,
116     GstVideoInfo * in_info, GstVideoInfo * out_info)
117 {
118   GstCudaBaseTransform *btrans = GST_CUDA_BASE_TRANSFORM (filter);
119
120   /* cleanup internal pool */
121   if (filter->in_fallback) {
122     gst_memory_unref (GST_MEMORY_CAST (filter->in_fallback));
123     filter->in_fallback = NULL;
124   }
125
126   if (filter->out_fallback) {
127     gst_memory_unref (GST_MEMORY_CAST (filter->out_fallback));
128     filter->out_fallback = NULL;
129   }
130
131   if (!filter->allocator)
132     filter->allocator = gst_cuda_allocator_new (btrans->context);
133
134   if (!filter->allocator) {
135     GST_ERROR_OBJECT (filter, "Failed to create CUDA allocator");
136     return FALSE;
137   }
138
139   return TRUE;
140 }
141
142 static gboolean
143 gst_cuda_base_filter_set_info (GstCudaBaseTransform * btrans, GstCaps * incaps,
144     GstVideoInfo * in_info, GstCaps * outcaps, GstVideoInfo * out_info)
145 {
146   GstCudaBaseFilter *filter = GST_CUDA_BASE_FILTER (btrans);
147
148   if (!gst_cuda_base_filter_configure (filter, in_info, out_info)) {
149     return FALSE;
150   }
151
152   if (filter->converter)
153     gst_cuda_converter_free (filter->converter);
154
155   filter->converter =
156       gst_cuda_converter_new (in_info, out_info, btrans->context);
157
158   if (filter->converter == NULL)
159     goto no_converter;
160
161   GST_DEBUG_OBJECT (filter, "reconfigured %d %d",
162       GST_VIDEO_INFO_FORMAT (in_info), GST_VIDEO_INFO_FORMAT (out_info));
163
164   return TRUE;
165
166 no_converter:
167   {
168     GST_ERROR_OBJECT (filter, "could not create converter");
169     return FALSE;
170   }
171 }
172
173 static GstFlowReturn
174 gst_cuda_base_filter_transform_frame (GstCudaBaseTransform * btrans,
175     GstVideoFrame * in_frame, GstCudaMemory * in_cuda_mem,
176     GstVideoFrame * out_frame, GstCudaMemory * out_cuda_mem)
177 {
178   GstCudaBaseFilter *filter = GST_CUDA_BASE_FILTER (btrans);
179   gboolean conv_ret;
180   GstCudaMemory *in_mem;
181   GstCudaMemory *out_mem;
182   gint i;
183
184   if (in_cuda_mem) {
185     in_mem = in_cuda_mem;
186   } else {
187     if (!filter->in_fallback) {
188       GstCudaAllocationParams params;
189
190       memset (&params, 0, sizeof (GstCudaAllocationParams));
191       params.info = btrans->in_info;
192
193       filter->in_fallback =
194           (GstCudaMemory *) gst_cuda_allocator_alloc (filter->allocator,
195           GST_VIDEO_INFO_SIZE (&params.info), &params);
196     }
197
198     if (!filter->in_fallback) {
199       GST_ERROR_OBJECT (filter, "Couldn't allocate fallback memory");
200       return GST_FLOW_ERROR;
201     }
202
203     GST_TRACE_OBJECT (filter, "use CUDA fallback memory input");
204
205     if (!gst_cuda_context_push (btrans->context)) {
206       GST_ELEMENT_ERROR (filter, LIBRARY, FAILED, (NULL),
207           ("Cannot push CUDA context"));
208       return FALSE;
209     }
210
211     /* upload frame to device memory */
212     for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (in_frame); i++) {
213       CUDA_MEMCPY2D param = { 0, };
214       guint width, height;
215
216       width = GST_VIDEO_FRAME_COMP_WIDTH (in_frame, i) *
217           GST_VIDEO_FRAME_COMP_PSTRIDE (in_frame, i);
218       height = GST_VIDEO_FRAME_COMP_HEIGHT (in_frame, i);
219
220       param.srcMemoryType = CU_MEMORYTYPE_HOST;
221       param.srcPitch = GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, i);
222       param.srcHost = GST_VIDEO_FRAME_PLANE_DATA (in_frame, i);
223       param.dstMemoryType = CU_MEMORYTYPE_DEVICE;
224       param.dstPitch = filter->in_fallback->stride;
225       param.dstDevice =
226           filter->in_fallback->data + filter->in_fallback->offset[i];
227       param.WidthInBytes = width;
228       param.Height = height;
229
230       if (!gst_cuda_result (CuMemcpy2DAsync (&param, btrans->cuda_stream))) {
231         gst_cuda_context_pop (NULL);
232         GST_ELEMENT_ERROR (filter, LIBRARY, FAILED, (NULL),
233             ("Cannot upload input video frame"));
234         return GST_FLOW_ERROR;
235       }
236     }
237
238     gst_cuda_result (CuStreamSynchronize (btrans->cuda_stream));
239     gst_cuda_context_pop (NULL);
240
241     in_mem = filter->in_fallback;
242   }
243
244   if (out_cuda_mem) {
245     out_mem = out_cuda_mem;
246   } else {
247     if (!filter->out_fallback) {
248       GstCudaAllocationParams params;
249
250       memset (&params, 0, sizeof (GstCudaAllocationParams));
251       params.info = btrans->out_info;
252
253       filter->out_fallback =
254           (GstCudaMemory *) gst_cuda_allocator_alloc (filter->allocator,
255           GST_VIDEO_INFO_SIZE (&params.info), &params);
256     }
257
258     if (!filter->out_fallback) {
259       GST_ERROR_OBJECT (filter, "Couldn't allocate fallback memory");
260       return GST_FLOW_ERROR;
261     }
262
263     out_mem = filter->out_fallback;
264   }
265
266   conv_ret =
267       gst_cuda_converter_frame (filter->converter, in_mem, &btrans->in_info,
268       out_mem, &btrans->out_info, btrans->cuda_stream);
269
270   if (!conv_ret) {
271     GST_ERROR_OBJECT (filter, "Failed to convert frame");
272     return GST_FLOW_ERROR;
273   }
274
275   if (!out_cuda_mem) {
276     if (!gst_cuda_context_push (btrans->context)) {
277       GST_ELEMENT_ERROR (filter, LIBRARY, FAILED, (NULL),
278           ("Cannot push CUDA context"));
279       return FALSE;
280     }
281
282     for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (out_frame); i++) {
283       CUDA_MEMCPY2D param = { 0, };
284       guint width, height;
285
286       width = GST_VIDEO_FRAME_COMP_WIDTH (out_frame, i) *
287           GST_VIDEO_FRAME_COMP_PSTRIDE (out_frame, i);
288       height = GST_VIDEO_FRAME_COMP_HEIGHT (out_frame, i);
289
290       param.srcMemoryType = CU_MEMORYTYPE_DEVICE;
291       param.srcPitch = out_mem->stride;
292       param.srcDevice =
293           filter->out_fallback->data + filter->out_fallback->offset[i];
294       param.dstMemoryType = CU_MEMORYTYPE_HOST;
295       param.dstPitch = GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, i);
296       param.dstHost = GST_VIDEO_FRAME_PLANE_DATA (out_frame, i);
297       param.WidthInBytes = width;
298       param.Height = height;
299
300       if (!gst_cuda_result (CuMemcpy2DAsync (&param, btrans->cuda_stream))) {
301         gst_cuda_context_pop (NULL);
302         GST_ELEMENT_ERROR (filter, LIBRARY, FAILED, (NULL),
303             ("Cannot upload input video frame"));
304         return GST_FLOW_ERROR;
305       }
306     }
307
308     gst_cuda_result (CuStreamSynchronize (btrans->cuda_stream));
309     gst_cuda_context_pop (NULL);
310   }
311
312   return GST_FLOW_OK;
313 }