1 /*
2 * GStreamer
3 * Copyright (C) 2012 Edward Hervey <edward@collabora.com>
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 more details.
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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <gst/video/video.h>
26
27 #include "gstvdpvideomemory.h"
28 #include "gstvdputils.h"
29
30 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
31 GST_DEBUG_CATEGORY_STATIC (gst_vdp_video_mem_debug);
32 #define GST_CAT_DEFAULT gst_vdp_video_mem_debug
33
34 static GstAllocator *_vdp_video_allocator;
35
36
37 static void
_vdp_video_mem_init(GstVdpVideoMemory * mem,GstAllocator * allocator,GstMemory * parent,GstVdpDevice * device,GstVideoInfo * info)38 _vdp_video_mem_init (GstVdpVideoMemory * mem, GstAllocator * allocator,
39 GstMemory * parent, GstVdpDevice * device, GstVideoInfo * info)
40 {
41 gst_memory_init (GST_MEMORY_CAST (mem), GST_MEMORY_FLAG_NO_SHARE,
42 allocator, parent, GST_VIDEO_INFO_SIZE (info), 0, 0,
43 GST_VIDEO_INFO_SIZE (info));
44
45 mem->device = gst_object_ref (device);
46 mem->info = info;
47 mem->chroma_type = gst_video_info_to_vdp_chroma_type (info);
48 mem->ycbcr_format =
49 gst_video_format_to_vdp_ycbcr (GST_VIDEO_INFO_FORMAT (info));
50 mem->refcount = 0;
51
52 GST_DEBUG ("new VdpVideo memory");
53 }
54
55
56 static GstVdpVideoMemory *
_vdp_video_mem_new(GstAllocator * allocator,GstMemory * parent,GstVdpDevice * device,GstVideoInfo * info)57 _vdp_video_mem_new (GstAllocator * allocator, GstMemory * parent,
58 GstVdpDevice * device, GstVideoInfo * info)
59 {
60 VdpStatus status;
61 GstVdpVideoMemory *mem;
62 VdpVideoSurface surface;
63
64 mem = g_slice_new0 (GstVdpVideoMemory);
65 _vdp_video_mem_init (mem, allocator, parent, device, info);
66
67 GST_TRACE
68 ("Calling VdpVideoSurfaceCreate(chroma_type:%d, width:%d, height:%d)",
69 mem->chroma_type, mem->info->width, mem->info->height);
70
71 status =
72 device->vdp_video_surface_create (device->device, mem->chroma_type,
73 mem->info->width, mem->info->height, &surface);
74
75 if (status != VDP_STATUS_OK)
76 goto create_error;
77
78 /* device->vdp_video_surface_get_parameters (device->device, &chroma_type, */
79 /* &width, &height); */
80
81 GST_TRACE ("created surface %u", surface);
82
83 mem->surface = surface;
84
85 return mem;
86
87 /* ERRORS */
88 create_error:
89 {
90 GST_ERROR ("Failed to create video surface: %s",
91 device->vdp_get_error_string (status));
92 g_slice_free (GstVdpVideoMemory, mem);
93 return NULL;
94 }
95 }
96
97 static gboolean
ensure_data(GstVdpVideoMemory * vmem)98 ensure_data (GstVdpVideoMemory * vmem)
99 {
100 VdpStatus vdp_stat;
101 GstVideoInfo *info = vmem->info;
102 #ifndef GST_DISABLE_GST_DEBUG
103 GstClockTime before, after;
104 #endif
105
106 if (g_atomic_int_add (&vmem->refcount, 1) > 1)
107 return TRUE;
108
109 /* Allocate enough room to store data */
110 vmem->cache = g_malloc (GST_VIDEO_INFO_SIZE (info));
111 vmem->cached_data[0] = vmem->cache;
112 vmem->cached_data[1] = vmem->cache + GST_VIDEO_INFO_PLANE_OFFSET (info, 1);
113 vmem->cached_data[2] = vmem->cache + GST_VIDEO_INFO_PLANE_OFFSET (info, 2);
114 vmem->destination_pitches[0] = GST_VIDEO_INFO_PLANE_STRIDE (info, 0);
115 vmem->destination_pitches[1] = GST_VIDEO_INFO_PLANE_STRIDE (info, 1);
116 vmem->destination_pitches[2] = GST_VIDEO_INFO_PLANE_STRIDE (info, 2);
117
118 GST_DEBUG ("cached_data %p %p %p",
119 vmem->cached_data[0], vmem->cached_data[1], vmem->cached_data[2]);
120 GST_DEBUG ("pitches %d %d %d",
121 vmem->destination_pitches[0],
122 vmem->destination_pitches[1], vmem->destination_pitches[2]);
123
124 #ifndef GST_DISABLE_GST_DEBUG
125 before = gst_util_get_timestamp ();
126 #endif
127 vdp_stat =
128 vmem->device->vdp_video_surface_get_bits_ycbcr (vmem->surface,
129 vmem->ycbcr_format, vmem->cached_data, vmem->destination_pitches);
130 #ifndef GST_DISABLE_GST_DEBUG
131 after = gst_util_get_timestamp ();
132 #endif
133
134 GST_CAT_WARNING (GST_CAT_PERFORMANCE, "Downloading took %" GST_TIME_FORMAT,
135 GST_TIME_ARGS (after - before));
136
137 if (vdp_stat != VDP_STATUS_OK) {
138 GST_ERROR ("Failed to get bits : %s",
139 vmem->device->vdp_get_error_string (vdp_stat));
140 g_free (vmem->cache);
141 vmem->cache = NULL;
142 return FALSE;
143 }
144
145 return TRUE;
146 }
147
148 static void
release_data(GstVdpVideoMemory * vmem)149 release_data (GstVdpVideoMemory * vmem)
150 {
151 g_return_if_fail (vmem->refcount > 0);
152
153 if (g_atomic_int_dec_and_test (&vmem->refcount)) {
154 g_free (vmem->cache);
155 }
156 }
157
158 static gpointer
_vdp_video_mem_map(GstVdpVideoMemory * vmem,gsize maxsize,GstMapFlags flags)159 _vdp_video_mem_map (GstVdpVideoMemory * vmem, gsize maxsize, GstMapFlags flags)
160 {
161 GST_DEBUG ("surface:%d, maxsize:%" G_GSIZE_FORMAT ", flags:%d",
162 vmem->surface, maxsize, flags);
163
164 if (!ensure_data (vmem))
165 return NULL;
166
167 return vmem->cache;
168 }
169
170 static void
_vdp_video_mem_unmap(GstVdpVideoMemory * vmem)171 _vdp_video_mem_unmap (GstVdpVideoMemory * vmem)
172 {
173 GST_DEBUG ("surface:%d", vmem->surface);
174
175 release_data (vmem);
176 }
177
178
179 static GstMemory *
_vdp_video_mem_copy(GstVdpVideoMemory * src,gssize offset,gssize size)180 _vdp_video_mem_copy (GstVdpVideoMemory * src, gssize offset, gssize size)
181 {
182 GST_FIXME ("Implement !");
183 return NULL;
184 }
185
186 static GstMemory *
_vdp_video_mem_share(GstVdpVideoMemory * mem,gssize offset,gssize size)187 _vdp_video_mem_share (GstVdpVideoMemory * mem, gssize offset, gssize size)
188 {
189 GST_FIXME ("Implement !");
190 return NULL;
191 }
192
193 static gboolean
_vdp_video_mem_is_span(GstVdpVideoMemory * mem1,GstVdpVideoMemory * mem2,gsize * offset)194 _vdp_video_mem_is_span (GstVdpVideoMemory * mem1, GstVdpVideoMemory * mem2,
195 gsize * offset)
196 {
197 return FALSE;
198 }
199
200 static GstMemory *
_vdp_video_mem_alloc(GstAllocator * allocator,gsize size,GstAllocationParams * params)201 _vdp_video_mem_alloc (GstAllocator * allocator, gsize size,
202 GstAllocationParams * params)
203 {
204 g_warning ("use gst_vdp_video_memory_alloc () to allocate from this "
205 "GstVdpVideoMemory allocator");
206
207 return NULL;
208 }
209
210 static void
_vdp_video_mem_free(GstAllocator * allocator,GstMemory * mem)211 _vdp_video_mem_free (GstAllocator * allocator, GstMemory * mem)
212 {
213 GstVdpVideoMemory *vmem = (GstVdpVideoMemory *) mem;
214 VdpStatus status;
215
216 GST_DEBUG ("Destroying surface %d", vmem->surface);
217
218 status = vmem->device->vdp_video_surface_destroy (vmem->surface);
219 if (status != VDP_STATUS_OK)
220 GST_ERROR ("Couldn't destroy the VdpVideoSurface: %s",
221 vmem->device->vdp_get_error_string (status));
222
223 gst_object_unref (vmem->device);
224
225 g_free (vmem->cache);
226
227 g_slice_free (GstVdpVideoMemory, vmem);
228 }
229
230 /**
231 * gst_vdp_video_memory_alloc:
232 * @device: a #GstVdpDevice
233 * @info: the #GstVideoInfo describing the format to use
234 *
235 * Returns: a GstMemory object with a VdpVideoSurface specified by @info
236 * from @device
237 */
238 GstMemory *
gst_vdp_video_memory_alloc(GstVdpDevice * device,GstVideoInfo * info)239 gst_vdp_video_memory_alloc (GstVdpDevice * device, GstVideoInfo * info)
240 {
241 return (GstMemory *) _vdp_video_mem_new (_vdp_video_allocator, NULL, device,
242 info);
243 }
244
245 G_DEFINE_TYPE (GstVdpVideoAllocator, gst_vdp_video_allocator,
246 GST_TYPE_ALLOCATOR);
247
248 static void
gst_vdp_video_allocator_class_init(GstVdpVideoAllocatorClass * klass)249 gst_vdp_video_allocator_class_init (GstVdpVideoAllocatorClass * klass)
250 {
251 GstAllocatorClass *allocator_class;
252
253 allocator_class = (GstAllocatorClass *) klass;
254
255 allocator_class->alloc = _vdp_video_mem_alloc;
256 allocator_class->free = _vdp_video_mem_free;
257 }
258
259 static void
gst_vdp_video_allocator_init(GstVdpVideoAllocator * allocator)260 gst_vdp_video_allocator_init (GstVdpVideoAllocator * allocator)
261 {
262 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
263
264 alloc->mem_type = GST_VDP_VIDEO_MEMORY_ALLOCATOR;
265 alloc->mem_map = (GstMemoryMapFunction) _vdp_video_mem_map;
266 alloc->mem_unmap = (GstMemoryUnmapFunction) _vdp_video_mem_unmap;
267 alloc->mem_copy = (GstMemoryCopyFunction) _vdp_video_mem_copy;
268 alloc->mem_share = (GstMemoryShareFunction) _vdp_video_mem_share;
269 alloc->mem_is_span = (GstMemoryIsSpanFunction) _vdp_video_mem_is_span;
270 }
271
272 /**
273 * gst_vdp_video_memory_init:
274 *
275 * Initializes the GL Memory allocator. It is safe to call this function
276 * multiple times. This must be called before any other GstVdpVideoMemory operation.
277 */
278 void
gst_vdp_video_memory_init(void)279 gst_vdp_video_memory_init (void)
280 {
281 static volatile gsize _init = 0;
282
283 if (g_once_init_enter (&_init)) {
284 _vdp_video_allocator =
285 g_object_new (gst_vdp_video_allocator_get_type (), NULL);
286 gst_object_ref_sink (_vdp_video_allocator);
287
288 gst_allocator_register (GST_VDP_VIDEO_MEMORY_ALLOCATOR,
289 gst_object_ref (_vdp_video_allocator));
290 GST_DEBUG_CATEGORY_INIT (gst_vdp_video_mem_debug, "vdpvideomem", 0,
291 "VDPAU VideoSurface Memory/Allocator");
292 GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
293 g_once_init_leave (&_init, 1);
294 }
295 }
296
297 gboolean
gst_vdp_video_memory_map(GstVideoMeta * meta,guint plane,GstMapInfo * info,gpointer * data,gint * stride,GstMapFlags flags)298 gst_vdp_video_memory_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
299 gpointer * data, gint * stride, GstMapFlags flags)
300 {
301 GstBuffer *buffer = meta->buffer;
302 GstVdpVideoMemory *vmem =
303 (GstVdpVideoMemory *) gst_buffer_get_memory (buffer, 0);
304
305 /* Only handle GstVdpVideoMemory */
306 g_return_val_if_fail (((GstMemory *) vmem)->allocator == _vdp_video_allocator,
307 FALSE);
308
309 GST_DEBUG ("plane:%d", plane);
310
311 /* download if not already done */
312 if (!ensure_data (vmem))
313 return FALSE;
314
315 *data = vmem->cached_data[plane];
316 *stride = vmem->destination_pitches[plane];
317
318 return TRUE;
319 }
320
321 gboolean
gst_vdp_video_memory_unmap(GstVideoMeta * meta,guint plane,GstMapInfo * info)322 gst_vdp_video_memory_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
323 {
324 GstVdpVideoMemory *vmem =
325 (GstVdpVideoMemory *) gst_buffer_get_memory (meta->buffer, 0);
326
327 GST_DEBUG ("plane:%d", plane);
328
329 GST_FIXME ("implement unmap (and potential upload on last unmap)");
330
331 release_data (vmem);
332
333 return TRUE;
334 }
335