cb2850110859d96023376ac7d00c0ea00e98f11a
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / gst-libs / gst / allocators / gsttizenmemory.c
1 /*
2  * GStreamer tizen memory
3  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
4  * Author: Sejun Park <sejun79.park@samsung.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
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  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <unistd.h>
26 #include "string.h"
27 #include "gstdmabuf.h"
28 #include "gsttizenmemory.h"
29 #include <tbm_surface_internal.h>
30
31 #define GST_TIZEN_ALLOCATOR_NAME "TizenVideoMemory"
32
33 GST_DEBUG_CATEGORY_STATIC (gst_tizenmemory_debug);
34 #define GST_CAT_DEFAULT gst_tizenmemory_debug
35
36 #define parent_class gst_tizen_allocator_parent_class
37 G_DEFINE_TYPE_WITH_CODE (GstTizenAllocator, gst_tizen_allocator, GST_TYPE_ALLOCATOR,
38     G_ADD_PRIVATE (GstTizenAllocator);
39     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "tizenmemory", 0,
40         "GstTizenMemory and GstTizenAllocator"));
41
42 typedef struct
43 {
44   tbm_format format;
45   GstVideoFormat vformat;    /* Gst video format */
46   GstVideoFormat nformat;    /* Gst native video format */
47 } GstTizenBufferFormats;
48
49 static void cached_tizen_disposed_cb (GstTizenAllocator * allocator, GstMiniObject *obj);
50
51 GstTizenBufferFormats yuv_formats[] = {
52     {TBM_FORMAT_YUV420, GST_VIDEO_FORMAT_I420, GST_VIDEO_FORMAT_S420},
53     {TBM_FORMAT_NV21, GST_VIDEO_FORMAT_NV21, GST_VIDEO_FORMAT_SN21},
54     {TBM_FORMAT_NV12, GST_VIDEO_FORMAT_NV12, GST_VIDEO_FORMAT_SN12},
55     {TBM_FORMAT_YUYV, GST_VIDEO_FORMAT_YUY2, GST_VIDEO_FORMAT_SUYV},
56     {TBM_FORMAT_UYVY, GST_VIDEO_FORMAT_UYVY, GST_VIDEO_FORMAT_SYVY},
57     {TBM_FORMAT_ARGB8888, GST_VIDEO_FORMAT_ARGB, GST_VIDEO_FORMAT_SR32}
58 };
59
60 /**
61  * gst_video_format_to_tbm_format: (skip)
62  * @format: a #GstVideoFormat to be converted
63  *
64  * Return: a #tbm_format which is converted from @format.
65  */
66 tbm_format
67 gst_video_format_to_tbm_format (GstVideoFormat format)
68 {
69   gint i;
70
71   for (i = 0; i < G_N_ELEMENTS (yuv_formats); i++) {
72     if (yuv_formats[i].nformat == format)
73       return yuv_formats[i].format;
74   }
75   return -1;
76 }
77
78 static GstTizenMemory *
79 _tizen_video_mem_new (GstAllocator * allocator, GstMemory * parent, GstVideoInfo * vinfo,
80     tbm_surface_h surface, gpointer user_data, GDestroyNotify notify)
81 {
82   int i = 0;
83   int j = 0;
84   int num_bos = 0;
85   gint width, height;
86   GstTizenMemory *tmem;
87   tbm_surface_info_s sinfo;
88   tbm_format format;
89
90   if (!vinfo) {
91     GST_ERROR ("invalid vinfo");
92     return NULL;
93   }
94
95   tmem = g_slice_new0 (GstTizenMemory);
96
97   /* Creates the tbm_surface with buffer objects. */
98   if (surface) {
99     tbm_surface_internal_ref (surface);
100     tmem->surface = surface;
101   } else {
102     width = GST_VIDEO_INFO_WIDTH (vinfo);
103     height = GST_VIDEO_INFO_HEIGHT (vinfo);
104     format = gst_video_format_to_tbm_format (GST_VIDEO_INFO_FORMAT (vinfo));
105     tmem->surface = tbm_surface_internal_create_with_flags (width, height, format, TBM_BO_NONCACHABLE);
106   }
107
108   if (!tbm_surface_internal_is_valid (tmem->surface)) {
109     GST_ERROR ("Invalid tbm surface");
110     g_slice_free (GstTizenMemory, tmem);
111     return NULL;
112   }
113
114   tbm_surface_get_info (tmem->surface, &sinfo);
115
116   for (i = 0; i < sinfo.num_planes; i++) {
117     GST_VIDEO_INFO_PLANE_STRIDE (vinfo, i) = sinfo.planes[i].stride;
118     GST_VIDEO_INFO_PLANE_OFFSET (vinfo, i) = sinfo.planes[i].offset;
119     GST_DEBUG ("tbm surface plane[%d][%p]", i, sinfo.planes[i].ptr);
120   }
121
122   GST_VIDEO_INFO_SIZE (vinfo) = sinfo.size;
123
124   num_bos = tbm_surface_internal_get_num_bos (tmem->surface);
125
126   for (i = 0 ; i < num_bos ; i++) {
127     tmem->fd[i] = tbm_bo_export_fd (tbm_surface_internal_get_bo (tmem->surface, i));
128     if (tmem->fd[i] < 0) {
129       GST_ERROR ("fd export failed for bo[%d] %p",
130         i, tbm_surface_internal_get_bo (tmem->surface, i));
131
132       for (j = i - 1 ; j >= 0 ; j--) {
133         GST_WARNING ("close exported fd[%d] %d", j, tmem->fd[j]);
134         close (tmem->fd[j]);
135       }
136
137       tbm_surface_internal_unref (tmem->surface);
138       g_slice_free (GstTizenMemory, tmem);
139       return NULL;
140     }
141
142     GST_DEBUG ("exported fd[%d] %d", i, tmem->fd[i]);
143   }
144
145   gst_memory_init (GST_MEMORY_CAST (tmem), 0,
146     allocator, parent, GST_VIDEO_INFO_SIZE (vinfo), 0, 0,
147     GST_VIDEO_INFO_SIZE (vinfo));
148
149   tmem->info = gst_video_info_copy (vinfo);
150   tmem->notify = notify;
151   tmem->user_data = user_data;
152   tmem->fd_count = num_bos;
153   tmem->is_fd_exported = TRUE;
154
155   g_mutex_init (&tmem->lock);
156
157   GST_DEBUG ("mem[%p], surface[%p], size[%" G_GSIZE_FORMAT"]",
158     tmem, tmem->surface, tmem->mem.maxsize);
159
160   return tmem;
161 }
162
163 static GstTizenMemory *
164 _tizen_video_mem_new2 (GstAllocator * allocator, GstMemory * parent, GstVideoInfo * vinfo,
165     tbm_bo bo, gsize size, gpointer user_data, GDestroyNotify notify)
166 {
167   int bo_size = tbm_bo_size (bo);
168   int fd = 0;
169   GstTizenMemory *tmem;
170
171   if (!vinfo) {
172     GST_ERROR ("invalid vinfo");
173     return NULL;
174   }
175
176   if (size > bo_size) {
177     GST_ERROR ("size[%"G_GSIZE_FORMAT"] can not exceed bo size[%d]",
178       size, bo_size);
179     return NULL;
180   }
181
182   bo = tbm_bo_ref (bo);
183   if (!bo) {
184     GST_ERROR ("invalid bo");
185     return NULL;
186   }
187
188   fd = tbm_bo_export_fd (bo);
189   if (fd < 0) {
190     GST_ERROR ("export fd failed from bo[%p]", bo);
191     tbm_bo_unref (bo);
192     return NULL;
193   }
194
195   tmem = g_slice_new0 (GstTizenMemory);
196
197   gst_memory_init (GST_MEMORY_CAST (tmem), 0,
198     allocator, parent, bo_size, 0, 0, size);
199
200   tmem->bo = bo;
201   tmem->info = gst_video_info_copy (vinfo);
202   tmem->notify = notify;
203   tmem->user_data = user_data;
204   tmem->fd[0] = fd;
205   tmem->fd_count = 1;
206   tmem->is_fd_exported = TRUE;
207   GST_VIDEO_INFO_SIZE (tmem->info) = size;
208
209   g_mutex_init (&tmem->lock);
210
211   GST_DEBUG ("mem[%p], bo[%p], size[%" G_GSIZE_FORMAT"], max[%" G_GSIZE_FORMAT"]",
212     tmem, tmem->bo, tmem->mem.size, tmem->mem.maxsize);
213
214   return tmem;
215 }
216
217 static GstTizenMemory *
218 _tizen_video_mem_new3 (GstAllocator * allocator, GstMemory * parent, GstVideoInfo * vinfo,
219     tbm_surface_info_s * tsinfo, int fd[GST_TIZEN_MEMORY_MAX_FD], int fd_count,
220     gpointer user_data, GDestroyNotify notify)
221 {
222   int i = 0;
223   tbm_bo bos[GST_TIZEN_MEMORY_MAX_FD] = {NULL, };
224   tbm_surface_h surface = NULL;
225   GstTizenMemory *tmem;
226   GstTizenAllocator *tallocator = GST_TIZEN_ALLOCATOR (allocator);
227
228   if (!vinfo || !tsinfo || fd_count < 1 || fd_count > GST_TIZEN_MEMORY_MAX_FD) {
229     GST_ERROR ("invalid param[vinfo:%p,tsinfo:%p, fd_count:%d]", vinfo, tsinfo, fd_count);
230     return NULL;
231   }
232
233   if (!tallocator->bufmgr) {
234     GST_ERROR ("No TBM bufmgr");
235     return NULL;
236   }
237
238   for (i = 0 ; i < fd_count ; i++) {
239     bos[i] = tbm_bo_import_fd (tallocator->bufmgr, fd[i]);
240     if (!bos[i]) {
241       GST_ERROR ("failed to import fd[%d]", fd[i]);
242       goto _TIZEN_VIDEO_MEM_NEW3_FAILED;
243     }
244   }
245
246   surface = tbm_surface_internal_create_with_bos (tsinfo, bos, fd_count);
247   if (!surface) {
248     GST_ERROR ("failed to create surface");
249     goto _TIZEN_VIDEO_MEM_NEW3_FAILED;
250   }
251
252   tmem = g_slice_new0 (GstTizenMemory);
253
254   gst_memory_init (GST_MEMORY_CAST (tmem), 0,
255     allocator, parent, tsinfo->size, 0, 0, tsinfo->size);
256
257   /* bos[] will be kept in tbm surface and released when surface is released finally. */
258   for (i = 0 ; i < fd_count ; i++) {
259     tbm_bo_unref (bos[i]);
260     bos[i] = NULL;
261     tmem->fd[i] = fd[i];
262   }
263
264   tmem->surface = surface;
265   tmem->fd_count = fd_count;
266   tmem->info = gst_video_info_copy (vinfo);
267   tmem->notify = notify;
268   tmem->user_data = user_data;
269   tmem->is_fd_exported = FALSE;
270
271   GST_VIDEO_INFO_SIZE (tmem->info) = tsinfo->size;
272
273   g_mutex_init (&tmem->lock);
274
275   GST_DEBUG ("mem[%p], fd[0][%d], fd_count[%d], size[%" G_GSIZE_FORMAT"], max[%" G_GSIZE_FORMAT"]",
276     tmem, tmem->fd[0], tmem->fd_count, tmem->mem.size, tmem->mem.maxsize);
277
278   return tmem;
279
280 _TIZEN_VIDEO_MEM_NEW3_FAILED:
281   for (i = 0 ; i < fd_count && bos[i] ; i++)
282     tbm_bo_unref (bos[i]);
283
284   return NULL;
285 }
286
287 static void
288 gst_tizen_mem_free (GstAllocator * allocator, GstMemory * mem)
289 {
290   int i = 0;
291   GstTizenMemory *tmem = (GstTizenMemory *) mem;
292
293   if (tmem->is_fd_exported) {
294     for (i = 0 ; i < tmem->fd_count && tmem->fd[i] >= 0 ; i++) {
295       GST_DEBUG ("close exported fd[%d] %d", i, tmem->fd[i]);
296       close (tmem->fd[i]);
297       tmem->fd[i] = -1;
298     }
299   }
300
301   if (tmem->bo) {
302     GST_DEBUG ("unref bo[%p] from mem[%p]", tmem->bo, tmem);
303     tbm_bo_unref (tmem->bo);
304     tmem->bo = NULL;
305   }
306
307   if (tmem->surface) {
308     GST_DEBUG ("unref surface[%p] from mem[%p]", tmem->surface, tmem);
309     tbm_surface_internal_unref (tmem->surface);
310     tmem->surface = NULL;
311   }
312
313   if (tmem->notify)
314     tmem->notify (tmem->user_data);
315
316   gst_video_info_free (tmem->info);
317   g_mutex_clear (&tmem->lock);
318   g_slice_free (GstTizenMemory, tmem);
319 }
320
321 static gpointer
322 gst_tizen_mem_map (GstMemory * gmem, gsize maxsize, GstMapFlags flags)
323 {
324   int tbm_ret;
325   int tbm_opt = 0;
326   gpointer data = NULL;
327   GstTizenMemory *tmem;
328   tbm_surface_info_s info = {0, };
329   tbm_bo_handle bo_handle = {NULL, };
330
331   tmem = (GstTizenMemory *)gmem;
332
333   g_mutex_lock (&tmem->lock);
334
335   if (tmem->surface) {
336     GST_DEBUG ("map surface[%p] in mem[%p]", tmem->surface, tmem);
337
338     if (flags & GST_MAP_READ)
339       tbm_opt |= TBM_SURF_OPTION_READ;
340     if (flags & GST_MAP_WRITE)
341       tbm_opt |= TBM_SURF_OPTION_WRITE;
342
343     tbm_ret = tbm_surface_map (tmem->surface, tbm_opt, &info);
344     if (tbm_ret != TBM_SURFACE_ERROR_NONE) {
345       GST_ERROR ("tbm_surface_map failed[0x%x]", tbm_ret);
346       goto done;
347     }
348
349     data = (gpointer) info.planes[0].ptr;
350   } else {
351     GST_DEBUG ("map bo[%p] in mem[%p]", tmem->bo, tmem);
352
353     if (flags & GST_MAP_READ)
354       tbm_opt |= TBM_OPTION_READ;
355     if (flags & GST_MAP_WRITE)
356       tbm_opt |= TBM_OPTION_WRITE;
357
358     bo_handle = tbm_bo_map (tmem->bo, TBM_DEVICE_CPU, tbm_opt);
359     if (!bo_handle.ptr) {
360       GST_ERROR ("tbm_bo_map failed for bo[%p]", tmem->bo);
361       goto done;
362     }
363
364     data = (gpointer) bo_handle.ptr;
365   }
366
367 done:
368   g_mutex_unlock (&tmem->lock);
369
370   return data;
371 }
372
373 static void
374 gst_tizen_mem_unmap (GstMemory * gmem)
375 {
376   GstTizenMemory *tmem;
377
378   tmem = (GstTizenMemory *)gmem;
379
380   g_mutex_lock (&tmem->lock);
381
382   if (tmem->surface) {
383     GST_DEBUG ("unmap surface[%p] in mem[%p]", tmem->surface, tmem);
384     tbm_surface_unmap (tmem->surface);
385   } else {
386     GST_DEBUG ("unmap bo[%p] in mem[%p]", tmem->bo, tmem);
387     tbm_bo_unmap (tmem->bo);
388   }
389
390   g_mutex_unlock (&tmem->lock);
391 }
392
393 static void
394 gst_tizen_allocator_finalize (GObject *obj)
395 {
396   GstTizenAllocator *allocator;
397   GList *iter;
398
399   allocator = GST_TIZEN_ALLOCATOR (obj);
400
401   iter = allocator->priv->mem_cache;
402
403   while (iter) {
404     GstMiniObject *obj = iter->data;
405         gst_mini_object_weak_unref (obj,
406         (GstMiniObjectNotify) cached_tizen_disposed_cb, allocator);
407
408     gst_mini_object_set_qdata (obj,
409     g_quark_from_static_string ("tizenmem"), NULL, NULL);
410     iter = iter->next;
411   }
412
413   g_list_free (allocator->priv->mem_cache);
414   allocator->priv->mem_cache = NULL;
415
416   tbm_bufmgr_deinit (allocator->bufmgr);
417   allocator->bufmgr = NULL;
418
419   G_OBJECT_CLASS (parent_class)->finalize (obj);
420 }
421
422 static GstMemory *
423 gst_tizen_mem_share (GstMemory * gmem, gssize offset, gssize size)
424 {
425   GST_WARNING ("not supported");
426   return NULL;
427 }
428
429 static GstMemory *
430 gst_tizen_mem_copy (GstMemory * gmem, gssize offset, gsize size)
431 {
432   gint i = 0;
433   GstMemory *copy = NULL;
434   GstTizenMemory *tmem = (GstTizenMemory *) gmem;
435
436   GST_DEBUG ("copy mem[%p], offset[%"G_GSSIZE_FORMAT"], size[%"G_GSIZE_FORMAT"]",
437     tmem, offset, size);
438
439   if (tmem->surface) {
440     tbm_surface_h new_surface, old_surface;
441     tbm_format format;
442     tbm_surface_info_s old_surface_info;
443     tbm_surface_info_s new_surface_info;
444
445     old_surface = tmem->surface;
446     format = tbm_surface_get_format (old_surface);
447
448     copy = gst_tizen_allocator_alloc (gmem->allocator, tmem->info);
449     new_surface = gst_tizen_memory_get_surface (copy);
450
451     tbm_surface_get_info (old_surface, &old_surface_info);
452     tbm_surface_get_info (new_surface, &new_surface_info);
453
454     for (i = 0; i < tbm_surface_internal_get_num_planes (format); i++) {
455       memcpy (new_surface_info.planes[i].ptr,
456           old_surface_info.planes[i].ptr, new_surface_info.planes[i].size);
457     }
458   } else {
459     int old_size = tbm_bo_size (tmem->bo);
460     tbm_bo new_bo = NULL;
461     tbm_bo_handle old_handle = {NULL, };
462     tbm_bo_handle new_handle = {NULL, };
463     tbm_bufmgr bufmgr = NULL;
464
465     if (size > old_size - offset) {
466       GST_ERROR ("invalid size[%"G_GSIZE_FORMAT"] (offset[%"G_GSSIZE_FORMAT"],bo size[%d])",
467         size, offset, old_size);
468       goto _BO_COPY_OUT;
469     }
470
471     bufmgr = tbm_bufmgr_init (-1);
472     if (!bufmgr) {
473       GST_ERROR ("tbm bufmgr failed");
474       goto _BO_COPY_OUT;
475     }
476
477     new_bo = tbm_bo_alloc (bufmgr, tbm_bo_size (tmem->bo), TBM_BO_DEFAULT);
478     if (!new_bo) {
479       GST_ERROR ("tbm_bo_alloc failed");
480       goto _BO_COPY_OUT;
481     }
482
483     old_handle = tbm_bo_map (tmem->bo, TBM_DEVICE_CPU, TBM_OPTION_READ);
484     new_handle = tbm_bo_map (new_bo, TBM_DEVICE_CPU, TBM_OPTION_WRITE);
485
486     if (!old_handle.ptr || !new_handle.ptr) {
487       GST_ERROR ("tbm bo map failed[old:%p,new:%p]",
488         old_handle.ptr, new_handle.ptr);
489       goto _BO_COPY_OUT;
490     }
491
492     memcpy (new_handle.ptr, (char *) old_handle.ptr + offset, size);
493
494     copy = gst_tizen_allocator_alloc_bo (gmem->allocator,
495       tmem->info, new_bo, size, NULL, NULL);
496
497 _BO_COPY_OUT:
498     if (old_handle.ptr)
499       tbm_bo_unmap (tmem->bo);
500
501     if (new_handle.ptr)
502       tbm_bo_unmap (new_bo);
503
504     if (new_bo)
505       tbm_bo_unref (new_bo);
506
507     if (bufmgr)
508       tbm_bufmgr_deinit (bufmgr);
509   }
510
511   return copy;
512 }
513
514 static void
515 gst_tizen_allocator_class_init (GstTizenAllocatorClass * klass)
516 {
517   GObjectClass *object_class;
518   GstAllocatorClass *allocator_class;
519
520   allocator_class = GST_ALLOCATOR_CLASS (klass);
521   object_class = G_OBJECT_CLASS (klass);
522
523   allocator_class->alloc = NULL;
524   allocator_class->free = gst_tizen_mem_free;
525
526   object_class->finalize = gst_tizen_allocator_finalize;
527 }
528
529 static void
530 gst_tizen_allocator_init (GstTizenAllocator * allocator)
531 {
532   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
533
534   allocator->priv = gst_tizen_allocator_get_instance_private (allocator);
535   alloc->mem_type = GST_TIZEN_MEMORY_TYPE;
536   alloc->mem_map = (GstMemoryMapFunction) gst_tizen_mem_map;
537   alloc->mem_unmap = (GstMemoryUnmapFunction) gst_tizen_mem_unmap;
538   alloc->mem_share = (GstMemoryShareFunction) gst_tizen_mem_share;
539   alloc->mem_copy = (GstMemoryCopyFunction) gst_tizen_mem_copy;
540
541   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
542
543   allocator->bufmgr = tbm_bufmgr_init (-1);
544   if (!allocator->bufmgr)
545     GST_ERROR ("TBM bufmgr failed");
546 }
547
548 /**
549  * gst_tizen_allocator_new:
550  *
551  * Return a new tizen allocator.
552  *
553  * Returns: (transfer full): a new fd allocator, or NULL if the allocator
554  *    isn't available. Use gst_object_unref() to release the allocator after
555  *    usage
556  *
557  */
558 GstAllocator *
559 gst_tizen_allocator_new (void)
560 {
561   return g_object_new (GST_TYPE_TIZEN_ALLOCATOR, NULL);
562 }
563
564 /**
565  * gst_tizen_allocator_alloc:
566  * @allocator: a #GstAllocator to use
567  * @vinfo: a #GstVideoInfo to be referred for size and type of allocated memory
568  *
569  * Returns: (transfer full) (nullable): a new #GstMemory.
570  */
571 GstMemory *
572 gst_tizen_allocator_alloc (GstAllocator * allocator, GstVideoInfo * vinfo)
573 {
574   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (allocator), NULL);
575
576   return (GstMemory *)_tizen_video_mem_new (allocator, NULL, vinfo, NULL, NULL, NULL);
577 }
578
579 /**
580  * gst_tizen_allocator_alloc_surface: (skip)
581  * @allocator: a #GstAllocator to use
582  * @vinfo: a #GstVideoInfo to be referred for memory size and type
583  * @surface: a #tbm_surface_h to be used for allocated memory
584  * @user_data: (allow-none): user data pointer
585  * @notify: (allow-none) (closure user_data): called with @user_data when the memory is freed
586  *
587  * Returns: (transfer full) (nullable): a new #GstMemory.
588  */
589 GstMemory *
590 gst_tizen_allocator_alloc_surface (GstAllocator * allocator, GstVideoInfo * vinfo,
591     tbm_surface_h surface, gpointer user_data, GDestroyNotify notify)
592 {
593   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (allocator), NULL);
594
595   return (GstMemory *)_tizen_video_mem_new (allocator, NULL, vinfo, surface, user_data, notify);
596 }
597
598 /**
599  * gst_tizen_allocator_alloc_bo: (skip)
600  * @allocator: a #GstAllocator to use
601  * @vinfo: a #GstVideoInfo to be referred for memory size and type
602  * @bo: a #tbm_bo to be used for allocated memory
603  * @size: size of used memory in @bo, it should not be bigger than size of bo
604  * @user_data: (allow-none): user data pointer
605  * @notify: (allow-none) (closure user_data): called with @user_data when the memory is freed
606  *
607  * Returns: (transfer full) (nullable): a new #GstMemory.
608  */
609 GstMemory *
610 gst_tizen_allocator_alloc_bo (GstAllocator * allocator, GstVideoInfo * vinfo,
611     tbm_bo bo, gsize size, gpointer user_data, GDestroyNotify notify)
612 {
613   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (allocator), NULL);
614
615   return (GstMemory *)_tizen_video_mem_new2 (allocator, NULL, vinfo, bo, size, user_data, notify);
616 }
617
618 /**
619  * gst_tizen_allocator_alloc_fd: (skip)
620  * @allocator: a #GstAllocator to use
621  * @vinfo: a #GstVideoInfo to be referred for memory size and type
622  * @tsinfo: a #tbm_surface_info_s to be referred for information of TBM surface
623  * @fd: fd list to be used for allocated memory
624  * @fd_count: a count of fd in list
625  * @user_data: (allow-none): user data pointer
626  * @notify: (allow-none) (closure user_data): called with @user_data when the memory is freed
627  *
628  * Returns: (transfer full) (nullable): a new #GstMemory.
629  */
630 GstMemory *
631 gst_tizen_allocator_alloc_fd (GstAllocator * allocator, GstVideoInfo * vinfo,
632     tbm_surface_info_s * tsinfo, int fd[GST_TIZEN_MEMORY_MAX_FD], int fd_count,
633     gpointer user_data, GDestroyNotify notify)
634 {
635   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (allocator), NULL);
636
637   return (GstMemory *)_tizen_video_mem_new3 (allocator, NULL, vinfo, tsinfo, fd, fd_count, user_data, notify);
638 }
639
640 gboolean
641 gst_is_tizen_memory (GstMemory * mem)
642 {
643   g_return_val_if_fail (mem != NULL, FALSE);
644
645   return GST_IS_TIZEN_ALLOCATOR (mem->allocator);
646 }
647
648 gint
649 gst_tizen_memory_get_num_bos (GstMemory *mem)
650 {
651   GstTizenMemory *tmem;
652
653   g_return_val_if_fail (mem != NULL, -1);
654   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (mem->allocator), -1);
655
656   tmem = (GstTizenMemory *)mem;
657
658   return (gint) tbm_surface_internal_get_num_bos (tmem->surface);
659 }
660
661 void *
662 gst_tizen_memory_get_bos (GstMemory * mem, gint bo_idx)
663 {
664   gint bo_num;
665   GstTizenMemory *tmem;
666
667   g_return_val_if_fail (mem != NULL, NULL);
668   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (mem->allocator), NULL);
669
670   tmem = (GstTizenMemory *)mem;
671
672   if (tmem->surface) {
673     bo_num = tbm_surface_internal_get_num_bos(tmem->surface);
674     if (bo_idx >= bo_num) {
675       GST_ERROR ("invalid idx[%d] for surface[bo_num:%d]", bo_idx, bo_num);
676       return NULL;
677     }
678
679     return tbm_surface_internal_get_bo(tmem->surface, bo_idx);
680   }
681
682   if (bo_idx > 0) {
683     GST_ERROR ("invalid idx[%d] for bo", bo_idx);
684     return NULL;
685   }
686
687   return tmem->bo;
688 }
689
690 void *
691 gst_tizen_memory_get_surface (GstMemory * mem)
692 {
693   GstTizenMemory *tmem;
694
695   g_return_val_if_fail (mem != NULL, NULL);
696   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (mem->allocator), NULL);
697
698   tmem = (GstTizenMemory *)mem;
699
700   GST_DEBUG ("surface[%p] in mem[%p]", tmem->surface, tmem);
701
702   return tmem->surface;
703 }
704
705 gint
706 gst_tizen_memory_get_num_fd (GstMemory * mem)
707 {
708   GstTizenMemory *tmem;
709
710   g_return_val_if_fail (mem != NULL, -1);
711   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (mem->allocator), -1);
712
713   tmem = (GstTizenMemory *)mem;
714
715   GST_DEBUG ("fd count[%d] in mem[%p]", tmem->fd_count, tmem);
716
717   return tmem->fd_count;
718 }
719
720 gint
721 gst_tizen_memory_get_fd (GstMemory * mem, gint index)
722 {
723   GstTizenMemory *tmem = (GstTizenMemory *)mem;
724
725   g_return_val_if_fail (mem != NULL, -1);
726   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (mem->allocator), -1);
727   g_return_val_if_fail (index >= 0 && index < tmem->fd_count, -1);
728
729   GST_DEBUG ("fd[%d] %d in mem[%p]", index, tmem->fd[index], tmem);
730
731   return tmem->fd[index];
732 }
733
734 static void
735 cached_tizen_disposed_cb (GstTizenAllocator * allocator, GstMiniObject *obj)
736 {
737   allocator->priv->mem_cache = g_list_remove (allocator->priv->mem_cache, obj);
738 }
739
740 GstMemory *
741 gst_tizen_allocator_dmabuf_export (GstAllocator * allocator, GstMemory * _tmem, int bo_idx)
742 {
743   GstTizenMemory *tmem = (GstTizenMemory *) _tmem;
744   GstTizenAllocator *tallocator = GST_TIZEN_ALLOCATOR (allocator);
745   GstMemory * mem;
746   gint fd;
747   tbm_bo bo;
748
749   g_return_val_if_fail (tmem->surface != NULL, NULL);
750
751   bo = tbm_surface_internal_get_bo (tmem->surface, bo_idx);
752   fd = tbm_bo_export_fd (bo);
753
754   tallocator->priv->dmabuf_alloc = gst_dmabuf_allocator_new ();
755
756   mem = gst_dmabuf_allocator_alloc (tallocator->priv->dmabuf_alloc, fd,
757       gst_memory_get_sizes (_tmem, NULL, NULL));
758
759   /* cache */
760   gst_mini_object_weak_ref (GST_MINI_OBJECT (mem),
761       (GstMiniObjectNotify) cached_tizen_disposed_cb, tallocator);
762
763   tallocator->priv->mem_cache = g_list_prepend (tallocator->priv->mem_cache, mem);
764
765   gst_mini_object_set_qdata (GST_MINI_OBJECT (mem),
766       g_quark_from_static_string ("_tmem"), _tmem,
767       (GDestroyNotify) gst_memory_unref);
768   return mem;
769 }
770
771 /**
772  * gst_tizen_allocator_dmabuf_import: (skip)
773  * @allocator: a #GstAllocator to use
774  * @fds: fd array to be imported to #GstTizenMemory
775  * @planes: a number of planes
776  * @offsets: offset array
777  * @vinfo: a #GstVideoInfo to be refered
778  *
779  * Returns: (transfer full) (nullable): a new #GstTizenMemory.
780  */
781 GstTizenMemory *
782 gst_tizen_allocator_dmabuf_import (GstAllocator * allocator, gint * fds, gint planes, gsize offsets[4], GstVideoInfo * vinfo)
783 {
784   GstTizenMemory *tmem;
785   GstMemory *mem;
786
787   tmem = g_slice_new0 (GstTizenMemory);
788
789   mem = GST_MEMORY_CAST (tmem);
790
791   gst_memory_init (mem, 0, allocator, NULL,
792       GST_VIDEO_INFO_SIZE (vinfo), 0, 0, GST_VIDEO_INFO_SIZE (vinfo));
793
794   return NULL;
795 }
796
797 /**
798  * gst_tizen_video_meta_map:
799  * @meta: a #GstVideoMeta
800  * @plane: a plane
801  * @info: a #GstMapInfo
802  * @data: (out): the data of @plane
803  * @stride: (out): the stride of @plane
804  * @flags: @GstMapFlags
805  *
806  * Returns: TRUE if the map operation was successful.
807  */
808 gboolean
809 gst_tizen_video_meta_map (GstVideoMeta * meta, guint plane, GstMapInfo * info,
810     gpointer * data, gint * stride, GstMapFlags flags)
811 {
812   int tbm_ret = TBM_SURFACE_ERROR_NONE;
813   int tbm_opt = 0;
814   gboolean mapped = FALSE;
815   GstBuffer *buffer = meta->buffer;
816   GstTizenMemory *tmem = (GstTizenMemory *) gst_buffer_get_memory (buffer, 0);
817
818   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (((GstMemory *) tmem)->allocator), FALSE);
819
820   if (!tmem->surface || plane >= GST_VIDEO_MAX_PLANES) {
821     GST_ERROR ("invalid param[surface:%p,plane:%u]", tmem->surface, plane);
822     return FALSE;
823   }
824
825   g_mutex_lock (&tmem->lock);
826
827   if (tmem->video_memory_map[plane]) {
828     GST_ERROR ("buf[%p] plane[%d] is already mapped", buffer, plane);
829     goto _VIDEO_MEMORY_MAP_DONE;
830   }
831
832   if (tmem->video_memory_map_count == 0) {
833     if (flags & GST_MAP_READ)
834       tbm_opt |= TBM_SURF_OPTION_READ;
835     if (flags & GST_MAP_WRITE)
836       tbm_opt |= TBM_SURF_OPTION_WRITE;
837
838     tbm_ret = tbm_surface_map (tmem->surface, tbm_opt, &tmem->surface_info);
839     if (tbm_ret != TBM_SURFACE_ERROR_NONE) {
840       GST_ERROR ("buf[%p] tbm_surface_map[%p] failed[0x%x]",
841         buffer, tmem->surface, tbm_ret);
842       goto _VIDEO_MEMORY_MAP_DONE;
843     }
844   }
845
846   if (plane >= tmem->surface_info.num_planes) {
847     GST_ERROR ("buf[%p] invalid plane index[%d] (num plane[%d])",
848       buffer, plane, tmem->surface_info.num_planes);
849
850     if (tmem->video_memory_map_count == 0) {
851       GST_ERROR ("buf[%p] unmap surface[%p]", buffer, tmem->surface);
852       tbm_surface_unmap (tmem->surface);
853     }
854
855     goto _VIDEO_MEMORY_MAP_DONE;
856   }
857
858   *data = tmem->surface_info.planes[plane].ptr;
859   *stride = tmem->surface_info.planes[plane].stride;
860
861   tmem->video_memory_map[plane] = TRUE;
862   tmem->video_memory_map_count++;
863
864   /* set map flags */
865   info->flags = flags;
866
867   GST_DEBUG ("buf[%p] plane[%d], data[%p], stride[%d], flags[0x%x]",
868     buffer, plane, *data, *stride, info->flags);
869
870   mapped = TRUE;
871
872 _VIDEO_MEMORY_MAP_DONE:
873   g_mutex_unlock (&tmem->lock);
874
875   return mapped;
876 }
877
878 /**
879  * gst_tizen_video_meta_unmap:
880  * @meta: a #GstVideoMeta
881  * @plane: a plane
882  * @info: a #GstMapInfo
883  *
884  * Returns: TRUE if the memory was successfully unmapped.
885  */
886 gboolean
887 gst_tizen_video_meta_unmap (GstVideoMeta * meta, guint plane, GstMapInfo * info)
888 {
889   int tbm_ret = TBM_SURFACE_ERROR_NONE;
890   gboolean unmapped = FALSE;
891   GstBuffer *buffer = meta->buffer;
892   GstTizenMemory *tmem = (GstTizenMemory *) gst_buffer_get_memory (buffer, 0);
893
894   g_return_val_if_fail (GST_IS_TIZEN_ALLOCATOR (((GstMemory *) tmem)->allocator), FALSE);
895
896   if (!tmem->surface) {
897     GST_ERROR ("no surface");
898     return FALSE;
899   }
900
901   g_mutex_lock (&tmem->lock);
902
903   if (tmem->video_memory_map[plane] == FALSE) {
904     GST_ERROR ("buf[%p] plane[%d] is already unmapped",
905       buffer, plane);
906     goto _VIDEO_MEMORY_UNMAP_DONE;
907   }
908
909   if (tmem->video_memory_map_count > 1) {
910     GST_DEBUG ("buf[%p] plane[%d] skip unmap surface[%p]",
911       buffer, plane, tmem->surface);
912     unmapped = TRUE;
913     goto _VIDEO_MEMORY_UNMAP_DONE;
914   }
915
916   tbm_ret = tbm_surface_unmap (tmem->surface);
917   if (tbm_ret != TBM_SURFACE_ERROR_NONE) {
918     GST_ERROR ("buf[%p] tbm_surface_unmap[%p] failed[0x%x]",
919       buffer, tmem->surface, tbm_ret);
920     goto _VIDEO_MEMORY_UNMAP_DONE;
921   }
922
923   unmapped = TRUE;
924
925   GST_DEBUG ("buf[%p] plane[%d] unmap surface[%p] done",
926     buffer, plane, tmem->surface);
927
928 _VIDEO_MEMORY_UNMAP_DONE:
929   if (unmapped == TRUE) {
930     tmem->video_memory_map[plane] = FALSE;
931     tmem->video_memory_map_count--;
932   }
933
934   g_mutex_unlock (&tmem->lock);
935
936   return unmapped;
937 }