312a3d4746d79aa225a3ccf83e22ac8f0f3e7947
[platform/upstream/gstreamer.git] / gst-libs / gst / allocators / gstfdmemory.c
1 /* GStreamer fd backed memory
2  * Copyright (C) 2013 Linaro SA
3  * Author: Benjamin Gaignard <benjamin.gaignard@linaro.org> for Linaro.
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 mordetails.
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., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:gstfdmemory
23  * @short_description: Memory wrapper for fd backed memory
24  * @see_also: #GstMemory
25  *
26  * Since: 1.4
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "gstfdmemory.h"
34
35 #ifdef HAVE_MMAP
36 #include <sys/mman.h>
37 #include <unistd.h>
38 #endif
39
40 GST_DEBUG_CATEGORY_STATIC (gst_fdmemory_debug);
41 #define GST_CAT_DEFAULT gst_fdmemory_debug
42
43 typedef struct
44 {
45   GstMemory mem;
46
47   GstFdMemoryFlags flags;
48   gint fd;
49   gpointer data;
50   gint mmapping_flags;
51   gint mmap_count;
52   GMutex lock;
53 } GstFdMemory;
54
55 static void
56 gst_fd_mem_free (GstAllocator * allocator, GstMemory * gmem)
57 {
58 #ifdef HAVE_MMAP
59   GstFdMemory *mem = (GstFdMemory *) gmem;
60
61   if (mem->data) {
62     if (!(mem->flags & GST_FD_MEMORY_FLAG_KEEP_MAPPED))
63       g_warning (G_STRLOC ":%s: Freeing memory %p still mapped", G_STRFUNC,
64           mem);
65
66     munmap ((void *) mem->data, gmem->maxsize);
67   }
68   if (mem->fd >= 0 && gmem->parent == NULL)
69     close (mem->fd);
70   g_mutex_clear (&mem->lock);
71   g_slice_free (GstFdMemory, mem);
72   GST_DEBUG ("%p: freed", mem);
73 #endif
74 }
75
76 static gpointer
77 gst_fd_mem_map (GstMemory * gmem, gsize maxsize, GstMapFlags flags)
78 {
79 #ifdef HAVE_MMAP
80   GstFdMemory *mem = (GstFdMemory *) gmem;
81   gint prot;
82   gpointer ret = NULL;
83
84   if (gmem->parent)
85     return gst_fd_mem_map (gmem->parent, maxsize, flags);
86
87   prot = flags & GST_MAP_READ ? PROT_READ : 0;
88   prot |= flags & GST_MAP_WRITE ? PROT_WRITE : 0;
89
90   g_mutex_lock (&mem->lock);
91   /* do not mmap twice the buffer */
92   if (mem->data) {
93     /* only return address if mapping flags are a subset
94      * of the previous flags */
95     if ((mem->mmapping_flags & prot) == prot) {
96       ret = mem->data;
97       mem->mmap_count++;
98     }
99
100     goto out;
101   }
102
103   if (mem->fd != -1) {
104     gint flags;
105
106     flags =
107         (mem->flags & GST_FD_MEMORY_FLAG_MAP_PRIVATE) ? MAP_PRIVATE :
108         MAP_SHARED;
109
110     mem->data = mmap (0, gmem->maxsize, prot, flags, mem->fd, 0);
111     if (mem->data == MAP_FAILED) {
112       mem->data = NULL;
113       GST_ERROR ("%p: fd %d: mmap failed: %s", mem, mem->fd,
114           g_strerror (errno));
115       goto out;
116     }
117   }
118
119   GST_DEBUG ("%p: fd %d: mapped %p", mem, mem->fd, mem->data);
120
121   if (mem->data) {
122     mem->mmapping_flags = prot;
123     mem->mmap_count++;
124     ret = mem->data;
125   }
126
127 out:
128   g_mutex_unlock (&mem->lock);
129   return ret;
130 #else /* !HAVE_MMAP */
131   return FALSE;
132 #endif
133 }
134
135 static void
136 gst_fd_mem_unmap (GstMemory * gmem)
137 {
138 #ifdef HAVE_MMAP
139   GstFdMemory *mem = (GstFdMemory *) gmem;
140
141   if (gmem->parent)
142     return gst_fd_mem_unmap (gmem->parent);
143
144   if (mem->flags & GST_FD_MEMORY_FLAG_KEEP_MAPPED)
145     return;
146
147   g_mutex_lock (&mem->lock);
148   if (mem->data && !(--mem->mmap_count)) {
149     munmap ((void *) mem->data, gmem->maxsize);
150     mem->data = NULL;
151     mem->mmapping_flags = 0;
152     GST_DEBUG ("%p: fd %d unmapped", mem, mem->fd);
153   }
154   g_mutex_unlock (&mem->lock);
155 #endif
156 }
157
158 static GstMemory *
159 gst_fd_mem_share (GstMemory * gmem, gssize offset, gssize size)
160 {
161 #ifdef HAVE_MMAP
162   GstFdMemory *mem = (GstFdMemory *) gmem;
163   GstFdMemory *sub;
164   GstMemory *parent;
165
166   GST_DEBUG ("%p: share %" G_GSSIZE_FORMAT " %" G_GSIZE_FORMAT, mem, offset,
167       size);
168
169   /* find the real parent */
170   if ((parent = mem->mem.parent) == NULL)
171     parent = (GstMemory *) mem;
172
173   if (size == -1)
174     size = gmem->maxsize - offset;
175
176   sub = g_slice_new0 (GstFdMemory);
177   /* the shared memory is always readonly */
178   gst_memory_init (GST_MEMORY_CAST (sub), GST_MINI_OBJECT_FLAGS (parent) |
179       GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->mem.allocator, parent,
180       mem->mem.maxsize, mem->mem.align, mem->mem.offset + offset, size);
181
182   sub->fd = mem->fd;
183   g_mutex_init (&sub->lock);
184
185   return GST_MEMORY_CAST (sub);
186 #else /* !HAVE_MMAP */
187   return NULL;
188 #endif
189 }
190
191 G_DEFINE_TYPE (GstFdAllocator, gst_fd_allocator, GST_TYPE_ALLOCATOR);
192
193 static void
194 gst_fd_allocator_class_init (GstFdAllocatorClass * klass)
195 {
196   GstAllocatorClass *allocator_class;
197
198   allocator_class = (GstAllocatorClass *) klass;
199
200   allocator_class->alloc = NULL;
201   allocator_class->free = gst_fd_mem_free;
202
203   GST_DEBUG_CATEGORY_INIT (gst_fdmemory_debug, "fdmemory", 0,
204       "GstFdMemory and GstFdAllocator");
205 }
206
207 static void
208 gst_fd_allocator_init (GstFdAllocator * allocator)
209 {
210   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
211
212   alloc->mem_type = GST_ALLOCATOR_FD;
213
214   alloc->mem_map = gst_fd_mem_map;
215   alloc->mem_unmap = gst_fd_mem_unmap;
216   alloc->mem_share = gst_fd_mem_share;
217
218   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
219 }
220
221 /**
222  * gst_fd_allocator_new:
223  *
224  * Return a new fd allocator.
225  *
226  * Returns: (transfer full): a new fd allocator, or NULL if the allocator
227  *    isn't available. Use gst_object_unref() to release the allocator after
228  *    usage
229  *
230  * Since: 1.6
231  */
232 GstAllocator *
233 gst_fd_allocator_new (void)
234 {
235   return g_object_new (GST_TYPE_FD_ALLOCATOR, NULL);
236 }
237
238 /**
239  * gst_fd_allocator_alloc:
240  * @allocator: allocator to be used for this memory
241  * @fd: file descriptor
242  * @size: memory size
243  * @flags: extra #GstFdMemoryFlags
244  *
245  * Return a %GstMemory that wraps a generic file descriptor.
246  *
247  * Returns: (transfer full): a GstMemory based on @allocator.
248  * When the buffer will be released the allocator will close the @fd.
249  * The memory is only mmapped on gst_buffer_mmap() request.
250  *
251  * Since: 1.6
252  */
253 GstMemory *
254 gst_fd_allocator_alloc (GstAllocator * allocator, gint fd, gsize size,
255     GstFdMemoryFlags flags)
256 {
257 #ifdef HAVE_MMAP
258   GstFdMemory *mem;
259
260   g_return_val_if_fail (GST_IS_FD_ALLOCATOR (allocator), NULL);
261
262   mem = g_slice_new0 (GstFdMemory);
263   gst_memory_init (GST_MEMORY_CAST (mem), 0, GST_ALLOCATOR_CAST (allocator),
264       NULL, size, 0, 0, size);
265
266   mem->flags = flags;
267   mem->fd = fd;
268   g_mutex_init (&mem->lock);
269
270   GST_DEBUG ("%p: fd: %d size %" G_GSIZE_FORMAT, mem, mem->fd,
271       mem->mem.maxsize);
272
273   return (GstMemory *) mem;
274 #else /* !HAVE_MMAP */
275   return NULL;
276 #endif
277 }
278
279 /**
280  * gst_is_fd_memory:
281  * @mem: #GstMemory
282  *
283  * Check if @mem is memory backed by an fd
284  *
285  * Returns: %TRUE when @mem has an fd that can be retrieved with
286  * gst_fd_memory_get_fd().
287  *
288  * Since: 1.6
289  */
290 gboolean
291 gst_is_fd_memory (GstMemory * mem)
292 {
293   g_return_val_if_fail (mem != NULL, FALSE);
294
295   return GST_IS_FD_ALLOCATOR (mem->allocator);
296 }
297
298 /**
299  * gst_fd_memory_get_fd:
300  * @mem: #GstMemory
301  *
302  * Get the fd from @mem. Call gst_is_fd_memory() to check if @mem has
303  * an fd.
304  *
305  * Returns: the fd of @mem or -1 when there is no fd on @mem
306  *
307  * Since: 1.6
308  */
309 gint
310 gst_fd_memory_get_fd (GstMemory * mem)
311 {
312   g_return_val_if_fail (mem != NULL, -1);
313   g_return_val_if_fail (GST_IS_FD_ALLOCATOR (mem->allocator), -1);
314
315   return ((GstFdMemory *) mem)->fd;
316 }