drmbufferpool: fix dmabuf leak
[gstreamer-omap:gst-plugins-bad.git] / gst-libs / gst / drm / gstdrmbufferpool.c
1 /*
2  * GStreamer
3  *
4  * Copyright (C) 2012 Texas Instruments
5  * Copyright (C) 2012 Collabora Ltd
6  *
7  * Authors:
8  *  Alessandro Decina <alessandro.decina@collabora.co.uk>
9  *  Rob Clark <rob.clark@linaro.org>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation
14  * version 2.1 of the License.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <string.h>
31
32 #include <gst/dmabuf/dmabuf.h>
33
34 #include "gstdrmbufferpool.h"
35
36 GST_DEBUG_CATEGORY (drmbufferpool_debug);
37 #define GST_CAT_DEFAULT drmbufferpool_debug
38
39 static GstDRMBuffer * gst_drm_buffer_new (GstDRMBufferPool * pool);
40 static void gst_drm_buffer_set_pool (GstDRMBuffer * self,
41     GstDRMBufferPool * pool);
42
43 /*
44  * GstDRMBufferPool:
45  */
46
47 G_DEFINE_TYPE (GstDRMBufferPool, gst_drm_buffer_pool,
48     GST_TYPE_MINI_OBJECT);
49
50 void
51 gst_drm_buffer_pool_initialize (GstDRMBufferPool * self,
52     GstElement * element, int fd, GstCaps * caps, guint size)
53 {
54   self->element = gst_object_ref (element);
55   self->fd   = fd;
56   self->dev  = omap_device_new (fd);
57   self->caps = NULL;
58   gst_drm_buffer_pool_set_caps (self, caps);
59   self->size = size;
60   self->head = NULL;
61   self->tail = NULL;
62   self->lock = g_mutex_new ();
63   self->running = TRUE;
64 }
65
66 GstDRMBufferPool *
67 gst_drm_buffer_pool_new (GstElement * element,
68     int fd, GstCaps * caps, guint size)
69 {
70   GstDRMBufferPool *self = (GstDRMBufferPool *)
71       gst_mini_object_new (GST_TYPE_DRM_BUFFER_POOL);
72
73   GST_DEBUG_OBJECT (element, "Creating DRM buffer pool with caps %" GST_PTR_FORMAT, caps);
74
75   gst_drm_buffer_pool_initialize (self, element, fd, caps, size);
76
77   return self;
78 }
79
80 /** get size of individual buffers within the bufferpool */
81 guint
82 gst_drm_buffer_pool_size (GstDRMBufferPool * self)
83 {
84   return self->size;
85 }
86
87 void
88 gst_drm_buffer_pool_set_caps (GstDRMBufferPool * self, GstCaps * caps)
89 {
90   gst_caps_replace (&self->caps, caps);
91   if (caps) {
92     GstStructure *s = gst_caps_get_structure (caps, 0);
93
94     self->strided =
95         !strcmp (gst_structure_get_name (s), "video/x-raw-yuv-strided");
96
97     gst_structure_get_int (s, "width", &self->width);
98     gst_structure_get_int (s, "height", &self->height);
99     gst_structure_get_fourcc (s, "format", &self->fourcc);
100   } else {
101     self->width = 0;
102     self->height = 0;
103     self->strided = FALSE;
104   }
105 }
106
107 gboolean
108 gst_drm_buffer_pool_check_caps (GstDRMBufferPool * self,
109     GstCaps * caps)
110 {
111   return gst_caps_is_strictly_equal (self->caps, caps);
112 }
113
114 /** destroy existing bufferpool */
115 void
116 gst_drm_buffer_pool_destroy (GstDRMBufferPool * self)
117 {
118   g_return_if_fail (self);
119
120   GST_DRM_BUFFER_POOL_LOCK (self);
121   self->running = FALSE;
122
123   GST_DEBUG_OBJECT (self->element, "destroy pool");
124
125   /* free all buffers on the freelist */
126   while (self->head) {
127     GstDRMBuffer *buf = self->head;
128     self->head = buf->next;
129     buf->next = NULL;
130     GST_DEBUG_OBJECT (self, "unreffing %p from freelist", buf);
131     GST_DRM_BUFFER_POOL_UNLOCK (self);
132     gst_buffer_unref (GST_BUFFER (buf));
133     GST_DRM_BUFFER_POOL_LOCK (self);
134   }
135   self->tail = NULL;
136   GST_DRM_BUFFER_POOL_UNLOCK (self);
137   gst_mini_object_unref (GST_MINI_OBJECT (self));
138 }
139
140 #if 0
141 static void
142 dump_list (GstDRMBufferPool * pool, GstDRMBuffer * buf)
143 {
144   GST_ERROR_OBJECT (pool->element, "LIST");
145   while (buf) {
146     GST_ERROR_OBJECT (pool->element, "BUF: %p", buf);
147     buf = buf->next;
148   }
149 }
150 #endif
151
152 /** get buffer from bufferpool, allocate new buffer if needed */
153 GstBuffer *
154 gst_drm_buffer_pool_get (GstDRMBufferPool * self, gboolean force_alloc)
155 {
156   GstDRMBuffer *buf = NULL;
157
158   g_return_val_if_fail (self, NULL);
159
160   GST_DRM_BUFFER_POOL_LOCK (self);
161   if (self->running) {
162     /* re-use a buffer off the freelist if any are available
163      */
164     if (!force_alloc && self->head) {
165 //      dump_list (self, self->head);
166       buf = self->head;
167       self->head = buf->next;
168       if (self->head == NULL)
169         self->tail = NULL;
170     } else {
171       buf = GST_DRM_BUFFER_POOL_GET_CLASS (self)->buffer_alloc (self);
172     }
173     if (self->caps)
174       gst_buffer_set_caps (GST_BUFFER (buf), self->caps);
175   }
176   GST_DRM_BUFFER_POOL_UNLOCK (self);
177
178   GST_LOG_OBJECT (self->element, "returning buf %p", buf);
179
180   return GST_BUFFER (buf);
181 }
182
183 static gboolean
184 gst_drm_buffer_pool_put (GstDRMBufferPool * self, GstDRMBuffer * buf)
185 {
186   gboolean reuse = FALSE;
187
188   if (buf->remove_from_pool)
189     return FALSE;
190
191   GST_DRM_BUFFER_POOL_LOCK (self);
192   if (self->running) {
193     reuse = TRUE;
194
195     GST_LOG_OBJECT (self->element, "reviving buffer %p", buf);
196     gst_buffer_ref (GST_BUFFER (buf));
197
198     buf->next = NULL;
199     if (self->tail)
200       self->tail->next = buf;
201     self->tail = buf;
202     if (self->head == NULL)
203       self->head = self->tail;
204     buf->remove_from_pool = FALSE;
205   } else {
206     GST_INFO_OBJECT (self->element, "the pool is shutting down");
207     buf->remove_from_pool = TRUE;
208   }
209   GST_DRM_BUFFER_POOL_UNLOCK (self);
210
211   return reuse;
212 }
213
214 static void
215 gst_drm_buffer_pool_finalize (GstDRMBufferPool * self)
216 {
217   GST_DEBUG_OBJECT (self->element, "finalize");
218   g_mutex_free (self->lock);
219   if (self->caps)
220     gst_caps_unref (self->caps);
221   gst_object_unref (self->element);
222   omap_device_del (self->dev);
223   GST_MINI_OBJECT_CLASS (gst_drm_buffer_pool_parent_class)->finalize
224       (GST_MINI_OBJECT (self));
225 }
226
227 static void
228 gst_drm_buffer_pool_class_init (GstDRMBufferPoolClass * klass)
229 {
230   GstMiniObjectClass *mini_object_class;
231
232   GST_DEBUG_CATEGORY_INIT (drmbufferpool_debug, "drmbufferpool", 0,
233       "DRM buffer pool");
234
235   mini_object_class = GST_MINI_OBJECT_CLASS (klass);
236   klass->buffer_alloc =
237       GST_DEBUG_FUNCPTR (gst_drm_buffer_new);
238   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
239       GST_DEBUG_FUNCPTR (gst_drm_buffer_pool_finalize);
240 }
241
242 static void
243 gst_drm_buffer_pool_init (GstDRMBufferPool * self)
244 {
245 }
246
247 /*
248  * GstDRMBuffer:
249  */
250
251 G_DEFINE_TYPE (GstDRMBuffer, gst_drm_buffer, GST_TYPE_BUFFER);
252
253 void
254 gst_drm_buffer_initialize (GstDRMBuffer * self,
255     GstDRMBufferPool * pool, struct omap_bo * bo)
256 {
257   GstDmaBuf *dmabuf;
258
259   self->bo = bo;
260
261   GST_BUFFER_DATA (self) = omap_bo_map (self->bo);
262   GST_BUFFER_SIZE (self) = pool->size;
263
264   /* attach dmabuf handle to buffer so that elements from other
265    * plugins can access for zero copy hw accel:
266    */
267   // XXX buffer doesn't take ownership of the GstDmaBuf...
268   dmabuf = gst_dma_buf_new (omap_bo_dmabuf (self->bo));
269   gst_buffer_set_dma_buf (GST_BUFFER (self), dmabuf);
270   gst_dma_buf_unref (dmabuf);
271
272   gst_drm_buffer_set_pool (self, pool);
273 }
274
275 static GstDRMBuffer *
276 gst_drm_buffer_new (GstDRMBufferPool * pool)
277 {
278   GstDRMBuffer *self = (GstDRMBuffer *)
279       gst_mini_object_new (GST_TYPE_DRM_BUFFER);
280
281   /* TODO: if allocation could be handled via libkms then this
282    * bufferpool implementation could be completely generic..
283    * otherwise we might want some support for various different
284    * drm drivers here:
285    */
286   struct omap_bo *bo = omap_bo_new (pool->dev, pool->size, OMAP_BO_WC);
287
288   gst_drm_buffer_initialize (self, pool, bo);
289
290   return self;
291 }
292
293 static void
294 gst_drm_buffer_set_pool (GstDRMBuffer * self, GstDRMBufferPool * pool)
295 {
296
297   GST_LOG_OBJECT (pool->element, "creating buffer %p in pool %p", self, pool);
298
299   self->pool = (GstDRMBufferPool *)
300       gst_mini_object_ref (GST_MINI_OBJECT (pool));
301   self->remove_from_pool = FALSE;
302
303   if (pool->caps)
304     gst_buffer_set_caps (GST_BUFFER (self), pool->caps);
305 }
306
307 static void
308 gst_drm_buffer_finalize (GstDRMBuffer * self)
309 {
310   GstDRMBufferPool *pool = self->pool;
311   gboolean resuscitated = FALSE;
312
313   GST_LOG_OBJECT (pool->element, "finalizing buffer %p", self);
314
315   resuscitated = gst_drm_buffer_pool_put (pool, self);
316   if (resuscitated)
317     return;
318
319   if (GST_DRM_BUFFER_POOL_GET_CLASS (self->pool)->buffer_cleanup) {
320     GST_DRM_BUFFER_POOL_GET_CLASS (self->pool)->buffer_cleanup (
321         self->pool, self);
322   }
323
324   GST_BUFFER_DATA (self) = NULL;
325   omap_bo_del (self->bo);
326
327   gst_mini_object_unref (GST_MINI_OBJECT (pool));
328
329   GST_MINI_OBJECT_CLASS (gst_drm_buffer_parent_class)->finalize
330       (GST_MINI_OBJECT (self));
331 }
332
333 static void
334 gst_drm_buffer_class_init (GstDRMBufferClass * klass)
335 {
336   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (klass);
337
338   mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
339       GST_DEBUG_FUNCPTR (gst_drm_buffer_finalize);
340 }
341
342 static void
343 gst_drm_buffer_init (GstDRMBuffer * buffer)
344 {
345 }