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, &params);
86     else
87       gst_query_add_allocation_param (query, allocator, &params);
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, &params);
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