1 /* GStreamer
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) <2003> David Schleef <ds@schleef.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:gstvideofilter
23 * @title: GstVideoFilter
24 * @short_description: Base class for video filters
25 *
26 * Provides useful functions and a base class for video filters.
27 *
28 * The videofilter will by default enable QoS on the parent GstBaseTransform
29 * to implement frame dropping.
30 *
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "gstvideofilter.h"
38
39 #include <gst/video/video.h>
40 #include <gst/video/gstvideometa.h>
41 #include <gst/video/gstvideopool.h>
42
43 GST_DEBUG_CATEGORY_STATIC (gst_video_filter_debug);
44 #define GST_CAT_DEFAULT gst_video_filter_debug
45
46 #define gst_video_filter_parent_class parent_class
47 G_DEFINE_ABSTRACT_TYPE (GstVideoFilter, gst_video_filter,
48 GST_TYPE_BASE_TRANSFORM);
49
50 /* Answer the allocation query downstream. */
51 static gboolean
gst_video_filter_propose_allocation(GstBaseTransform * trans,GstQuery * decide_query,GstQuery * query)52 gst_video_filter_propose_allocation (GstBaseTransform * trans,
53 GstQuery * decide_query, GstQuery * query)
54 {
55 GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
56 GstVideoInfo info;
57 GstBufferPool *pool;
58 GstCaps *caps;
59 guint size;
60
61 if (!GST_BASE_TRANSFORM_CLASS (parent_class)->propose_allocation (trans,
62 decide_query, query))
63 return FALSE;
64
65 /* passthrough, we're done */
66 if (decide_query == NULL)
67 return TRUE;
68
69 gst_query_parse_allocation (query, &caps, NULL);
70
71 if (caps == NULL)
72 return FALSE;
73
74 if (!gst_video_info_from_caps (&info, caps))
75 return FALSE;
76
77 size = GST_VIDEO_INFO_SIZE (&info);
78
79 if (gst_query_get_n_allocation_pools (query) == 0) {
80 GstStructure *structure;
81 GstAllocator *allocator = NULL;
82 GstAllocationParams params = { 0, 15, 0, 0, };
83
84 if (gst_query_get_n_allocation_params (query) > 0)
85 gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms);
86 else
87 gst_query_add_allocation_param (query, allocator, ¶ms);
88
89 pool = gst_video_buffer_pool_new ();
90
91 structure = gst_buffer_pool_get_config (pool);
92 gst_buffer_pool_config_set_params (structure, caps, size, 0, 0);
93 gst_buffer_pool_config_set_allocator (structure, allocator, ¶ms);
94
95 if (allocator)
96 gst_object_unref (allocator);
97
98 if (!gst_buffer_pool_set_config (pool, structure))
99 goto config_failed;
100
101 gst_query_add_allocation_pool (query, pool, size, 0, 0);
102 gst_object_unref (pool);
103 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
104 }
105
106 return TRUE;
107
108 /* ERRORS */
109 config_failed:
110 {
111 GST_ERROR_OBJECT (filter, "failed to set config");
112 gst_object_unref (pool);
113 return FALSE;
114 }
115 }
116
117 /* configure the allocation query that was answered downstream, we can configure
118 * some properties on it. Only called when not in passthrough mode. */
119 static gboolean
gst_video_filter_decide_allocation(GstBaseTransform * trans,GstQuery * query)120 gst_video_filter_decide_allocation (GstBaseTransform * trans, GstQuery * query)
121 {
122 GstBufferPool *pool = NULL;
123 GstStructure *config;
124 guint min, max, size;
125 gboolean update_pool;
126 GstCaps *outcaps = NULL;
127
128 if (gst_query_get_n_allocation_pools (query) > 0) {
129 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
130
131 if (!pool)
132 gst_query_parse_allocation (query, &outcaps, NULL);
133
134 update_pool = TRUE;
135 } else {
136 GstVideoInfo vinfo;
137
138 gst_query_parse_allocation (query, &outcaps, NULL);
139 gst_video_info_init (&vinfo);
140 gst_video_info_from_caps (&vinfo, outcaps);
141 size = vinfo.size;
142 min = max = 0;
143 update_pool = FALSE;
144 }
145
146 if (!pool)
147 pool = gst_video_buffer_pool_new ();
148
149 config = gst_buffer_pool_get_config (pool);
150 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
151 if (outcaps)
152 gst_buffer_pool_config_set_params (config, outcaps, size, 0, 0);
153 gst_buffer_pool_set_config (pool, config);
154
155 if (update_pool)
156 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
157 else
158 gst_query_add_allocation_pool (query, pool, size, min, max);
159
160 gst_object_unref (pool);
161
162 return GST_BASE_TRANSFORM_CLASS (parent_class)->decide_allocation (trans,
163 query);
164 }
165
166
167 /* our output size only depends on the caps, not on the input caps */
168 static gboolean
gst_video_filter_transform_size(GstBaseTransform * btrans,GstPadDirection direction,GstCaps * caps,gsize size,GstCaps * othercaps,gsize * othersize)169 gst_video_filter_transform_size (GstBaseTransform * btrans,
170 GstPadDirection direction, GstCaps * caps, gsize size,
171 GstCaps * othercaps, gsize * othersize)
172 {
173 gboolean ret = TRUE;
174 GstVideoInfo info;
175
176 g_assert (size);
177
178 ret = gst_video_info_from_caps (&info, othercaps);
179 if (ret)
180 *othersize = info.size;
181
182 return ret;
183 }
184
185 static gboolean
gst_video_filter_get_unit_size(GstBaseTransform * btrans,GstCaps * caps,gsize * size)186 gst_video_filter_get_unit_size (GstBaseTransform * btrans, GstCaps * caps,
187 gsize * size)
188 {
189 GstVideoInfo info;
190
191 if (!gst_video_info_from_caps (&info, caps)) {
192 GST_WARNING_OBJECT (btrans, "Failed to parse caps %" GST_PTR_FORMAT, caps);
193 return FALSE;
194 }
195
196 *size = info.size;
197
198 GST_DEBUG_OBJECT (btrans, "Returning size %" G_GSIZE_FORMAT " bytes"
199 "for caps %" GST_PTR_FORMAT, *size, caps);
200
201 return TRUE;
202 }
203
204 static gboolean
gst_video_filter_set_caps(GstBaseTransform * trans,GstCaps * incaps,GstCaps * outcaps)205 gst_video_filter_set_caps (GstBaseTransform * trans, GstCaps * incaps,
206 GstCaps * outcaps)
207 {
208 GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
209 GstVideoFilterClass *fclass;
210 GstVideoInfo in_info, out_info;
211 gboolean res;
212
213 /* input caps */
214 if (!gst_video_info_from_caps (&in_info, incaps))
215 goto invalid_caps;
216
217 /* output caps */
218 if (!gst_video_info_from_caps (&out_info, outcaps))
219 goto invalid_caps;
220
221 fclass = GST_VIDEO_FILTER_GET_CLASS (filter);
222 if (fclass->set_info)
223 res = fclass->set_info (filter, incaps, &in_info, outcaps, &out_info);
224 else
225 res = TRUE;
226
227 if (res) {
228 filter->in_info = in_info;
229 filter->out_info = out_info;
230 if (fclass->transform_frame == NULL)
231 gst_base_transform_set_in_place (trans, TRUE);
232 if (fclass->transform_frame_ip == NULL)
233 GST_BASE_TRANSFORM_CLASS (fclass)->transform_ip_on_passthrough = FALSE;
234 }
235 filter->negotiated = res;
236
237 return res;
238
239 /* ERRORS */
240 invalid_caps:
241 {
242 GST_ERROR_OBJECT (filter, "invalid caps");
243 filter->negotiated = FALSE;
244 return FALSE;
245 }
246 }
247
248 static GstFlowReturn
gst_video_filter_transform(GstBaseTransform * trans,GstBuffer * inbuf,GstBuffer * outbuf)249 gst_video_filter_transform (GstBaseTransform * trans, GstBuffer * inbuf,
250 GstBuffer * outbuf)
251 {
252 GstFlowReturn res;
253 GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
254 GstVideoFilterClass *fclass;
255
256 if (G_UNLIKELY (!filter->negotiated))
257 goto unknown_format;
258
259 fclass = GST_VIDEO_FILTER_GET_CLASS (filter);
260 if (fclass->transform_frame) {
261 GstVideoFrame in_frame, out_frame;
262
263 if (!gst_video_frame_map (&in_frame, &filter->in_info, inbuf,
264 GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF))
265 goto invalid_buffer;
266
267 if (!gst_video_frame_map (&out_frame, &filter->out_info, outbuf,
268 GST_MAP_WRITE | GST_VIDEO_FRAME_MAP_FLAG_NO_REF)) {
269 gst_video_frame_unmap (&in_frame);
270 goto invalid_buffer;
271 }
272 res = fclass->transform_frame (filter, &in_frame, &out_frame);
273
274 gst_video_frame_unmap (&out_frame);
275 gst_video_frame_unmap (&in_frame);
276 } else {
277 GST_DEBUG_OBJECT (trans, "no transform_frame vmethod");
278 res = GST_FLOW_OK;
279 }
280
281 return res;
282
283 /* ERRORS */
284 unknown_format:
285 {
286 GST_ELEMENT_ERROR (filter, CORE, NOT_IMPLEMENTED, (NULL),
287 ("unknown format"));
288 return GST_FLOW_NOT_NEGOTIATED;
289 }
290 invalid_buffer:
291 {
292 GST_ELEMENT_WARNING (filter, CORE, NOT_IMPLEMENTED, (NULL),
293 ("invalid video buffer received"));
294 return GST_FLOW_OK;
295 }
296 }
297
298 static GstFlowReturn
gst_video_filter_transform_ip(GstBaseTransform * trans,GstBuffer * buf)299 gst_video_filter_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
300 {
301 GstFlowReturn res;
302 GstVideoFilter *filter = GST_VIDEO_FILTER_CAST (trans);
303 GstVideoFilterClass *fclass;
304
305 if (G_UNLIKELY (!filter->negotiated))
306 goto unknown_format;
307
308 fclass = GST_VIDEO_FILTER_GET_CLASS (filter);
309 if (fclass->transform_frame_ip) {
310 GstVideoFrame frame;
311 GstMapFlags flags;
312
313 flags = GST_MAP_READ | GST_VIDEO_FRAME_MAP_FLAG_NO_REF;
314
315 if (!gst_base_transform_is_passthrough (trans))
316 flags |= GST_MAP_WRITE;
317
318 if (!gst_video_frame_map (&frame, &filter->in_info, buf, flags))
319 goto invalid_buffer;
320
321 res = fclass->transform_frame_ip (filter, &frame);
322
323 gst_video_frame_unmap (&frame);
324 } else {
325 GST_DEBUG_OBJECT (trans, "no transform_frame_ip vmethod");
326 res = GST_FLOW_OK;
327 }
328
329 return res;
330
331 /* ERRORS */
332 unknown_format:
333 {
334 GST_ELEMENT_ERROR (filter, CORE, NOT_IMPLEMENTED, (NULL),
335 ("unknown format"));
336 return GST_FLOW_NOT_NEGOTIATED;
337 }
338 invalid_buffer:
339 {
340 GST_ELEMENT_WARNING (filter, CORE, NOT_IMPLEMENTED, (NULL),
341 ("invalid video buffer received"));
342 return GST_FLOW_OK;
343 }
344 }
345
346 static gboolean
gst_video_filter_transform_meta(GstBaseTransform * trans,GstBuffer * inbuf,GstMeta * meta,GstBuffer * outbuf)347 gst_video_filter_transform_meta (GstBaseTransform * trans, GstBuffer * inbuf,
348 GstMeta * meta, GstBuffer * outbuf)
349 {
350 const GstMetaInfo *info = meta->info;
351 const gchar *const *tags;
352
353 tags = gst_meta_api_type_get_tags (info->api);
354
355 if (!tags || (g_strv_length ((gchar **) tags) == 1
356 && gst_meta_api_type_has_tag (info->api,
357 g_quark_from_string (GST_META_TAG_VIDEO_STR))))
358 return TRUE;
359
360 return GST_BASE_TRANSFORM_CLASS (parent_class)->transform_meta (trans, inbuf,
361 meta, outbuf);
362 }
363
364 static void
gst_video_filter_class_init(GstVideoFilterClass * g_class)365 gst_video_filter_class_init (GstVideoFilterClass * g_class)
366 {
367 GstBaseTransformClass *trans_class;
368 GstVideoFilterClass *klass;
369
370 klass = (GstVideoFilterClass *) g_class;
371 trans_class = (GstBaseTransformClass *) klass;
372
373 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_filter_set_caps);
374 trans_class->propose_allocation =
375 GST_DEBUG_FUNCPTR (gst_video_filter_propose_allocation);
376 trans_class->decide_allocation =
377 GST_DEBUG_FUNCPTR (gst_video_filter_decide_allocation);
378 trans_class->transform_size =
379 GST_DEBUG_FUNCPTR (gst_video_filter_transform_size);
380 trans_class->get_unit_size =
381 GST_DEBUG_FUNCPTR (gst_video_filter_get_unit_size);
382 trans_class->transform = GST_DEBUG_FUNCPTR (gst_video_filter_transform);
383 trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_video_filter_transform_ip);
384 trans_class->transform_meta =
385 GST_DEBUG_FUNCPTR (gst_video_filter_transform_meta);
386
387 GST_DEBUG_CATEGORY_INIT (gst_video_filter_debug, "videofilter", 0,
388 "videofilter");
389 }
390
391 static void
gst_video_filter_init(GstVideoFilter * instance)392 gst_video_filter_init (GstVideoFilter * instance)
393 {
394 GstVideoFilter *videofilter = GST_VIDEO_FILTER (instance);
395
396 GST_DEBUG_OBJECT (videofilter, "gst_video_filter_init");
397
398 videofilter->negotiated = FALSE;
399 /* enable QoS */
400 gst_base_transform_set_qos_enabled (GST_BASE_TRANSFORM (videofilter), TRUE);
401 }
402