winegstreamer: Introduce a new wg_init_gstreamer unixlib entry.
[wine.git] / dlls / winegstreamer / wg_allocator.c
blob14550ad8bccf39890862401e4c712d7d7ace43c0
1 /*
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
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include <assert.h>
28 #include <stdarg.h>
30 #include <gst/gst.h>
31 #include <gst/video/video.h>
32 #include <gst/audio/audio.h>
34 #include "unix_private.h"
36 #include "wine/list.h"
38 typedef struct
40 GstMemory parent;
41 struct list entry;
43 GstMemory *unix_memory;
44 GstMapInfo unix_map_info;
46 struct wg_sample *sample;
47 gsize written;
48 } WgMemory;
50 typedef struct
52 GstAllocator parent;
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;
60 } WgAllocator;
62 typedef struct
64 GstAllocatorClass parent_class;
65 } WgAllocatorClass;
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);
81 if (!memory->sample)
82 info->data = memory->unix_map_info.data;
83 else
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);
94 return 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;
151 WgMemory *memory;
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);
166 else
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);
187 if (memory->sample)
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)))
217 return 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))
229 return;
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;
251 WgMemory *memory;
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)
267 WgMemory *memory;
269 LIST_FOR_EACH_ENTRY(memory, &allocator->memory_list, WgMemory, entry)
270 if (memory->sample == sample)
271 return memory;
273 return NULL;
276 void wg_allocator_release_sample(GstAllocator *gst_allocator, struct wg_sample *sample,
277 bool discard_data)
279 WgAllocator *allocator = (WgAllocator *)gst_allocator;
280 WgMemory *memory;
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);