1 /* GStreamer
2  * Copyright (C) <2011> Wim Taymans <wim.taymans@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "gst/video/gstvideometa.h"
24 #include "gst/video/gstvideopool.h"
25 
26 
27 GST_DEBUG_CATEGORY_STATIC (gst_video_pool_debug);
28 #define GST_CAT_DEFAULT gst_video_pool_debug
29 
30 /**
31  * SECTION:gstvideopool
32  * @title: GstVideoBufferPool
33  * @short_description: GstBufferPool for raw video buffers
34  * @see_also: #GstBufferPool
35  *
36  * Special GstBufferPool subclass for raw video buffers.
37  *
38  * Allows configuration of video-specific requirements such as
39  * stride alignments or pixel padding, and can also be configured
40  * to automatically add #GstVideoMeta to the buffers.
41  */
42 
43 /**
44  * gst_buffer_pool_config_set_video_alignment:
45  * @config: a #GstStructure
46  * @align: a #GstVideoAlignment
47  *
48  * Set the video alignment in @align to the bufferpool configuration
49  * @config
50  */
51 void
gst_buffer_pool_config_set_video_alignment(GstStructure * config,GstVideoAlignment * align)52 gst_buffer_pool_config_set_video_alignment (GstStructure * config,
53     GstVideoAlignment * align)
54 {
55   g_return_if_fail (config != NULL);
56   g_return_if_fail (align != NULL);
57 
58   gst_structure_set (config,
59       "padding-top", G_TYPE_UINT, align->padding_top,
60       "padding-bottom", G_TYPE_UINT, align->padding_bottom,
61       "padding-left", G_TYPE_UINT, align->padding_left,
62       "padding-right", G_TYPE_UINT, align->padding_right,
63       "stride-align0", G_TYPE_UINT, align->stride_align[0],
64       "stride-align1", G_TYPE_UINT, align->stride_align[1],
65       "stride-align2", G_TYPE_UINT, align->stride_align[2],
66       "stride-align3", G_TYPE_UINT, align->stride_align[3], NULL);
67 }
68 
69 /**
70  * gst_buffer_pool_config_get_video_alignment:
71  * @config: a #GstStructure
72  * @align: a #GstVideoAlignment
73  *
74  * Get the video alignment from the bufferpool configuration @config in
75  * in @align
76  *
77  * Returns: %TRUE if @config could be parsed correctly.
78  */
79 gboolean
gst_buffer_pool_config_get_video_alignment(GstStructure * config,GstVideoAlignment * align)80 gst_buffer_pool_config_get_video_alignment (GstStructure * config,
81     GstVideoAlignment * align)
82 {
83   g_return_val_if_fail (config != NULL, FALSE);
84   g_return_val_if_fail (align != NULL, FALSE);
85 
86   return gst_structure_get (config,
87       "padding-top", G_TYPE_UINT, &align->padding_top,
88       "padding-bottom", G_TYPE_UINT, &align->padding_bottom,
89       "padding-left", G_TYPE_UINT, &align->padding_left,
90       "padding-right", G_TYPE_UINT, &align->padding_right,
91       "stride-align0", G_TYPE_UINT, &align->stride_align[0],
92       "stride-align1", G_TYPE_UINT, &align->stride_align[1],
93       "stride-align2", G_TYPE_UINT, &align->stride_align[2],
94       "stride-align3", G_TYPE_UINT, &align->stride_align[3], NULL);
95 }
96 
97 /* bufferpool */
98 struct _GstVideoBufferPoolPrivate
99 {
100   GstVideoInfo info;
101   GstVideoAlignment video_align;
102   gboolean add_videometa;
103   gboolean need_alignment;
104   GstAllocator *allocator;
105   GstAllocationParams params;
106 };
107 
108 static void gst_video_buffer_pool_finalize (GObject * object);
109 
110 #define gst_video_buffer_pool_parent_class parent_class
111 G_DEFINE_TYPE_WITH_PRIVATE (GstVideoBufferPool, gst_video_buffer_pool,
112     GST_TYPE_BUFFER_POOL);
113 
114 static const gchar **
video_buffer_pool_get_options(GstBufferPool * pool)115 video_buffer_pool_get_options (GstBufferPool * pool)
116 {
117   static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META,
118     GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, NULL
119   };
120   return options;
121 }
122 
123 static gboolean
video_buffer_pool_set_config(GstBufferPool * pool,GstStructure * config)124 video_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
125 {
126   GstVideoBufferPool *vpool = GST_VIDEO_BUFFER_POOL_CAST (pool);
127   GstVideoBufferPoolPrivate *priv = vpool->priv;
128   GstVideoInfo info;
129   GstCaps *caps;
130   guint size, min_buffers, max_buffers;
131   gint width, height;
132   GstAllocator *allocator;
133   GstAllocationParams params;
134 
135   if (!gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers,
136           &max_buffers))
137     goto wrong_config;
138 
139   if (caps == NULL)
140     goto no_caps;
141 
142   /* now parse the caps from the config */
143   if (!gst_video_info_from_caps (&info, caps))
144     goto wrong_caps;
145 
146   if (size < info.size)
147     goto wrong_size;
148 
149   if (!gst_buffer_pool_config_get_allocator (config, &allocator, &params))
150     goto wrong_config;
151 
152   width = info.width;
153   height = info.height;
154 
155   GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, width, height, caps);
156 
157   priv->params = params;
158   if (priv->allocator)
159     gst_object_unref (priv->allocator);
160   if ((priv->allocator = allocator))
161     gst_object_ref (allocator);
162 
163   /* enable metadata based on config of the pool */
164   priv->add_videometa =
165       gst_buffer_pool_config_has_option (config,
166       GST_BUFFER_POOL_OPTION_VIDEO_META);
167 
168   /* parse extra alignment info */
169   priv->need_alignment = gst_buffer_pool_config_has_option (config,
170       GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT);
171 
172   if (priv->need_alignment && priv->add_videometa) {
173     guint max_align, n;
174 
175     gst_buffer_pool_config_get_video_alignment (config, &priv->video_align);
176 
177     /* ensure GstAllocationParams alignment is compatible with video alignment */
178     max_align = priv->params.align;
179     for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
180       max_align |= priv->video_align.stride_align[n];
181 
182     for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
183       priv->video_align.stride_align[n] = max_align;
184 
185     /* apply the alignment to the info */
186     if (!gst_video_info_align (&info, &priv->video_align))
187       goto failed_to_align;
188 
189     gst_buffer_pool_config_set_video_alignment (config, &priv->video_align);
190 
191     if (priv->params.align < max_align) {
192       GST_WARNING_OBJECT (pool, "allocation params alignment %u is smaller "
193           "than the max specified video stride alignment %u, fixing",
194           (guint) priv->params.align, max_align);
195       priv->params.align = max_align;
196       gst_buffer_pool_config_set_allocator (config, allocator, &priv->params);
197     }
198   }
199   info.size = MAX (size, info.size);
200   priv->info = info;
201 
202   gst_buffer_pool_config_set_params (config, caps, info.size, min_buffers,
203       max_buffers);
204 
205   return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config);
206 
207   /* ERRORS */
208 wrong_config:
209   {
210     GST_WARNING_OBJECT (pool, "invalid config");
211     return FALSE;
212   }
213 no_caps:
214   {
215     GST_WARNING_OBJECT (pool, "no caps in config");
216     return FALSE;
217   }
218 wrong_caps:
219   {
220     GST_WARNING_OBJECT (pool,
221         "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
222     return FALSE;
223   }
224 wrong_size:
225   {
226     GST_WARNING_OBJECT (pool,
227         "Provided size is to small for the caps: %u < %" G_GSIZE_FORMAT, size,
228         info.size);
229     return FALSE;
230   }
231 failed_to_align:
232   {
233     GST_WARNING_OBJECT (pool, "Failed to align");
234     return FALSE;
235   }
236 }
237 
238 static GstFlowReturn
video_buffer_pool_alloc(GstBufferPool * pool,GstBuffer ** buffer,GstBufferPoolAcquireParams * params)239 video_buffer_pool_alloc (GstBufferPool * pool, GstBuffer ** buffer,
240     GstBufferPoolAcquireParams * params)
241 {
242   GstVideoBufferPool *vpool = GST_VIDEO_BUFFER_POOL_CAST (pool);
243   GstVideoBufferPoolPrivate *priv = vpool->priv;
244   GstVideoInfo *info;
245 
246   info = &priv->info;
247 
248   GST_DEBUG_OBJECT (pool, "alloc %" G_GSIZE_FORMAT, info->size);
249 
250   *buffer =
251       gst_buffer_new_allocate (priv->allocator, info->size, &priv->params);
252   if (*buffer == NULL)
253     goto no_memory;
254 
255   if (priv->add_videometa) {
256     GST_DEBUG_OBJECT (pool, "adding GstVideoMeta");
257 
258     gst_buffer_add_video_meta_full (*buffer, GST_VIDEO_FRAME_FLAG_NONE,
259         GST_VIDEO_INFO_FORMAT (info),
260         GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info),
261         GST_VIDEO_INFO_N_PLANES (info), info->offset, info->stride);
262   }
263 
264   return GST_FLOW_OK;
265 
266   /* ERROR */
267 no_memory:
268   {
269     GST_WARNING_OBJECT (pool, "can't create memory");
270     return GST_FLOW_ERROR;
271   }
272 }
273 
274 /**
275  * gst_video_buffer_pool_new:
276  *
277  * Create a new bufferpool that can allocate video frames. This bufferpool
278  * supports all the video bufferpool options.
279  *
280  * Returns: (transfer full): a new #GstBufferPool to allocate video frames
281  */
282 GstBufferPool *
gst_video_buffer_pool_new()283 gst_video_buffer_pool_new ()
284 {
285   GstVideoBufferPool *pool;
286 
287   pool = g_object_new (GST_TYPE_VIDEO_BUFFER_POOL, NULL);
288   gst_object_ref_sink (pool);
289 
290   GST_LOG_OBJECT (pool, "new video buffer pool %p", pool);
291 
292   return GST_BUFFER_POOL_CAST (pool);
293 }
294 
295 static void
gst_video_buffer_pool_class_init(GstVideoBufferPoolClass * klass)296 gst_video_buffer_pool_class_init (GstVideoBufferPoolClass * klass)
297 {
298   GObjectClass *gobject_class = (GObjectClass *) klass;
299   GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
300 
301   gobject_class->finalize = gst_video_buffer_pool_finalize;
302 
303   gstbufferpool_class->get_options = video_buffer_pool_get_options;
304   gstbufferpool_class->set_config = video_buffer_pool_set_config;
305   gstbufferpool_class->alloc_buffer = video_buffer_pool_alloc;
306 
307   GST_DEBUG_CATEGORY_INIT (gst_video_pool_debug, "videopool", 0,
308       "videopool object");
309 }
310 
311 static void
gst_video_buffer_pool_init(GstVideoBufferPool * pool)312 gst_video_buffer_pool_init (GstVideoBufferPool * pool)
313 {
314   pool->priv = gst_video_buffer_pool_get_instance_private (pool);
315 }
316 
317 static void
gst_video_buffer_pool_finalize(GObject * object)318 gst_video_buffer_pool_finalize (GObject * object)
319 {
320   GstVideoBufferPool *pool = GST_VIDEO_BUFFER_POOL_CAST (object);
321   GstVideoBufferPoolPrivate *priv = pool->priv;
322 
323   GST_LOG_OBJECT (pool, "finalize video buffer pool %p", pool);
324 
325   if (priv->allocator)
326     gst_object_unref (priv->allocator);
327 
328   G_OBJECT_CLASS (gst_video_buffer_pool_parent_class)->finalize (object);
329 }
330