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"
38 GST_DEBUG_CATEGORY_EXTERN(wine
);
39 #define GST_CAT_DEFAULT wine
46 GstMemory
*unix_memory
;
47 GstMapInfo unix_map_info
;
49 struct wg_sample
*sample
;
57 wg_allocator_request_sample_cb request_sample
;
58 void *request_sample_context
;
60 pthread_mutex_t mutex
;
61 pthread_cond_t release_cond
;
62 struct list memory_list
;
67 GstAllocatorClass parent_class
;
70 G_DEFINE_TYPE(WgAllocator
, wg_allocator
, GST_TYPE_ALLOCATOR
);
72 static gpointer
wg_allocator_map(GstMemory
*gst_memory
, GstMapInfo
*info
, gsize maxsize
)
74 WgAllocator
*allocator
= (WgAllocator
*)gst_memory
->allocator
;
75 WgMemory
*memory
= (WgMemory
*)gst_memory
;
77 if (gst_memory
->parent
)
78 return wg_allocator_map(gst_memory
->parent
, info
, maxsize
);
80 GST_LOG("memory %p, info %p, maxsize %#zx", memory
, info
, maxsize
);
82 pthread_mutex_lock(&allocator
->mutex
);
85 info
->data
= memory
->unix_map_info
.data
;
88 InterlockedIncrement(&memory
->sample
->refcount
);
89 info
->data
= memory
->sample
->data
;
91 if (info
->flags
& GST_MAP_WRITE
)
92 memory
->written
= max(memory
->written
, maxsize
);
94 pthread_mutex_unlock(&allocator
->mutex
);
96 GST_INFO("Mapped memory %p to %p", memory
, info
->data
);
100 static void wg_allocator_unmap(GstMemory
*gst_memory
, GstMapInfo
*info
)
102 WgAllocator
*allocator
= (WgAllocator
*)gst_memory
->allocator
;
103 WgMemory
*memory
= (WgMemory
*)gst_memory
;
105 if (gst_memory
->parent
)
106 return wg_allocator_unmap(gst_memory
->parent
, info
);
108 GST_LOG("memory %p, info %p", memory
, info
);
110 pthread_mutex_lock(&allocator
->mutex
);
112 if (memory
->sample
&& info
->data
== memory
->sample
->data
)
114 InterlockedDecrement(&memory
->sample
->refcount
);
115 pthread_cond_signal(&allocator
->release_cond
);
118 pthread_mutex_unlock(&allocator
->mutex
);
121 static void wg_allocator_init(WgAllocator
*allocator
)
123 GST_LOG("allocator %p", allocator
);
125 allocator
->parent
.mem_type
= "Wine";
127 allocator
->parent
.mem_map_full
= wg_allocator_map
;
128 allocator
->parent
.mem_unmap_full
= wg_allocator_unmap
;
130 GST_OBJECT_FLAG_SET(allocator
, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC
);
132 pthread_mutex_init(&allocator
->mutex
, NULL
);
133 pthread_cond_init(&allocator
->release_cond
, NULL
);
134 list_init(&allocator
->memory_list
);
137 static void wg_allocator_finalize(GObject
*object
)
139 WgAllocator
*allocator
= (WgAllocator
*)object
;
141 GST_LOG("allocator %p", allocator
);
143 pthread_cond_destroy(&allocator
->release_cond
);
144 pthread_mutex_destroy(&allocator
->mutex
);
146 G_OBJECT_CLASS(wg_allocator_parent_class
)->finalize(object
);
149 static GstMemory
*wg_allocator_alloc(GstAllocator
*gst_allocator
, gsize size
,
150 GstAllocationParams
*params
)
152 WgAllocator
*allocator
= (WgAllocator
*)gst_allocator
;
155 GST_LOG("allocator %p, size %#zx, params %p", allocator
, size
, params
);
157 memory
= g_slice_new0(WgMemory
);
158 gst_memory_init(GST_MEMORY_CAST(memory
), 0, GST_ALLOCATOR_CAST(allocator
),
159 NULL
, size
, 0, 0, size
);
160 memory
->unix_memory
= gst_allocator_alloc(NULL
, size
, params
);
161 gst_memory_map(memory
->unix_memory
, &memory
->unix_map_info
, GST_MAP_WRITE
);
163 pthread_mutex_lock(&allocator
->mutex
);
165 memory
->sample
= allocator
->request_sample(size
, allocator
->request_sample_context
);
166 list_add_tail(&allocator
->memory_list
, &memory
->entry
);
168 pthread_mutex_unlock(&allocator
->mutex
);
170 GST_INFO("Allocated memory %p, sample %p, unix_memory %p, data %p", memory
,
171 memory
->sample
, memory
->unix_memory
, memory
->unix_map_info
.data
);
172 return (GstMemory
*)memory
;
175 static void wg_allocator_free(GstAllocator
*gst_allocator
, GstMemory
*gst_memory
)
177 WgAllocator
*allocator
= (WgAllocator
*)gst_allocator
;
178 WgMemory
*memory
= (WgMemory
*)gst_memory
;
180 GST_LOG("allocator %p, memory %p", allocator
, memory
);
182 pthread_mutex_lock(&allocator
->mutex
);
185 InterlockedDecrement(&memory
->sample
->refcount
);
186 memory
->sample
= NULL
;
188 list_remove(&memory
->entry
);
190 pthread_mutex_unlock(&allocator
->mutex
);
192 gst_memory_unmap(memory
->unix_memory
, &memory
->unix_map_info
);
193 gst_memory_unref(memory
->unix_memory
);
194 g_slice_free(WgMemory
, memory
);
197 static void wg_allocator_class_init(WgAllocatorClass
*klass
)
199 GstAllocatorClass
*parent_class
= (GstAllocatorClass
*)klass
;
200 GObjectClass
*root_class
= (GObjectClass
*)klass
;
202 GST_LOG("klass %p", klass
);
204 parent_class
->alloc
= wg_allocator_alloc
;
205 parent_class
->free
= wg_allocator_free
;
206 root_class
->finalize
= wg_allocator_finalize
;
209 GstAllocator
*wg_allocator_create(wg_allocator_request_sample_cb request_sample
, void *request_sample_context
)
211 WgAllocator
*allocator
;
213 if (!(allocator
= g_object_new(wg_allocator_get_type(), NULL
)))
216 allocator
->request_sample
= request_sample
;
217 allocator
->request_sample_context
= request_sample_context
;
218 return GST_ALLOCATOR(allocator
);
221 static void release_memory_sample(WgAllocator
*allocator
, WgMemory
*memory
, bool discard_data
)
223 struct wg_sample
*sample
;
225 if (!(sample
= memory
->sample
))
228 while (sample
->refcount
> 1)
230 GST_WARNING("Waiting for sample %p to be unmapped", sample
);
231 pthread_cond_wait(&allocator
->release_cond
, &allocator
->mutex
);
233 InterlockedDecrement(&sample
->refcount
);
235 if (memory
->written
&& !discard_data
)
237 GST_WARNING("Copying %#zx bytes from sample %p, back to memory %p", memory
->written
, sample
, memory
);
238 memcpy(memory
->unix_map_info
.data
, memory
->sample
->data
, memory
->written
);
241 memory
->sample
= NULL
;
242 GST_INFO("Released sample %p from memory %p", sample
, memory
);
245 void wg_allocator_destroy(GstAllocator
*gst_allocator
)
247 WgAllocator
*allocator
= (WgAllocator
*)gst_allocator
;
250 GST_LOG("allocator %p", allocator
);
252 pthread_mutex_lock(&allocator
->mutex
);
253 LIST_FOR_EACH_ENTRY(memory
, &allocator
->memory_list
, WgMemory
, entry
)
254 release_memory_sample(allocator
, memory
, true);
255 pthread_mutex_unlock(&allocator
->mutex
);
257 g_object_unref(allocator
);
259 GST_INFO("Destroyed buffer allocator %p", allocator
);
262 static WgMemory
*find_sample_memory(WgAllocator
*allocator
, struct wg_sample
*sample
)
266 LIST_FOR_EACH_ENTRY(memory
, &allocator
->memory_list
, WgMemory
, entry
)
267 if (memory
->sample
== sample
)
273 void wg_allocator_release_sample(GstAllocator
*gst_allocator
, struct wg_sample
*sample
,
276 WgAllocator
*allocator
= (WgAllocator
*)gst_allocator
;
279 GST_LOG("allocator %p, sample %p, discard_data %u", allocator
, sample
, discard_data
);
281 pthread_mutex_lock(&allocator
->mutex
);
282 if ((memory
= find_sample_memory(allocator
, sample
)))
283 release_memory_sample(allocator
, memory
, discard_data
);
284 else if (sample
->refcount
)
285 GST_ERROR("Couldn't find memory for sample %p", sample
);
286 pthread_mutex_unlock(&allocator
->mutex
);