2 * GStreamer memory allocator
4 * Copyright 2022 RĂ©mi Bernon for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include <gst/video/video.h>
32 #include <gst/audio/audio.h>
34 #include "unix_private.h"
36 #include "wine/list.h"
43 GstMemory
*unix_memory
;
44 GstMapInfo unix_map_info
;
46 struct wg_sample
*sample
;
54 wg_allocator_request_sample_cb request_sample
;
55 void *request_sample_context
;
57 pthread_mutex_t mutex
;
58 pthread_cond_t release_cond
;
59 struct list memory_list
;
64 GstAllocatorClass parent_class
;
67 G_DEFINE_TYPE(WgAllocator
, wg_allocator
, GST_TYPE_ALLOCATOR
);
69 static gpointer
wg_allocator_map(GstMemory
*gst_memory
, GstMapInfo
*info
, gsize maxsize
)
71 WgAllocator
*allocator
= (WgAllocator
*)gst_memory
->allocator
;
72 WgMemory
*memory
= (WgMemory
*)gst_memory
;
74 if (gst_memory
->parent
)
75 return wg_allocator_map(gst_memory
->parent
, info
, maxsize
);
77 GST_LOG("memory %p, info %p, maxsize %#zx", memory
, info
, maxsize
);
79 pthread_mutex_lock(&allocator
->mutex
);
82 info
->data
= memory
->unix_map_info
.data
;
85 InterlockedIncrement(&memory
->sample
->refcount
);
86 info
->data
= memory
->sample
->data
;
88 if (info
->flags
& GST_MAP_WRITE
)
89 memory
->written
= max(memory
->written
, maxsize
);
91 pthread_mutex_unlock(&allocator
->mutex
);
93 GST_INFO("Mapped memory %p to %p", memory
, info
->data
);
97 static void wg_allocator_unmap(GstMemory
*gst_memory
, GstMapInfo
*info
)
99 WgAllocator
*allocator
= (WgAllocator
*)gst_memory
->allocator
;
100 WgMemory
*memory
= (WgMemory
*)gst_memory
;
102 if (gst_memory
->parent
)
103 return wg_allocator_unmap(gst_memory
->parent
, info
);
105 GST_LOG("memory %p, info %p", memory
, info
);
107 pthread_mutex_lock(&allocator
->mutex
);
109 if (memory
->sample
&& info
->data
== memory
->sample
->data
)
111 InterlockedDecrement(&memory
->sample
->refcount
);
112 pthread_cond_signal(&allocator
->release_cond
);
115 pthread_mutex_unlock(&allocator
->mutex
);
118 static void wg_allocator_init(WgAllocator
*allocator
)
120 GST_LOG("allocator %p", allocator
);
122 allocator
->parent
.mem_type
= "Wine";
124 allocator
->parent
.mem_map_full
= wg_allocator_map
;
125 allocator
->parent
.mem_unmap_full
= wg_allocator_unmap
;
127 GST_OBJECT_FLAG_SET(allocator
, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC
);
129 pthread_mutex_init(&allocator
->mutex
, NULL
);
130 pthread_cond_init(&allocator
->release_cond
, NULL
);
131 list_init(&allocator
->memory_list
);
134 static void wg_allocator_finalize(GObject
*object
)
136 WgAllocator
*allocator
= (WgAllocator
*)object
;
138 GST_LOG("allocator %p", allocator
);
140 pthread_cond_destroy(&allocator
->release_cond
);
141 pthread_mutex_destroy(&allocator
->mutex
);
143 G_OBJECT_CLASS(wg_allocator_parent_class
)->finalize(object
);
146 static GstMemory
*wg_allocator_alloc(GstAllocator
*gst_allocator
, gsize size
,
147 GstAllocationParams
*params
)
149 WgAllocator
*allocator
= (WgAllocator
*)gst_allocator
;
150 struct wg_sample
*sample
;
153 GST_LOG("allocator %p, size %#zx, params %p", allocator
, size
, params
);
155 memory
= g_slice_new0(WgMemory
);
156 gst_memory_init(GST_MEMORY_CAST(memory
), 0, GST_ALLOCATOR_CAST(allocator
),
157 NULL
, size
, 0, 0, size
);
158 memory
->unix_memory
= gst_allocator_alloc(NULL
, size
, params
);
159 gst_memory_map(memory
->unix_memory
, &memory
->unix_map_info
, GST_MAP_WRITE
);
161 pthread_mutex_lock(&allocator
->mutex
);
163 sample
= allocator
->request_sample(size
, allocator
->request_sample_context
);
164 if (sample
&& sample
->max_size
< size
)
165 InterlockedDecrement(&sample
->refcount
);
167 memory
->sample
= sample
;
169 list_add_tail(&allocator
->memory_list
, &memory
->entry
);
171 pthread_mutex_unlock(&allocator
->mutex
);
173 GST_INFO("Allocated memory %p, sample %p, unix_memory %p, data %p", memory
,
174 memory
->sample
, memory
->unix_memory
, memory
->unix_map_info
.data
);
175 return (GstMemory
*)memory
;
178 static void wg_allocator_free(GstAllocator
*gst_allocator
, GstMemory
*gst_memory
)
180 WgAllocator
*allocator
= (WgAllocator
*)gst_allocator
;
181 WgMemory
*memory
= (WgMemory
*)gst_memory
;
183 GST_LOG("allocator %p, memory %p", allocator
, memory
);
185 pthread_mutex_lock(&allocator
->mutex
);
188 InterlockedDecrement(&memory
->sample
->refcount
);
189 memory
->sample
= NULL
;
191 list_remove(&memory
->entry
);
193 pthread_mutex_unlock(&allocator
->mutex
);
195 gst_memory_unmap(memory
->unix_memory
, &memory
->unix_map_info
);
196 gst_memory_unref(memory
->unix_memory
);
197 g_slice_free(WgMemory
, memory
);
200 static void wg_allocator_class_init(WgAllocatorClass
*klass
)
202 GstAllocatorClass
*parent_class
= (GstAllocatorClass
*)klass
;
203 GObjectClass
*root_class
= (GObjectClass
*)klass
;
205 GST_LOG("klass %p", klass
);
207 parent_class
->alloc
= wg_allocator_alloc
;
208 parent_class
->free
= wg_allocator_free
;
209 root_class
->finalize
= wg_allocator_finalize
;
212 GstAllocator
*wg_allocator_create(wg_allocator_request_sample_cb request_sample
, void *request_sample_context
)
214 WgAllocator
*allocator
;
216 if (!(allocator
= g_object_new(wg_allocator_get_type(), NULL
)))
219 allocator
->request_sample
= request_sample
;
220 allocator
->request_sample_context
= request_sample_context
;
221 return GST_ALLOCATOR(allocator
);
224 static void release_memory_sample(WgAllocator
*allocator
, WgMemory
*memory
, bool discard_data
)
226 struct wg_sample
*sample
;
228 if (!(sample
= memory
->sample
))
231 while (sample
->refcount
> 1)
233 GST_WARNING("Waiting for sample %p to be unmapped", sample
);
234 pthread_cond_wait(&allocator
->release_cond
, &allocator
->mutex
);
236 InterlockedDecrement(&sample
->refcount
);
238 if (memory
->written
&& !discard_data
)
240 GST_WARNING("Copying %#zx bytes from sample %p, back to memory %p", memory
->written
, sample
, memory
);
241 memcpy(memory
->unix_map_info
.data
, memory
->sample
->data
, memory
->written
);
244 memory
->sample
= NULL
;
245 GST_INFO("Released sample %p from memory %p", sample
, memory
);
248 void wg_allocator_destroy(GstAllocator
*gst_allocator
)
250 WgAllocator
*allocator
= (WgAllocator
*)gst_allocator
;
253 GST_LOG("allocator %p", allocator
);
255 pthread_mutex_lock(&allocator
->mutex
);
256 LIST_FOR_EACH_ENTRY(memory
, &allocator
->memory_list
, WgMemory
, entry
)
257 release_memory_sample(allocator
, memory
, true);
258 pthread_mutex_unlock(&allocator
->mutex
);
260 g_object_unref(allocator
);
262 GST_INFO("Destroyed buffer allocator %p", allocator
);
265 static WgMemory
*find_sample_memory(WgAllocator
*allocator
, struct wg_sample
*sample
)
269 LIST_FOR_EACH_ENTRY(memory
, &allocator
->memory_list
, WgMemory
, entry
)
270 if (memory
->sample
== sample
)
276 void wg_allocator_release_sample(GstAllocator
*gst_allocator
, struct wg_sample
*sample
,
279 WgAllocator
*allocator
= (WgAllocator
*)gst_allocator
;
282 GST_LOG("allocator %p, sample %p, discard_data %u", allocator
, sample
, discard_data
);
284 pthread_mutex_lock(&allocator
->mutex
);
285 if ((memory
= find_sample_memory(allocator
, sample
)))
286 release_memory_sample(allocator
, memory
, discard_data
);
287 else if (sample
->refcount
)
288 GST_ERROR("Couldn't find memory for sample %p", sample
);
289 pthread_mutex_unlock(&allocator
->mutex
);