26422fa888aa857cf74452445d99d0f2dd091394
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / nvcodec / gstcudautils.c
1 /* GStreamer
2  * Copyright (C) <2018-2019> Seungha Yang <seungha.yang@navercorp.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gstcudautils.h"
25 #include "gstcudacontext.h"
26
27 #ifdef HAVE_NVCODEC_GST_GL
28 #include <gst/gl/gl.h>
29 #include <gst/gl/gstglfuncs.h>
30 #endif
31
32 #ifdef HAVE_NVCODEC_GST_D3D11
33 #include <gst/d3d11/gstd3d11.h>
34 #endif
35
36 #ifdef HAVE_NVCODEC_NVMM
37 #include "gstcudanvmm.h"
38 #endif
39
40 #include "gstcudamemory.h"
41
42 GST_DEBUG_CATEGORY_STATIC (gst_cuda_utils_debug);
43 #define GST_CAT_DEFAULT gst_cuda_utils_debug
44 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
45
46 static void
47 _init_debug (void)
48 {
49   static gsize once_init = 0;
50
51   if (g_once_init_enter (&once_init)) {
52
53     GST_DEBUG_CATEGORY_INIT (gst_cuda_utils_debug, "cudautils", 0,
54         "CUDA utils");
55     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
56     g_once_init_leave (&once_init, 1);
57   }
58 }
59
60 static gboolean
61 pad_query (const GValue * item, GValue * value, gpointer user_data)
62 {
63   GstPad *pad = g_value_get_object (item);
64   GstQuery *query = user_data;
65   gboolean res;
66
67   res = gst_pad_peer_query (pad, query);
68
69   if (res) {
70     g_value_set_boolean (value, TRUE);
71     return FALSE;
72   }
73
74   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
75   return TRUE;
76 }
77
78 static gboolean
79 run_query (GstElement * element, GstQuery * query, GstPadDirection direction)
80 {
81   GstIterator *it;
82   GstIteratorFoldFunction func = pad_query;
83   GValue res = { 0 };
84
85   g_value_init (&res, G_TYPE_BOOLEAN);
86   g_value_set_boolean (&res, FALSE);
87
88   /* Ask neighbor */
89   if (direction == GST_PAD_SRC)
90     it = gst_element_iterate_src_pads (element);
91   else
92     it = gst_element_iterate_sink_pads (element);
93
94   while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
95     gst_iterator_resync (it);
96
97   gst_iterator_free (it);
98
99   return g_value_get_boolean (&res);
100 }
101
102 static void
103 find_cuda_context (GstElement * element, GstCudaContext ** cuda_ctx)
104 {
105   GstQuery *query;
106   GstContext *ctxt;
107
108   /*  1) Query downstream with GST_QUERY_CONTEXT for the context and
109    *      check if upstream already has a context of the specific type
110    *  2) Query upstream as above.
111    */
112   query = gst_query_new_context (GST_CUDA_CONTEXT_TYPE);
113   if (run_query (element, query, GST_PAD_SRC)) {
114     gst_query_parse_context (query, &ctxt);
115     if (ctxt) {
116       GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
117           "found context (%p) in downstream query", ctxt);
118       gst_element_set_context (element, ctxt);
119     }
120   }
121
122   /* although we found cuda context above, the element does not want
123    * to use the context. Then try to find from the other direction */
124   if (*cuda_ctx == NULL && run_query (element, query, GST_PAD_SINK)) {
125     gst_query_parse_context (query, &ctxt);
126     if (ctxt) {
127       GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
128           "found context (%p) in upstream query", ctxt);
129       gst_element_set_context (element, ctxt);
130     }
131   }
132
133   if (*cuda_ctx == NULL) {
134     /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
135      *    the required context type and afterwards check if a
136      *    usable context was set now. The message could
137      *    be handled by the parent bins of the element and the
138      *    application.
139      */
140     GstMessage *msg;
141
142     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
143         "posting need context message");
144     msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
145         GST_CUDA_CONTEXT_TYPE);
146     gst_element_post_message (element, msg);
147   }
148
149   /*
150    * Whomever responds to the need-context message performs a
151    * GstElement::set_context() with the required context in which the element
152    * is required to update the cuda_ctx or call gst_cuda_handle_set_context().
153    */
154
155   gst_query_unref (query);
156 }
157
158 static void
159 context_set_cuda_context (GstContext * context, GstCudaContext * cuda_ctx)
160 {
161   GstStructure *s;
162   guint device_id;
163
164   g_return_if_fail (context != NULL);
165
166   g_object_get (G_OBJECT (cuda_ctx), "cuda-device-id", &device_id, NULL);
167
168   GST_CAT_LOG (GST_CAT_CONTEXT,
169       "setting GstCudaContext(%" GST_PTR_FORMAT
170       ") with cuda-device-id %d on context(%" GST_PTR_FORMAT ")",
171       cuda_ctx, device_id, context);
172
173   s = gst_context_writable_structure (context);
174   gst_structure_set (s, GST_CUDA_CONTEXT_TYPE, GST_TYPE_CUDA_CONTEXT,
175       cuda_ctx, "cuda-device-id", G_TYPE_INT, device_id, NULL);
176 }
177
178 /**
179  * gst_cuda_ensure_element_context:
180  * @element: the #GstElement running the query
181  * @device_id: preferred device-id, pass device_id >=0 when
182  *             the device_id explicitly required. Otherwise, set -1.
183  * @cuda_ctx: (inout): the resulting #GstCudaContext
184  *
185  * Perform the steps necessary for retrieving a #GstCudaContext from the
186  * surrounding elements or from the application using the #GstContext mechanism.
187  *
188  * If the content of @cuda_ctx is not %NULL, then no #GstContext query is
189  * necessary for #GstCudaContext.
190  *
191  * Returns: whether a #GstCudaContext exists in @cuda_ctx
192  */
193 gboolean
194 gst_cuda_ensure_element_context (GstElement * element, gint device_id,
195     GstCudaContext ** cuda_ctx)
196 {
197   guint target_device_id = 0;
198
199   g_return_val_if_fail (element != NULL, FALSE);
200   g_return_val_if_fail (cuda_ctx != NULL, FALSE);
201
202   _init_debug ();
203
204   if (*cuda_ctx)
205     return TRUE;
206
207   find_cuda_context (element, cuda_ctx);
208   if (*cuda_ctx)
209     return TRUE;
210
211   if (device_id > 0)
212     target_device_id = device_id;
213
214   /* No available CUDA context in pipeline, create new one here */
215   *cuda_ctx = gst_cuda_context_new (target_device_id);
216
217   if (*cuda_ctx == NULL) {
218     GST_CAT_ERROR_OBJECT (GST_CAT_CONTEXT, element,
219         "Failed to create CUDA context with device-id %d", device_id);
220     return FALSE;
221   } else {
222     GstContext *context;
223     GstMessage *msg;
224
225     /* Propagate new CUDA context */
226
227     context = gst_context_new (GST_CUDA_CONTEXT_TYPE, TRUE);
228     context_set_cuda_context (context, *cuda_ctx);
229
230     gst_element_set_context (element, context);
231
232     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
233         "posting have context (%p) message with CUDA context (%p)",
234         context, *cuda_ctx);
235     msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
236     gst_element_post_message (GST_ELEMENT_CAST (element), msg);
237   }
238
239   return TRUE;
240 }
241
242 /**
243  * gst_cuda_handle_set_context:
244  * @element: a #GstElement
245  * @context: a #GstContext
246  * @device_id: preferred device-id, pass device_id >=0 when
247  *             the device_id explicitly required. Otherwise, set -1.
248  * @cuda_ctx: (inout) (transfer full): location of a #GstCudaContext
249  *
250  * Helper function for implementing #GstElementClass.set_context() in
251  * CUDA capable elements.
252  *
253  * Retrieves the #GstCudaContext in @context and places the result in @cuda_ctx.
254  *
255  * Returns: whether the @cuda_ctx could be set successfully
256  */
257 gboolean
258 gst_cuda_handle_set_context (GstElement * element,
259     GstContext * context, gint device_id, GstCudaContext ** cuda_ctx)
260 {
261   const gchar *context_type;
262
263   g_return_val_if_fail (element != NULL, FALSE);
264   g_return_val_if_fail (cuda_ctx != NULL, FALSE);
265
266   _init_debug ();
267
268   if (!context)
269     return FALSE;
270
271   context_type = gst_context_get_context_type (context);
272   if (g_strcmp0 (context_type, GST_CUDA_CONTEXT_TYPE) == 0) {
273     const GstStructure *str;
274     GstCudaContext *other_ctx = NULL;
275     guint other_device_id = 0;
276
277     /* If we had context already, will not replace it */
278     if (*cuda_ctx)
279       return TRUE;
280
281     str = gst_context_get_structure (context);
282     if (gst_structure_get (str, GST_CUDA_CONTEXT_TYPE, GST_TYPE_CUDA_CONTEXT,
283             &other_ctx, NULL)) {
284       g_object_get (other_ctx, "cuda-device-id", &other_device_id, NULL);
285
286       if (device_id == -1 || other_device_id == device_id) {
287         GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, element, "Found CUDA context");
288         *cuda_ctx = other_ctx;
289
290         return TRUE;
291       }
292
293       gst_object_unref (other_ctx);
294     }
295   }
296
297   return FALSE;
298 }
299
300 /**
301  * gst_cuda_handle_context_query:
302  * @element: a #GstElement
303  * @query: a #GstQuery of type %GST_QUERY_CONTEXT
304  * @cuda_ctx: (transfer none) (nullable): a #GstCudaContext
305  *
306  * Returns: Whether the @query was successfully responded to from the passed
307  *          @context.
308  */
309 gboolean
310 gst_cuda_handle_context_query (GstElement * element,
311     GstQuery * query, GstCudaContext * cuda_ctx)
312 {
313   const gchar *context_type;
314   GstContext *context, *old_context;
315
316   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
317   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
318   g_return_val_if_fail (cuda_ctx == NULL
319       || GST_IS_CUDA_CONTEXT (cuda_ctx), FALSE);
320
321   _init_debug ();
322
323   GST_CAT_LOG_OBJECT (GST_CAT_CONTEXT, element,
324       "handle context query %" GST_PTR_FORMAT, query);
325   gst_query_parse_context_type (query, &context_type);
326
327   if (cuda_ctx && g_strcmp0 (context_type, GST_CUDA_CONTEXT_TYPE) == 0) {
328     gst_query_parse_context (query, &old_context);
329
330     if (old_context)
331       context = gst_context_copy (old_context);
332     else
333       context = gst_context_new (GST_CUDA_CONTEXT_TYPE, TRUE);
334
335     context_set_cuda_context (context, cuda_ctx);
336     gst_query_set_context (query, context);
337     gst_context_unref (context);
338     GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, element,
339         "successfully set %" GST_PTR_FORMAT " on %" GST_PTR_FORMAT, cuda_ctx,
340         query);
341
342     return TRUE;
343   }
344
345   return FALSE;
346 }
347
348 /**
349  * gst_context_new_cuda_context:
350  * @cuda_ctx: (transfer none) a #GstCudaContext
351  *
352  * Returns: (transfer full) (nullable): a new #GstContext embedding the @cuda_ctx
353  * or %NULL
354  */
355 GstContext *
356 gst_context_new_cuda_context (GstCudaContext * cuda_ctx)
357 {
358   GstContext *context;
359
360   g_return_val_if_fail (GST_IS_CUDA_CONTEXT (cuda_ctx), NULL);
361
362   context = gst_context_new (GST_CUDA_CONTEXT_TYPE, TRUE);
363   context_set_cuda_context (context, cuda_ctx);
364
365   return context;
366 }
367
368 static const gchar *gst_cuda_quark_strings[] =
369     { "GstCudaQuarkGraphicsResource" };
370
371 static GQuark gst_cuda_quark_table[GST_CUDA_QUARK_MAX];
372
373 static void
374 init_cuda_quark_once (void)
375 {
376   static gsize once_init = 0;
377
378   if (g_once_init_enter (&once_init)) {
379     gint i;
380
381     for (i = 0; i < GST_CUDA_QUARK_MAX; i++)
382       gst_cuda_quark_table[i] =
383           g_quark_from_static_string (gst_cuda_quark_strings[i]);
384
385     g_once_init_leave (&once_init, 1);
386   }
387 }
388
389 /**
390  * gst_cuda_quark_from_id: (skip)
391  * @id: a #GstCudaQuarkId
392  *
393  * Returns: the GQuark for given @id or 0 if @id is unknown value
394  */
395 GQuark
396 gst_cuda_quark_from_id (GstCudaQuarkId id)
397 {
398   g_return_val_if_fail (id < GST_CUDA_QUARK_MAX, 0);
399
400   init_cuda_quark_once ();
401   _init_debug ();
402
403   return gst_cuda_quark_table[id];
404 }
405
406 /**
407  * gst_cuda_graphics_resource_new: (skip)
408  * @context: (transfer none): a #GstCudaContext
409  * @graphics_context: (transfer none) (nullable): a graphics API specific context object
410  * @type: a #GstCudaGraphicsResourceType of resource registration
411  *
412  * Create new #GstCudaGraphicsResource with given @context and @type
413  *
414  * Returns: a new #GstCudaGraphicsResource.
415  * Free with gst_cuda_graphics_resource_free
416  */
417 GstCudaGraphicsResource *
418 gst_cuda_graphics_resource_new (GstCudaContext *
419     context, GstObject * graphics_context, GstCudaGraphicsResourceType type)
420 {
421   GstCudaGraphicsResource *resource;
422
423   g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), NULL);
424
425   _init_debug ();
426
427   resource = g_new0 (GstCudaGraphicsResource, 1);
428   resource->cuda_context = gst_object_ref (context);
429   if (graphics_context)
430     resource->graphics_context = gst_object_ref (graphics_context);
431
432   return resource;
433 }
434
435 /**
436  * gst_cuda_graphics_resource_register_gl_buffer: (skip)
437  * @resource a #GstCudaGraphicsResource
438  * @buffer: a GL buffer object
439  * @flags: a #CUgraphicsRegisterFlags
440  *
441  * Register the @buffer for access by CUDA.
442  * Must be called from the gl context thread with current cuda context was
443  * pushed on the current thread
444  *
445  * Returns: whether @buffer was registered or not
446  */
447 gboolean
448 gst_cuda_graphics_resource_register_gl_buffer (GstCudaGraphicsResource *
449     resource, guint buffer, CUgraphicsRegisterFlags flags)
450 {
451   CUresult cuda_ret;
452
453   g_return_val_if_fail (resource != NULL, FALSE);
454   g_return_val_if_fail (resource->registered == FALSE, FALSE);
455
456   _init_debug ();
457
458   cuda_ret = CuGraphicsGLRegisterBuffer (&resource->resource, buffer, flags);
459
460   if (!gst_cuda_result (cuda_ret))
461     return FALSE;
462
463   resource->registered = TRUE;
464   resource->type = GST_CUDA_GRAPHICS_RESOURCE_GL_BUFFER;
465   resource->flags = flags;
466
467   return TRUE;
468 }
469
470 /**
471  * gst_cuda_graphics_resource_register_d3d11_resource: (skip)
472  * @resource a #GstCudaGraphicsResource
473  * @d3d11_resource: a ID3D11Resource
474  * @flags: a #CUgraphicsRegisterFlags
475  *
476  * Register the @d3d11_resource for accessing by CUDA.
477  * Must be called with d3d11 device lock with current cuda context was
478  * pushed on the current thread
479  *
480  * Returns: whether @d3d11_resource was registered or not
481  */
482 gboolean
483 gst_cuda_graphics_resource_register_d3d11_resource (GstCudaGraphicsResource *
484     resource, gpointer d3d11_resource, CUgraphicsRegisterFlags flags)
485 {
486   CUresult cuda_ret;
487
488   g_return_val_if_fail (resource != NULL, FALSE);
489   g_return_val_if_fail (resource->registered == FALSE, FALSE);
490
491   _init_debug ();
492
493   cuda_ret = CuGraphicsD3D11RegisterResource (&resource->resource,
494       d3d11_resource, flags);
495
496   if (!gst_cuda_result (cuda_ret))
497     return FALSE;
498
499   resource->registered = TRUE;
500   resource->type = GST_CUDA_GRAPHICS_RESOURCE_D3D11_RESOURCE;
501   resource->flags = flags;
502
503   return TRUE;
504 }
505
506 /**
507  * gst_cuda_graphics_resource_unregister: (skip)
508  * @resource: a #GstCudaGraphicsResource
509  *
510  * Unregister previously registered resource.
511  * For GL resource, this method must be called from gl context thread.
512  * Also, current cuda context should be pushed on the current thread
513  * before calling this method.
514  */
515 void
516 gst_cuda_graphics_resource_unregister (GstCudaGraphicsResource * resource)
517 {
518   g_return_if_fail (resource != NULL);
519
520   _init_debug ();
521
522   if (!resource->registered)
523     return;
524
525   gst_cuda_result (CuGraphicsUnregisterResource (resource->resource));
526   resource->resource = NULL;
527   resource->registered = FALSE;
528
529   return;
530 }
531
532 /**
533  * gst_cuda_graphics_resource_map: (skip)
534  * @resource: a #GstCudaGraphicsResource
535  * @stream: a #CUstream
536  * @flags: a #CUgraphicsMapResourceFlags
537  *
538  * Map previously registered resource with map flags
539  *
540  * Returns: the #CUgraphicsResource if successful or %NULL when failed
541  */
542 CUgraphicsResource
543 gst_cuda_graphics_resource_map (GstCudaGraphicsResource * resource,
544     CUstream stream, CUgraphicsMapResourceFlags flags)
545 {
546   CUresult cuda_ret;
547
548   g_return_val_if_fail (resource != NULL, NULL);
549   g_return_val_if_fail (resource->registered != FALSE, NULL);
550
551   _init_debug ();
552
553   cuda_ret = CuGraphicsResourceSetMapFlags (resource->resource, flags);
554   if (!gst_cuda_result (cuda_ret))
555     return NULL;
556
557   cuda_ret = CuGraphicsMapResources (1, &resource->resource, stream);
558   if (!gst_cuda_result (cuda_ret))
559     return NULL;
560
561   resource->mapped = TRUE;
562
563   return resource->resource;
564 }
565
566 /**
567  * gst_cuda_graphics_resource_unmap: (skip)
568  * @resource: a #GstCudaGraphicsResource
569  * @stream: a #CUstream
570  *
571  * Unmap previously mapped resource
572  */
573 void
574 gst_cuda_graphics_resource_unmap (GstCudaGraphicsResource * resource,
575     CUstream stream)
576 {
577   g_return_if_fail (resource != NULL);
578   g_return_if_fail (resource->registered != FALSE);
579
580   _init_debug ();
581
582   if (!resource->mapped)
583     return;
584
585   gst_cuda_result (CuGraphicsUnmapResources (1, &resource->resource, stream));
586
587   resource->mapped = FALSE;
588 }
589
590 #ifdef HAVE_NVCODEC_GST_GL
591 static void
592 unregister_resource_from_gl_thread (GstGLContext * gl_context,
593     GstCudaGraphicsResource * resource)
594 {
595   GstCudaContext *cuda_context = resource->cuda_context;
596
597   if (!gst_cuda_context_push (cuda_context)) {
598     GST_WARNING_OBJECT (cuda_context, "failed to push CUDA context");
599     return;
600   }
601
602   gst_cuda_graphics_resource_unregister (resource);
603
604   if (!gst_cuda_context_pop (NULL)) {
605     GST_WARNING_OBJECT (cuda_context, "failed to pop CUDA context");
606   }
607 }
608 #endif
609
610 #ifdef HAVE_NVCODEC_GST_D3D11
611 static void
612 unregister_d3d11_resource (GstCudaGraphicsResource * resource)
613 {
614   GstCudaContext *cuda_context = resource->cuda_context;
615   GstD3D11Device *device = GST_D3D11_DEVICE (resource->graphics_context);
616
617   if (!gst_cuda_context_push (cuda_context)) {
618     GST_WARNING_OBJECT (cuda_context, "failed to push CUDA context");
619     return;
620   }
621
622   gst_d3d11_device_lock (device);
623   gst_cuda_graphics_resource_unregister (resource);
624   gst_d3d11_device_unlock (device);
625
626   if (!gst_cuda_context_pop (NULL)) {
627     GST_WARNING_OBJECT (cuda_context, "failed to pop CUDA context");
628   }
629 }
630 #endif
631
632 /**
633  * gst_cuda_graphics_resource_free: (skip)
634  * @resource: a #GstCudaGraphicsResource
635  *
636  * Free @resource
637  */
638 void
639 gst_cuda_graphics_resource_free (GstCudaGraphicsResource * resource)
640 {
641   g_return_if_fail (resource != NULL);
642
643   if (resource->registered) {
644 #ifdef HAVE_NVCODEC_GST_GL
645     if (resource->type == GST_CUDA_GRAPHICS_RESOURCE_GL_BUFFER) {
646       gst_gl_context_thread_add ((GstGLContext *) resource->graphics_context,
647           (GstGLContextThreadFunc) unregister_resource_from_gl_thread,
648           resource);
649     } else
650 #endif
651 #ifdef HAVE_NVCODEC_GST_D3D11
652     if (resource->type == GST_CUDA_GRAPHICS_RESOURCE_D3D11_RESOURCE) {
653       unregister_d3d11_resource (resource);
654     } else
655 #endif
656     {
657       /* FIXME: currently only opengl & d3d11 */
658       g_assert_not_reached ();
659     }
660   }
661
662   gst_object_unref (resource->cuda_context);
663   if (resource->graphics_context)
664     gst_object_unref (resource->graphics_context);
665   g_free (resource);
666 }
667
668 const gchar *
669 gst_cuda_buffery_copy_type_to_string (GstCudaBufferCopyType type)
670 {
671   switch (type) {
672     case GST_CUDA_BUFFER_COPY_SYSTEM:
673       return "SYSTEM";
674     case GST_CUDA_BUFFER_COPY_CUDA:
675       return "CUDA";
676     case GST_CUDA_BUFFER_COPY_GL:
677       return "GL";
678     case GST_CUDA_BUFFER_COPY_D3D11:
679       return "D3D11";
680     case GST_CUDA_BUFFER_COPY_NVMM:
681       return "NVMM";
682     default:
683       g_assert_not_reached ();
684       break;
685   }
686
687   return "UNKNOWN";
688 }
689
690 static gboolean
691 gst_cuda_buffer_fallback_copy (GstBuffer * dst, const GstVideoInfo * dst_info,
692     GstBuffer * src, const GstVideoInfo * src_info)
693 {
694   GstVideoFrame dst_frame, src_frame;
695   guint i, j;
696
697   if (!gst_video_frame_map (&dst_frame, dst_info, dst, GST_MAP_WRITE)) {
698     GST_ERROR ("Failed to map dst buffer");
699     return FALSE;
700   }
701
702   if (!gst_video_frame_map (&src_frame, src_info, src, GST_MAP_READ)) {
703     gst_video_frame_unmap (&dst_frame);
704     GST_ERROR ("Failed to map src buffer");
705     return FALSE;
706   }
707
708   /* src and dst resolutions can be different, pick min value */
709   for (i = 0; GST_VIDEO_FRAME_N_PLANES (&dst_frame); i++) {
710     guint dst_width_in_bytes, src_width_in_bytes;
711     guint dst_height, src_height;
712     guint width_in_bytes, height;
713     guint dst_stride, src_stride;
714     guint8 *dst_data, *src_data;
715
716     dst_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&dst_frame, i) *
717         GST_VIDEO_FRAME_COMP_PSTRIDE (&dst_frame, i);
718     src_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&src_frame, i) *
719         GST_VIDEO_FRAME_COMP_PSTRIDE (&src_frame, i);
720
721     width_in_bytes = MIN (dst_width_in_bytes, src_width_in_bytes);
722
723     dst_height = GST_VIDEO_FRAME_COMP_HEIGHT (&dst_frame, i);
724     src_height = GST_VIDEO_FRAME_COMP_HEIGHT (&src_frame, i);
725
726     height = MIN (dst_height, src_height);
727
728     dst_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&dst_frame, i);
729     src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&src_frame, i);
730
731     dst_data = GST_VIDEO_FRAME_PLANE_DATA (&dst_frame, i);
732     src_data = GST_VIDEO_FRAME_PLANE_DATA (&src_frame, i);
733
734     for (j = 0; j < height; j++) {
735       memcpy (dst_data, src_data, width_in_bytes);
736       dst_data += dst_stride;
737       src_data += src_stride;
738     }
739   }
740
741   gst_video_frame_unmap (&src_frame);
742   gst_video_frame_unmap (&dst_frame);
743
744   return TRUE;
745 }
746
747 static gboolean
748 map_buffer_and_fill_copy2d (GstBuffer * buf, const GstVideoInfo * info,
749     GstCudaBufferCopyType copy_type, GstVideoFrame * frame,
750     GstMapInfo * map_info, gboolean is_src,
751     CUDA_MEMCPY2D copy_params[GST_VIDEO_MAX_PLANES])
752 {
753   gboolean buffer_mapped = FALSE;
754   guint i;
755
756 #ifdef HAVE_NVCODEC_NVMM
757   if (copy_type == GST_CUDA_BUFFER_COPY_NVMM) {
758     NvBufSurface *surface;
759     NvBufSurfaceParams *surface_params;
760     NvBufSurfacePlaneParams *plane_params;
761
762     if (!gst_buffer_map (buf, map_info, GST_MAP_READ)) {
763       GST_ERROR ("Failed to map input NVMM buffer");
764       memset (map_info, 0, sizeof (GstMapInfo));
765       return FALSE;
766     }
767
768     surface = (NvBufSurface *) map_info->data;
769
770     GST_TRACE ("batch-size %d, num-filled %d, memType %d",
771         surface->batchSize, surface->numFilled, surface->memType);
772
773     surface_params = surface->surfaceList;
774     buffer_mapped = TRUE;
775     if (!surface_params) {
776       GST_ERROR ("NVMM memory doesn't hold buffer");
777       goto error;
778     }
779
780     plane_params = &surface_params->planeParams;
781     if (plane_params->num_planes != GST_VIDEO_INFO_N_PLANES (info)) {
782       GST_ERROR ("num_planes mismatch, %d / %d",
783           plane_params->num_planes, GST_VIDEO_INFO_N_PLANES (info));
784       goto error;
785     }
786
787     switch (surface->memType) {
788         /* TODO: NVBUF_MEM_DEFAULT on jetson is SURFACE_ARRAY */
789       case NVBUF_MEM_DEFAULT:
790       case NVBUF_MEM_CUDA_DEVICE:
791       {
792         for (i = 0; i < plane_params->num_planes; i++) {
793           if (is_src) {
794             copy_params[i].srcMemoryType = CU_MEMORYTYPE_DEVICE;
795             copy_params[i].srcDevice = (CUdeviceptr)
796                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
797             copy_params[i].srcPitch = plane_params->pitch[i];
798           } else {
799             copy_params[i].dstMemoryType = CU_MEMORYTYPE_DEVICE;
800             copy_params[i].dstDevice = (CUdeviceptr)
801                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
802             copy_params[i].dstPitch = plane_params->pitch[i];
803           }
804         }
805         break;
806       }
807       case NVBUF_MEM_CUDA_PINNED:
808       {
809         for (i = 0; i < plane_params->num_planes; i++) {
810           if (is_src) {
811             copy_params[i].srcMemoryType = CU_MEMORYTYPE_HOST;
812             copy_params[i].srcHost =
813                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
814             copy_params[i].srcPitch = plane_params->pitch[i];
815           } else {
816             copy_params[i].dstMemoryType = CU_MEMORYTYPE_HOST;
817             copy_params[i].dstHost =
818                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
819             copy_params[i].dstPitch = plane_params->pitch[i];
820           }
821         }
822         break;
823       }
824       case NVBUF_MEM_CUDA_UNIFIED:
825       {
826         for (i = 0; i < plane_params->num_planes; i++) {
827           if (is_src) {
828             copy_params[i].srcMemoryType = CU_MEMORYTYPE_UNIFIED;
829             copy_params[i].srcDevice = (CUdeviceptr)
830                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
831             copy_params[i].srcPitch = plane_params->pitch[i];
832           } else {
833             copy_params[i].dstMemoryType = CU_MEMORYTYPE_UNIFIED;
834             copy_params[i].dstDevice = (CUdeviceptr)
835                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
836             copy_params[i].dstPitch = plane_params->pitch[i];
837           }
838         }
839         break;
840       }
841       default:
842         GST_ERROR ("Unexpected NVMM memory type %d", surface->memType);
843         goto error;
844     }
845
846     for (i = 0; i < plane_params->num_planes; i++) {
847       gsize width_in_bytes, height;
848
849       width_in_bytes = plane_params->width[i] * plane_params->bytesPerPix[i];
850       height = plane_params->height[i];
851
852       if (copy_params[i].WidthInBytes == 0 ||
853           width_in_bytes < copy_params[i].WidthInBytes) {
854         copy_params[i].WidthInBytes = width_in_bytes;
855       }
856
857       if (copy_params[i].Height == 0 || height < copy_params[i].Height) {
858         copy_params[i].Height = height;
859       }
860     }
861   } else
862 #endif
863   {
864     GstMapFlags map_flags;
865
866     if (is_src)
867       map_flags = GST_MAP_READ;
868     else
869       map_flags = GST_MAP_WRITE;
870
871     if (copy_type == GST_CUDA_BUFFER_COPY_CUDA)
872       map_flags |= GST_MAP_CUDA;
873
874     if (!gst_video_frame_map (frame, info, buf, map_flags)) {
875       GST_ERROR ("Failed to map buffer");
876       goto error;
877     }
878
879     for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (frame); i++) {
880       gsize width_in_bytes, height;
881
882       if (is_src) {
883         if (copy_type == GST_CUDA_BUFFER_COPY_CUDA) {
884           copy_params[i].srcMemoryType = CU_MEMORYTYPE_DEVICE;
885           copy_params[i].srcDevice =
886               (CUdeviceptr) GST_VIDEO_FRAME_PLANE_DATA (frame, i);
887         } else {
888           copy_params[i].srcMemoryType = CU_MEMORYTYPE_HOST;
889           copy_params[i].srcHost = GST_VIDEO_FRAME_PLANE_DATA (frame, i);
890         }
891         copy_params[i].srcPitch = GST_VIDEO_FRAME_PLANE_STRIDE (frame, i);
892       } else {
893         if (copy_type == GST_CUDA_BUFFER_COPY_CUDA) {
894           copy_params[i].dstMemoryType = CU_MEMORYTYPE_DEVICE;
895           copy_params[i].dstDevice =
896               (CUdeviceptr) GST_VIDEO_FRAME_PLANE_DATA (frame, i);
897         } else {
898           copy_params[i].dstMemoryType = CU_MEMORYTYPE_HOST;
899           copy_params[i].dstHost = GST_VIDEO_FRAME_PLANE_DATA (frame, i);
900         }
901         copy_params[i].dstPitch = GST_VIDEO_FRAME_PLANE_STRIDE (frame, i);
902       }
903
904       width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (frame, i) *
905           GST_VIDEO_FRAME_COMP_PSTRIDE (frame, i);
906       height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, i);
907
908       if (copy_params[i].WidthInBytes == 0 ||
909           width_in_bytes < copy_params[i].WidthInBytes) {
910         copy_params[i].WidthInBytes = width_in_bytes;
911       }
912
913       if (copy_params[i].Height == 0 || height < copy_params[i].Height) {
914         copy_params[i].Height = height;
915       }
916     }
917   }
918
919   return TRUE;
920
921 error:
922   if (buffer_mapped) {
923     gst_buffer_unmap (buf, map_info);
924     memset (map_info, 0, sizeof (GstMapInfo));
925   }
926
927   return FALSE;
928 }
929
930 static void
931 unmap_buffer_or_frame (GstBuffer * buf, GstVideoFrame * frame,
932     GstMapInfo * map_info)
933 {
934   if (frame->buffer)
935     gst_video_frame_unmap (frame);
936
937   if (map_info->data)
938     gst_buffer_unmap (buf, map_info);
939 }
940
941 static gboolean
942 gst_cuda_buffer_copy_internal (GstBuffer * dst_buf,
943     GstCudaBufferCopyType dst_type, const GstVideoInfo * dst_info,
944     GstBuffer * src_buf, GstCudaBufferCopyType src_type,
945     const GstVideoInfo * src_info, GstCudaContext * context, CUstream stream)
946 {
947   GstVideoFrame dst_frame, src_frame;
948   gboolean ret = FALSE;
949   GstMapInfo dst_map, src_map;
950   guint i;
951   CUDA_MEMCPY2D copy_params[GST_VIDEO_MAX_PLANES];
952
953   memset (copy_params, 0, sizeof (copy_params));
954   memset (&dst_frame, 0, sizeof (GstVideoFrame));
955   memset (&src_frame, 0, sizeof (GstVideoFrame));
956   memset (&dst_map, 0, sizeof (GstMapInfo));
957   memset (&src_map, 0, sizeof (GstMapInfo));
958
959   if (!map_buffer_and_fill_copy2d (dst_buf, dst_info,
960           dst_type, &dst_frame, &dst_map, FALSE, copy_params)) {
961     GST_ERROR_OBJECT (context, "Failed to map output buffer");
962     return FALSE;
963   }
964
965   if (!map_buffer_and_fill_copy2d (src_buf, src_info,
966           src_type, &src_frame, &src_map, TRUE, copy_params)) {
967     GST_ERROR_OBJECT (context, "Failed to map input buffer");
968     unmap_buffer_or_frame (dst_buf, &dst_frame, &dst_map);
969     return FALSE;
970   }
971
972   if (!gst_cuda_context_push (context)) {
973     GST_ERROR_OBJECT (context, "Failed to push our context");
974     goto unmap_and_out;
975   }
976
977   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (dst_info); i++) {
978     ret = gst_cuda_result (CuMemcpy2DAsync (&copy_params[i], stream));
979     if (!ret) {
980       GST_ERROR_OBJECT (context, "Failed to copy plane %d", i);
981       break;
982     }
983   }
984
985   gst_cuda_result (CuStreamSynchronize (stream));
986   gst_cuda_context_pop (NULL);
987
988 unmap_and_out:
989   unmap_buffer_or_frame (dst_buf, &src_frame, &src_map);
990   unmap_buffer_or_frame (src_buf, &dst_frame, &dst_map);
991
992   return ret;
993 }
994
995 #ifdef HAVE_NVCODEC_GST_GL
996 static gboolean
997 ensure_gl_interop (void)
998 {
999   guint device_count = 0;
1000   CUdevice device_list[1] = { 0, };
1001   CUresult cuda_ret;
1002
1003   cuda_ret = CuGLGetDevices (&device_count,
1004       device_list, 1, CU_GL_DEVICE_LIST_ALL);
1005
1006   if (cuda_ret != CUDA_SUCCESS || device_count == 0)
1007     return FALSE;
1008
1009   return TRUE;
1010 }
1011
1012 typedef struct _GLCopyData
1013 {
1014   GstBuffer *src_buf;
1015   const GstVideoInfo *src_info;
1016   GstBuffer *dst_buf;
1017   const GstVideoInfo *dst_info;
1018
1019   gboolean pbo_to_cuda;
1020   GstCudaBufferCopyType copy_type;
1021   GstCudaContext *context;
1022   CUstream stream;
1023   gboolean ret;
1024 } GLCopyData;
1025
1026 static GstCudaGraphicsResource *
1027 ensure_cuda_gl_graphics_resource (GstCudaContext * context, GstMemory * mem)
1028 {
1029   GQuark quark;
1030   GstCudaGraphicsResource *ret = NULL;
1031
1032   if (!gst_is_gl_memory_pbo (mem)) {
1033     GST_WARNING_OBJECT (context, "memory is not GL PBO memory, %s",
1034         mem->allocator->mem_type);
1035     return NULL;
1036   }
1037
1038   quark = gst_cuda_quark_from_id (GST_CUDA_QUARK_GRAPHICS_RESOURCE);
1039   ret = (GstCudaGraphicsResource *)
1040       gst_mini_object_get_qdata (GST_MINI_OBJECT (mem), quark);
1041
1042   if (!ret) {
1043     GstGLMemoryPBO *pbo;
1044     GstGLBuffer *buf;
1045     GstMapInfo info;
1046
1047     ret = gst_cuda_graphics_resource_new (context,
1048         GST_OBJECT (GST_GL_BASE_MEMORY_CAST (mem)->context),
1049         GST_CUDA_GRAPHICS_RESOURCE_GL_BUFFER);
1050
1051     if (!gst_memory_map (mem, &info, (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) {
1052       GST_ERROR_OBJECT (context, "Failed to map gl memory");
1053       gst_cuda_graphics_resource_free (ret);
1054       return NULL;
1055     }
1056
1057     pbo = (GstGLMemoryPBO *) mem;
1058     buf = pbo->pbo;
1059
1060     if (!gst_cuda_graphics_resource_register_gl_buffer (ret,
1061             buf->id, CU_GRAPHICS_REGISTER_FLAGS_NONE)) {
1062       GST_ERROR_OBJECT (context, "Failed to register gl buffer");
1063       gst_memory_unmap (mem, &info);
1064       gst_cuda_graphics_resource_free (ret);
1065
1066       return NULL;
1067     }
1068
1069     gst_memory_unmap (mem, &info);
1070
1071     gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), quark, ret,
1072         (GDestroyNotify) gst_cuda_graphics_resource_free);
1073   }
1074
1075   return ret;
1076 }
1077
1078 static void
1079 gl_copy_thread_func (GstGLContext * gl_context, GLCopyData * data)
1080 {
1081   GstCudaGraphicsResource *resources[GST_VIDEO_MAX_PLANES];
1082   guint num_resources;
1083   GstBuffer *gl_buf, *cuda_buf;
1084   GstVideoFrame cuda_frame;
1085   GstMapInfo cuda_map_info;
1086   CUDA_MEMCPY2D copy_params[GST_VIDEO_MAX_PLANES];
1087   guint i;
1088   GstCudaContext *context = data->context;
1089   CUstream stream = data->stream;
1090
1091   memset (copy_params, 0, sizeof (copy_params));
1092   memset (&cuda_frame, 0, sizeof (GstVideoFrame));
1093   memset (&cuda_map_info, 0, sizeof (GstMapInfo));
1094
1095   data->ret = FALSE;
1096
1097   /* Incompatible gl context */
1098   if (!ensure_gl_interop ())
1099     return;
1100
1101   if (data->pbo_to_cuda) {
1102     gl_buf = data->src_buf;
1103     cuda_buf = data->dst_buf;
1104
1105     if (!map_buffer_and_fill_copy2d (cuda_buf,
1106             data->dst_info, data->copy_type, &cuda_frame, &cuda_map_info,
1107             FALSE, copy_params)) {
1108       GST_ERROR_OBJECT (context, "Failed to map output CUDA buffer");
1109       return;
1110     }
1111   } else {
1112     gl_buf = data->dst_buf;
1113     cuda_buf = data->src_buf;
1114
1115     if (!map_buffer_and_fill_copy2d (cuda_buf,
1116             data->src_info, data->copy_type, &cuda_frame, &cuda_map_info,
1117             TRUE, copy_params)) {
1118       GST_ERROR_OBJECT (context, "Failed to map input CUDA buffer");
1119       return;
1120     }
1121   }
1122
1123   num_resources = gst_buffer_n_memory (gl_buf);
1124   g_assert (num_resources >= GST_VIDEO_INFO_N_PLANES (data->src_info));
1125
1126   if (!gst_cuda_context_push (context)) {
1127     GST_ERROR_OBJECT (context, "Failed to push context");
1128     unmap_buffer_or_frame (cuda_buf, &cuda_frame, &cuda_map_info);
1129     return;
1130   }
1131
1132   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (data->src_info); i++) {
1133     GstMemory *mem = gst_buffer_peek_memory (gl_buf, i);
1134     GstGLMemoryPBO *pbo;
1135
1136     resources[i] = ensure_cuda_gl_graphics_resource (context, mem);
1137     if (!resources[i])
1138       goto out;
1139
1140     pbo = (GstGLMemoryPBO *) mem;
1141     if (!data->pbo_to_cuda) {
1142       /* Need PBO -> texture */
1143       GST_MINI_OBJECT_FLAG_SET (mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD);
1144
1145       /* PBO -> sysmem */
1146       GST_MINI_OBJECT_FLAG_SET (pbo->pbo,
1147           GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD);
1148     } else {
1149       /* get the texture into the PBO */
1150       gst_gl_memory_pbo_upload_transfer (pbo);
1151       gst_gl_memory_pbo_download_transfer (pbo);
1152     }
1153   }
1154
1155   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (data->src_info); i++) {
1156     CUgraphicsResource cuda_resource;
1157     CUdeviceptr dev_ptr;
1158     size_t size;
1159     gboolean copy_ret;
1160     gsize width_in_bytes, height;
1161
1162     if (data->pbo_to_cuda) {
1163       cuda_resource =
1164           gst_cuda_graphics_resource_map (resources[i], stream,
1165           CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
1166     } else {
1167       cuda_resource =
1168           gst_cuda_graphics_resource_map (resources[i], stream,
1169           CU_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD);
1170     }
1171
1172     if (!cuda_resource) {
1173       GST_ERROR_OBJECT (context, "Failed to map graphics resource %d", i);
1174       goto out;
1175     }
1176
1177     if (!gst_cuda_result (CuGraphicsResourceGetMappedPointer (&dev_ptr, &size,
1178                 cuda_resource))) {
1179       gst_cuda_graphics_resource_unmap (resources[i], stream);
1180       GST_ERROR_OBJECT (context, "Failed to get mapped pointer");
1181       goto out;
1182     }
1183
1184     if (data->pbo_to_cuda) {
1185       copy_params[i].srcMemoryType = CU_MEMORYTYPE_DEVICE;
1186       copy_params[i].srcDevice = dev_ptr;
1187       copy_params[i].srcPitch = GST_VIDEO_INFO_PLANE_STRIDE (data->src_info, i);
1188
1189       width_in_bytes = GST_VIDEO_INFO_COMP_WIDTH (data->src_info, i) *
1190           GST_VIDEO_INFO_COMP_PSTRIDE (data->src_info, i);
1191       height = GST_VIDEO_INFO_COMP_HEIGHT (data->src_info, i);
1192     } else {
1193       copy_params[i].dstMemoryType = CU_MEMORYTYPE_DEVICE;
1194       copy_params[i].dstDevice = dev_ptr;
1195       copy_params[i].dstPitch = GST_VIDEO_INFO_PLANE_STRIDE (data->dst_info, i);
1196
1197       width_in_bytes = GST_VIDEO_INFO_COMP_WIDTH (data->dst_info, i) *
1198           GST_VIDEO_INFO_COMP_PSTRIDE (data->dst_info, i);
1199       height = GST_VIDEO_INFO_COMP_HEIGHT (data->dst_info, i);
1200     }
1201
1202     if (width_in_bytes < copy_params[i].WidthInBytes)
1203       copy_params[i].WidthInBytes = width_in_bytes;
1204
1205     if (height < copy_params[i].Height)
1206       copy_params[i].Height = height;
1207
1208     copy_ret = gst_cuda_result (CuMemcpy2DAsync (&copy_params[i], stream));
1209     gst_cuda_graphics_resource_unmap (resources[i], stream);
1210
1211     if (!copy_ret) {
1212       GST_ERROR_OBJECT (context, "Failed to copy plane %d", i);
1213       goto out;
1214     }
1215   }
1216
1217   data->ret = TRUE;
1218
1219 out:
1220   gst_cuda_result (CuStreamSynchronize (stream));
1221   gst_cuda_context_pop (NULL);
1222   unmap_buffer_or_frame (cuda_buf, &cuda_frame, &cuda_map_info);
1223 }
1224
1225 static gboolean
1226 cuda_copy_gl_interop (GstBuffer * dst_buf, const GstVideoInfo * dst_info,
1227     GstBuffer * src_buf, const GstVideoInfo * src_info,
1228     GstGLContext * gl_context, GstCudaContext * context, CUstream stream,
1229     gboolean pbo_to_cuda, GstCudaBufferCopyType copy_type)
1230 {
1231   GLCopyData data;
1232
1233   g_assert (copy_type == GST_CUDA_BUFFER_COPY_CUDA ||
1234       copy_type == GST_CUDA_BUFFER_COPY_NVMM);
1235
1236   data.src_buf = src_buf;
1237   data.src_info = src_info;
1238   data.dst_buf = dst_buf;
1239   data.dst_info = dst_info;
1240   data.pbo_to_cuda = pbo_to_cuda;
1241   data.copy_type = copy_type;
1242   data.context = context;
1243   data.stream = stream;
1244   data.ret = FALSE;
1245
1246   gst_gl_context_thread_add (gl_context,
1247       (GstGLContextThreadFunc) gl_copy_thread_func, &data);
1248
1249   return data.ret;
1250 }
1251 #endif
1252
1253 #ifdef HAVE_NVCODEC_GST_D3D11
1254 static gboolean
1255 ensure_d3d11_interop (GstCudaContext * context, GstD3D11Device * device)
1256 {
1257   guint device_count = 0;
1258   guint cuda_device_id;
1259   CUdevice device_list[1] = { 0, };
1260   CUresult cuda_ret;
1261
1262   g_object_get (context, "cuda-device-id", &cuda_device_id, NULL);
1263
1264   cuda_ret = CuD3D11GetDevices (&device_count,
1265       device_list, 1, gst_d3d11_device_get_device_handle (device),
1266       CU_D3D11_DEVICE_LIST_ALL);
1267
1268   if (cuda_ret != CUDA_SUCCESS || device_count == 0)
1269     return FALSE;
1270
1271   if (device_list[0] != (CUdevice) cuda_device_id)
1272     return FALSE;
1273
1274   return TRUE;
1275 }
1276
1277 static GstCudaGraphicsResource *
1278 ensure_cuda_d3d11_graphics_resource (GstCudaContext * context, GstMemory * mem)
1279 {
1280   GQuark quark;
1281   GstCudaGraphicsResource *ret = NULL;
1282
1283   if (!gst_is_d3d11_memory (mem)) {
1284     GST_WARNING_OBJECT (context, "memory is not D3D11 memory, %s",
1285         mem->allocator->mem_type);
1286     return NULL;
1287   }
1288
1289   quark = gst_cuda_quark_from_id (GST_CUDA_QUARK_GRAPHICS_RESOURCE);
1290   ret = (GstCudaGraphicsResource *)
1291       gst_mini_object_get_qdata (GST_MINI_OBJECT (mem), quark);
1292
1293   if (!ret) {
1294     ret = gst_cuda_graphics_resource_new (context,
1295         GST_OBJECT (GST_D3D11_MEMORY_CAST (mem)->device),
1296         GST_CUDA_GRAPHICS_RESOURCE_D3D11_RESOURCE);
1297
1298     if (!gst_cuda_graphics_resource_register_d3d11_resource (ret,
1299             gst_d3d11_memory_get_resource_handle (GST_D3D11_MEMORY_CAST (mem)),
1300             CU_GRAPHICS_REGISTER_FLAGS_SURFACE_LOAD_STORE)) {
1301       GST_ERROR_OBJECT (context, "failed to register d3d11 resource");
1302       gst_cuda_graphics_resource_free (ret);
1303
1304       return NULL;
1305     }
1306
1307     gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), quark, ret,
1308         (GDestroyNotify) gst_cuda_graphics_resource_free);
1309   }
1310
1311   return ret;
1312 }
1313
1314 static gboolean
1315 cuda_copy_d3d11_interop (GstBuffer * dst_buf, const GstVideoInfo * dst_info,
1316     GstBuffer * src_buf, const GstVideoInfo * src_info, GstD3D11Device * device,
1317     GstCudaContext * context, CUstream stream, gboolean d3d11_to_cuda)
1318 {
1319   GstCudaGraphicsResource *resources[GST_VIDEO_MAX_PLANES];
1320   D3D11_TEXTURE2D_DESC desc[GST_VIDEO_MAX_PLANES];
1321   guint num_resources;
1322   GstBuffer *d3d11_buf, *cuda_buf;
1323   GstVideoFrame d3d11_frame, cuda_frame;
1324   GstMapInfo cuda_map_info;
1325   CUDA_MEMCPY2D copy_params[GST_VIDEO_MAX_PLANES];
1326   guint i;
1327   gboolean ret = FALSE;
1328
1329   memset (copy_params, 0, sizeof (copy_params));
1330   memset (&cuda_frame, 0, sizeof (GstVideoFrame));
1331   memset (&cuda_map_info, 0, sizeof (GstMapInfo));
1332
1333   /* Incompatible d3d11 device */
1334   if (!ensure_d3d11_interop (context, device))
1335     return FALSE;
1336
1337   if (d3d11_to_cuda) {
1338     d3d11_buf = src_buf;
1339     cuda_buf = dst_buf;
1340     if (!gst_video_frame_map (&d3d11_frame, src_info, d3d11_buf,
1341             GST_MAP_READ | GST_MAP_D3D11)) {
1342       GST_ERROR_OBJECT (context, "Failed to map input D3D11 buffer");
1343       return FALSE;
1344     }
1345     if (!map_buffer_and_fill_copy2d (cuda_buf,
1346             dst_info, GST_CUDA_BUFFER_COPY_CUDA, &cuda_frame, &cuda_map_info,
1347             FALSE, copy_params)) {
1348       GST_ERROR_OBJECT (context, "Failed to map output CUDA buffer");
1349       gst_video_frame_unmap (&d3d11_frame);
1350       return FALSE;
1351     }
1352   } else {
1353     d3d11_buf = dst_buf;
1354     cuda_buf = src_buf;
1355     if (!gst_video_frame_map (&d3d11_frame, dst_info, d3d11_buf,
1356             GST_MAP_WRITE | GST_MAP_D3D11)) {
1357       GST_ERROR_OBJECT (context, "Failed to map output D3D11 buffer");
1358       return FALSE;
1359     }
1360     if (!map_buffer_and_fill_copy2d (cuda_buf,
1361             src_info, GST_CUDA_BUFFER_COPY_CUDA, &cuda_frame, &cuda_map_info,
1362             TRUE, copy_params)) {
1363       GST_ERROR_OBJECT (context, "Failed to map input CUDA buffer");
1364       gst_video_frame_unmap (&d3d11_frame);
1365       return FALSE;
1366     }
1367   }
1368
1369   num_resources = gst_buffer_n_memory (d3d11_buf);
1370   g_assert (num_resources >= GST_VIDEO_FRAME_N_PLANES (&d3d11_frame));
1371
1372   if (!gst_cuda_context_push (context)) {
1373     GST_ERROR_OBJECT (context, "Failed to push context");
1374     gst_video_frame_unmap (&d3d11_frame);
1375     unmap_buffer_or_frame (cuda_buf, &cuda_frame, &cuda_map_info);
1376     return FALSE;
1377   }
1378
1379   for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&d3d11_frame); i++) {
1380     GstMemory *mem = gst_buffer_peek_memory (d3d11_buf, i);
1381
1382     resources[i] = ensure_cuda_d3d11_graphics_resource (context, mem);
1383     if (!resources[i]
1384         || !gst_d3d11_memory_get_texture_desc (GST_D3D11_MEMORY_CAST (mem),
1385             &desc[i]))
1386       goto out;
1387   }
1388
1389   for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&d3d11_frame); i++) {
1390     CUgraphicsResource cuda_resource;
1391     CUarray d3d11_array;
1392     gboolean copy_ret;
1393
1394     if (d3d11_to_cuda) {
1395       cuda_resource =
1396           gst_cuda_graphics_resource_map (resources[i], stream,
1397           CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
1398     } else {
1399       cuda_resource =
1400           gst_cuda_graphics_resource_map (resources[i], stream,
1401           CU_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD);
1402     }
1403
1404     if (!cuda_resource) {
1405       GST_ERROR_OBJECT (context, "Failed to map graphics resource %d", i);
1406       goto out;
1407     }
1408
1409     if (!gst_cuda_result (CuGraphicsSubResourceGetMappedArray (&d3d11_array,
1410                 cuda_resource, 0, 0))) {
1411       gst_cuda_graphics_resource_unmap (resources[i], stream);
1412       GST_ERROR_OBJECT (context, "Failed to get mapped array");
1413       goto out;
1414     }
1415
1416     if (d3d11_to_cuda) {
1417       copy_params[i].srcMemoryType = CU_MEMORYTYPE_ARRAY;
1418       copy_params[i].srcArray = d3d11_array;
1419       copy_params[i].srcPitch =
1420           desc[i].Width * GST_VIDEO_FRAME_COMP_PSTRIDE (&d3d11_frame, i);
1421     } else {
1422       copy_params[i].dstMemoryType = CU_MEMORYTYPE_ARRAY;
1423       copy_params[i].dstArray = d3d11_array;
1424       copy_params[i].dstPitch =
1425           desc[i].Width * GST_VIDEO_FRAME_COMP_PSTRIDE (&d3d11_frame, i);
1426     }
1427
1428     copy_ret = gst_cuda_result (CuMemcpy2DAsync (&copy_params[i], stream));
1429     gst_cuda_graphics_resource_unmap (resources[i], stream);
1430
1431     if (!copy_ret) {
1432       GST_ERROR_OBJECT (context, "Failed to copy plane %d", i);
1433       goto out;
1434     }
1435   }
1436
1437   ret = TRUE;
1438
1439 out:
1440   gst_cuda_result (CuStreamSynchronize (stream));
1441   gst_cuda_context_pop (NULL);
1442   gst_video_frame_unmap (&d3d11_frame);
1443   unmap_buffer_or_frame (cuda_buf, &cuda_frame, &cuda_map_info);
1444
1445   return ret;
1446 }
1447 #endif
1448
1449 gboolean
1450 gst_cuda_buffer_copy (GstBuffer * dst, GstCudaBufferCopyType dst_type,
1451     const GstVideoInfo * dst_info, GstBuffer * src,
1452     GstCudaBufferCopyType src_type, const GstVideoInfo * src_info,
1453     GstCudaContext * context, CUstream stream)
1454 {
1455   gboolean use_copy_2d = FALSE;
1456   GstMemory *dst_mem, *src_mem;
1457 #ifdef HAVE_NVCODEC_GST_D3D11
1458   D3D11_TEXTURE2D_DESC desc;
1459 #endif
1460   GstCudaContext *cuda_context;
1461
1462   g_return_val_if_fail (GST_IS_BUFFER (dst), FALSE);
1463   g_return_val_if_fail (dst_info != NULL, FALSE);
1464   g_return_val_if_fail (GST_IS_BUFFER (src), FALSE);
1465   g_return_val_if_fail (src_info != NULL, FALSE);
1466   g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), FALSE);
1467
1468   if (dst_type == GST_CUDA_BUFFER_COPY_NVMM &&
1469       src_type == GST_CUDA_BUFFER_COPY_NVMM) {
1470     GST_ERROR_OBJECT (context, "Not supported copy NVMM -> NVMM");
1471     return FALSE;
1472   }
1473
1474   if (GST_VIDEO_INFO_FORMAT (dst_info) != GST_VIDEO_INFO_FORMAT (src_info)) {
1475     GST_ERROR_OBJECT (context,
1476         "Copy between different format is not supported");
1477     return FALSE;
1478   }
1479
1480   if (dst_type == GST_CUDA_BUFFER_COPY_CUDA ||
1481       dst_type == GST_CUDA_BUFFER_COPY_NVMM ||
1482       src_type == GST_CUDA_BUFFER_COPY_CUDA ||
1483       src_type == GST_CUDA_BUFFER_COPY_NVMM) {
1484     use_copy_2d = TRUE;
1485   }
1486
1487   if (!use_copy_2d) {
1488     GST_TRACE_OBJECT (context, "Not a device memory, use system memory copy");
1489     return gst_cuda_buffer_fallback_copy (dst, dst_info, src, src_info);
1490   }
1491
1492   dst_mem = gst_buffer_peek_memory (dst, 0);
1493   src_mem = gst_buffer_peek_memory (src, 0);
1494
1495 #ifdef HAVE_NVCODEC_GST_GL
1496   if (src_type == GST_CUDA_BUFFER_COPY_GL && gst_is_gl_memory_pbo (src_mem)) {
1497     GstGLMemory *gl_mem = (GstGLMemory *) src_mem;
1498     GstGLContext *gl_context = gl_mem->mem.context;
1499     GstCudaContext *cuda_context = context;
1500
1501     if (dst_type == GST_CUDA_BUFFER_COPY_CUDA && gst_is_cuda_memory (dst_mem))
1502       cuda_context = GST_CUDA_MEMORY_CAST (dst_mem)->context;
1503
1504     GST_TRACE_OBJECT (context, "GL -> %s",
1505         gst_cuda_buffery_copy_type_to_string (dst_type));
1506
1507     return cuda_copy_gl_interop (dst, dst_info, src, src_info, gl_context,
1508         cuda_context, stream, TRUE, dst_type);
1509   }
1510
1511   if (dst_type == GST_CUDA_BUFFER_COPY_GL && gst_is_gl_memory_pbo (dst_mem)) {
1512     GstGLMemory *gl_mem = (GstGLMemory *) dst_mem;
1513     GstGLContext *gl_context = gl_mem->mem.context;
1514     GstCudaContext *cuda_context = context;
1515
1516     if (src_type == GST_CUDA_BUFFER_COPY_CUDA && gst_is_cuda_memory (src_mem))
1517       cuda_context = GST_CUDA_MEMORY_CAST (src_mem)->context;
1518
1519     GST_TRACE_OBJECT (context, "%s -> GL",
1520         gst_cuda_buffery_copy_type_to_string (src_type));
1521
1522     return cuda_copy_gl_interop (dst, dst_info, src, src_info, gl_context,
1523         cuda_context, stream, FALSE, src_type);
1524   }
1525 #endif
1526
1527 #ifdef HAVE_NVCODEC_GST_D3D11
1528   if (src_type == GST_CUDA_BUFFER_COPY_D3D11 && gst_is_d3d11_memory (src_mem) &&
1529       gst_d3d11_memory_get_texture_desc (GST_D3D11_MEMORY_CAST (src_mem), &desc)
1530       && desc.Usage == D3D11_USAGE_DEFAULT && gst_is_cuda_memory (dst_mem)) {
1531     GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (src_mem);
1532     GstD3D11Device *device = dmem->device;
1533     GstCudaContext *cuda_context = GST_CUDA_MEMORY_CAST (dst_mem)->context;
1534     gboolean ret;
1535
1536     GST_TRACE_OBJECT (context, "D3D11 -> CUDA");
1537
1538     gst_d3d11_device_lock (device);
1539     ret = cuda_copy_d3d11_interop (dst, dst_info, src, src_info, device,
1540         cuda_context, stream, TRUE);
1541     gst_d3d11_device_unlock (device);
1542
1543     return ret;
1544   }
1545
1546   if (dst_type == GST_CUDA_BUFFER_COPY_D3D11 && gst_is_d3d11_memory (dst_mem) &&
1547       gst_d3d11_memory_get_texture_desc (GST_D3D11_MEMORY_CAST (dst_mem), &desc)
1548       && desc.Usage == D3D11_USAGE_DEFAULT && gst_is_cuda_memory (src_mem)) {
1549     GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (dst_mem);
1550     GstD3D11Device *device = dmem->device;
1551     GstCudaContext *cuda_context = GST_CUDA_MEMORY_CAST (src_mem)->context;
1552     gboolean ret;
1553
1554     GST_TRACE_OBJECT (context, "CUDA -> D3D11");
1555
1556     gst_d3d11_device_lock (device);
1557     ret = cuda_copy_d3d11_interop (dst, dst_info, src, src_info, device,
1558         cuda_context, stream, FALSE);
1559     gst_d3d11_device_unlock (device);
1560
1561     return ret;
1562   }
1563 #endif
1564
1565   if (gst_is_cuda_memory (dst_mem)) {
1566     cuda_context = GST_CUDA_MEMORY_CAST (dst_mem)->context;
1567   } else if (gst_is_cuda_memory (src_mem)) {
1568     cuda_context = GST_CUDA_MEMORY_CAST (src_mem)->context;
1569   } else {
1570     cuda_context = context;
1571   }
1572
1573   GST_TRACE_OBJECT (context, "%s -> %s",
1574       gst_cuda_buffery_copy_type_to_string (src_type),
1575       gst_cuda_buffery_copy_type_to_string (dst_type));
1576
1577   return gst_cuda_buffer_copy_internal (dst, dst_type, dst_info,
1578       src, src_type, src_info, cuda_context, stream);
1579 }