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