Merging gst-plugins-bad
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / nvcodec / gstnvdec.c
1 /*
2  * Copyright (C) 2017 Ericsson AB. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include "gstnvdec.h"
33 #include "gstcudautils.h"
34 #include "gstcudabufferpool.h"
35
36 #include <string.h>
37
38 GST_DEBUG_CATEGORY_EXTERN (gst_nvdec_debug);
39 #define GST_CAT_DEFAULT gst_nvdec_debug
40
41 #define DEFAULT_MAX_DISPLAY_DELAY -1
42
43 enum
44 {
45   PROP_0,
46   PROP_MAX_DISPLAY_DELAY,
47 };
48
49 #ifdef HAVE_NVCODEC_GST_GL
50 #define SUPPORTED_GL_APIS (GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2)
51
52 static gboolean
53 gst_nvdec_copy_device_to_gl (GstNvDec * nvdec,
54     CUVIDPARSERDISPINFO * dispinfo, GstBuffer * output_buffer);
55 #endif
56
57 static gboolean
58 gst_nvdec_copy_device_to_memory (GstNvDec * nvdec,
59     CUVIDPARSERDISPINFO * dispinfo, GstBuffer * output_buffer);
60
61 #ifdef HAVE_NVCODEC_GST_GL
62 typedef struct _GstNvDecRegisterResourceData
63 {
64   GstMemory *mem;
65   GstCudaGraphicsResource *resource;
66   GstNvDec *nvdec;
67   gboolean ret;
68 } GstNvDecRegisterResourceData;
69
70 static void
71 register_cuda_resource (GstGLContext * context,
72     GstNvDecRegisterResourceData * data)
73 {
74   GstMemory *mem = data->mem;
75   GstCudaGraphicsResource *resource = data->resource;
76   GstNvDec *nvdec = data->nvdec;
77   GstMapInfo map_info = GST_MAP_INFO_INIT;
78   GstGLBuffer *gl_buf_obj;
79
80   data->ret = FALSE;
81
82   if (!gst_cuda_context_push (nvdec->cuda_ctx)) {
83     GST_WARNING_OBJECT (nvdec, "failed to push CUDA context");
84     return;
85   }
86
87   if (gst_memory_map (mem, &map_info, GST_MAP_READ | GST_MAP_GL)) {
88     GstGLMemoryPBO *gl_mem = (GstGLMemoryPBO *) data->mem;
89     gl_buf_obj = gl_mem->pbo;
90
91     GST_LOG_OBJECT (nvdec,
92         "register glbuffer %d to CUDA resource", gl_buf_obj->id);
93
94     /* register resource without read/write only flags, since
95      * downstream CUDA elements (e.g., nvenc) might want to access
96      * this resource later. Instead, use map flags during map/unmap */
97     if (gst_cuda_graphics_resource_register_gl_buffer (resource,
98             gl_buf_obj->id, CU_GRAPHICS_REGISTER_FLAGS_NONE)) {
99       data->ret = TRUE;
100     } else {
101       GST_WARNING_OBJECT (nvdec, "failed to register memory");
102     }
103
104     gst_memory_unmap (mem, &map_info);
105   } else {
106     GST_WARNING_OBJECT (nvdec, "failed to map memory");
107   }
108
109   if (!gst_cuda_context_pop (NULL))
110     GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
111 }
112
113 static GstCudaGraphicsResource *
114 ensure_cuda_graphics_resource (GstMemory * mem, GstNvDec * nvdec)
115 {
116   GQuark quark;
117   GstCudaGraphicsResource *cgr_info;
118   GstNvDecRegisterResourceData data;
119
120   if (!gst_is_gl_memory_pbo (mem)) {
121     GST_WARNING_OBJECT (nvdec, "memory is not GL PBO memory, %s",
122         mem->allocator->mem_type);
123     return NULL;
124   }
125
126   quark = gst_cuda_quark_from_id (GST_CUDA_QUARK_GRAPHICS_RESOURCE);
127
128   cgr_info = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem), quark);
129   if (!cgr_info) {
130     cgr_info = gst_cuda_graphics_resource_new (nvdec->cuda_ctx,
131         GST_OBJECT (GST_GL_BASE_MEMORY_CAST (mem)->context),
132         GST_CUDA_GRAPHICS_RESOURCE_GL_BUFFER);
133     data.mem = mem;
134     data.resource = cgr_info;
135     data.nvdec = nvdec;
136     gst_gl_context_thread_add ((GstGLContext *) cgr_info->graphics_context,
137         (GstGLContextThreadFunc) register_cuda_resource, &data);
138     if (!data.ret) {
139       GST_WARNING_OBJECT (nvdec, "could not register resource");
140       gst_cuda_graphics_resource_free (cgr_info);
141
142       return NULL;
143     }
144
145     gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), quark, cgr_info,
146         (GDestroyNotify) gst_cuda_graphics_resource_free);
147   }
148
149   return cgr_info;
150 }
151 #endif /* HAVE_NVCODEC_GST_GL */
152
153 static gboolean gst_nvdec_open (GstVideoDecoder * decoder);
154 static gboolean gst_nvdec_start (GstVideoDecoder * decoder);
155 static gboolean gst_nvdec_stop (GstVideoDecoder * decoder);
156 static gboolean gst_nvdec_close (GstVideoDecoder * decoder);
157 static gboolean gst_nvdec_set_format (GstVideoDecoder * decoder,
158     GstVideoCodecState * state);
159 static GstFlowReturn gst_nvdec_handle_frame (GstVideoDecoder * decoder,
160     GstVideoCodecFrame * frame);
161 static gboolean gst_nvdec_decide_allocation (GstVideoDecoder * decoder,
162     GstQuery * query);
163 static void gst_nvdec_set_context (GstElement * element, GstContext * context);
164 static gboolean gst_nvdec_src_query (GstVideoDecoder * decoder,
165     GstQuery * query);
166 static gboolean gst_nvdec_flush (GstVideoDecoder * decoder);
167 static GstFlowReturn gst_nvdec_drain (GstVideoDecoder * decoder);
168 static GstFlowReturn gst_nvdec_finish (GstVideoDecoder * decoder);
169 static gboolean gst_nvdec_negotiate (GstVideoDecoder * decoder);
170 #ifdef HAVE_NVCODEC_GST_GL
171 static gboolean gst_nvdec_ensure_gl_context (GstNvDec * nvdec);
172 #endif
173
174 #define gst_nvdec_parent_class parent_class
175 G_DEFINE_ABSTRACT_TYPE (GstNvDec, gst_nvdec, GST_TYPE_VIDEO_DECODER);
176
177 static void
178 gst_nv_dec_set_property (GObject * object, guint prop_id, const GValue * value,
179     GParamSpec * pspec)
180 {
181   GstNvDec *nvdec = GST_NVDEC (object);
182
183   switch (prop_id) {
184     case PROP_MAX_DISPLAY_DELAY:
185       nvdec->max_display_delay = g_value_get_int (value);
186       break;
187     default:
188       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
189       break;
190   }
191 }
192
193 static void
194 gst_nv_dec_get_property (GObject * object, guint prop_id, GValue * value,
195     GParamSpec * pspec)
196 {
197   GstNvDec *nvdec = GST_NVDEC (object);
198
199   switch (prop_id) {
200     case PROP_MAX_DISPLAY_DELAY:
201       g_value_set_int (value, nvdec->max_display_delay);
202       break;
203     default:
204       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
205       break;
206   }
207 }
208
209 static void
210 gst_nvdec_class_init (GstNvDecClass * klass)
211 {
212   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
213   GstVideoDecoderClass *video_decoder_class = GST_VIDEO_DECODER_CLASS (klass);
214   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
215
216   gobject_class->set_property = gst_nv_dec_set_property;
217   gobject_class->get_property = gst_nv_dec_get_property;
218
219   video_decoder_class->open = GST_DEBUG_FUNCPTR (gst_nvdec_open);
220   video_decoder_class->start = GST_DEBUG_FUNCPTR (gst_nvdec_start);
221   video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_nvdec_stop);
222   video_decoder_class->close = GST_DEBUG_FUNCPTR (gst_nvdec_close);
223   video_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_nvdec_set_format);
224   video_decoder_class->handle_frame =
225       GST_DEBUG_FUNCPTR (gst_nvdec_handle_frame);
226   video_decoder_class->decide_allocation =
227       GST_DEBUG_FUNCPTR (gst_nvdec_decide_allocation);
228   video_decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_nvdec_src_query);
229   video_decoder_class->drain = GST_DEBUG_FUNCPTR (gst_nvdec_drain);
230   video_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_nvdec_flush);
231   video_decoder_class->finish = GST_DEBUG_FUNCPTR (gst_nvdec_finish);
232   video_decoder_class->negotiate = GST_DEBUG_FUNCPTR (gst_nvdec_negotiate);
233
234   element_class->set_context = GST_DEBUG_FUNCPTR (gst_nvdec_set_context);
235   gst_type_mark_as_plugin_api (GST_TYPE_NVDEC, 0);
236
237   g_object_class_install_property (gobject_class, PROP_MAX_DISPLAY_DELAY,
238       g_param_spec_int ("max-display-delay", "Max Display Delay",
239           "Improves pipelining of decode with display, 0 means no delay "
240           "(auto = -1)",
241           -1, G_MAXINT, DEFAULT_MAX_DISPLAY_DELAY,
242           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
243 }
244
245 static void
246 gst_nvdec_init (GstNvDec * nvdec)
247 {
248   nvdec->max_display_delay = DEFAULT_MAX_DISPLAY_DELAY;
249   gst_video_decoder_set_packetized (GST_VIDEO_DECODER (nvdec), TRUE);
250   gst_video_decoder_set_needs_format (GST_VIDEO_DECODER (nvdec), TRUE);
251 }
252
253 static cudaVideoSurfaceFormat
254 get_cuda_surface_format_from_gst (GstVideoFormat format)
255 {
256   switch (format) {
257     case GST_VIDEO_FORMAT_NV12:
258       return cudaVideoSurfaceFormat_NV12;
259     case GST_VIDEO_FORMAT_P010_10LE:
260     case GST_VIDEO_FORMAT_P010_10BE:
261     case GST_VIDEO_FORMAT_P016_LE:
262     case GST_VIDEO_FORMAT_P016_BE:
263       return cudaVideoSurfaceFormat_P016;
264     case GST_VIDEO_FORMAT_Y444:
265       return cudaVideoSurfaceFormat_YUV444;
266     case GST_VIDEO_FORMAT_Y444_16LE:
267     case GST_VIDEO_FORMAT_Y444_16BE:
268       return cudaVideoSurfaceFormat_YUV444_16Bit;
269     default:
270       g_assert_not_reached ();
271       break;
272   }
273
274   return cudaVideoSurfaceFormat_NV12;
275 }
276
277 static guint
278 calculate_num_decode_surface (cudaVideoCodec codec, guint width, guint height)
279 {
280   switch (codec) {
281     case cudaVideoCodec_VP9:
282       return 12;
283     case cudaVideoCodec_H264:
284     case cudaVideoCodec_H264_SVC:
285     case cudaVideoCodec_H264_MVC:
286       return 20;
287     case cudaVideoCodec_HEVC:{
288       gint max_dpb_size;
289       gint MaxLumaPS;
290       const gint MaxDpbPicBuf = 6;
291       gint PicSizeInSamplesY;
292
293       /* A.4.1 */
294       MaxLumaPS = 35651584;
295       PicSizeInSamplesY = width * height;
296       if (PicSizeInSamplesY <= (MaxLumaPS >> 2))
297         max_dpb_size = MaxDpbPicBuf * 4;
298       else if (PicSizeInSamplesY <= (MaxLumaPS >> 1))
299         max_dpb_size = MaxDpbPicBuf * 2;
300       else if (PicSizeInSamplesY <= ((3 * MaxLumaPS) >> 2))
301         max_dpb_size = (MaxDpbPicBuf * 4) / 3;
302       else
303         max_dpb_size = MaxDpbPicBuf;
304
305       max_dpb_size = MIN (max_dpb_size, 16);
306
307       return max_dpb_size + 4;
308     }
309     default:
310       break;
311   }
312
313   return 8;
314 }
315
316 static guint
317 gst_nvdec_get_max_display_delay (GstNvDec * nvdec)
318 {
319   return nvdec->max_display_delay >= 0 ? nvdec->max_display_delay :
320       (nvdec->is_live ? 0 : 4);
321 }
322
323 static gint64
324 gst_nvdec_get_latency (GstNvDec * nvdec)
325 {
326   gint fps_n, fps_d;
327
328   if (!nvdec->input_state)
329     return 0;
330   fps_n = GST_VIDEO_INFO_FPS_N (&nvdec->input_state->info);
331   fps_d = GST_VIDEO_INFO_FPS_D (&nvdec->input_state->info);
332
333   /* We assume 25 fps if the input framerate is invalid */
334   if (fps_n < 1 || fps_d < 1) {
335     fps_n = 25;
336     fps_d = 1;
337   }
338
339   return gst_util_uint64_scale_int ((nvdec->num_decode_surface +
340           gst_nvdec_get_max_display_delay (nvdec)) * GST_SECOND, fps_d, fps_n);
341 }
342
343 /* 0: fail, 1: succeeded, > 1: override dpb size of parser
344  * (set by CUVIDPARSERPARAMS::ulMaxNumDecodeSurfaces while creating parser) */
345 static gint CUDAAPI
346 parser_sequence_callback (GstNvDec * nvdec, CUVIDEOFORMAT * format)
347 {
348   guint width, height;
349   CUVIDDECODECREATEINFO create_info = { 0, };
350   GstVideoFormat out_format;
351   GstVideoInfo *in_info = &nvdec->input_state->info;
352   GstVideoInfo *out_info = &nvdec->out_info;
353   GstVideoInfo prev_out_info = *out_info;
354   GstCudaContext *ctx = nvdec->cuda_ctx;
355   GstStructure *in_s = NULL;
356   gboolean updata = FALSE;
357   guint major_api_ver = 0;
358   guint64 curr_latency, old_latency;
359
360   old_latency = gst_nvdec_get_latency (nvdec);
361   width = format->display_area.right - format->display_area.left;
362   height = format->display_area.bottom - format->display_area.top;
363
364   switch (format->chroma_format) {
365     case cudaVideoChromaFormat_444:
366       if (format->bit_depth_luma_minus8 == 0) {
367         out_format = GST_VIDEO_FORMAT_Y444;
368       } else if (format->bit_depth_luma_minus8 == 2 ||
369           format->bit_depth_luma_minus8 == 4) {
370 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
371         out_format = GST_VIDEO_FORMAT_Y444_16LE;
372 #else
373         out_format = GST_VIDEO_FORMAT_Y444_16BE;
374 #endif
375       } else {
376         GST_ERROR_OBJECT (nvdec, "Unknown 4:4:4 format bitdepth %d",
377             format->bit_depth_luma_minus8 + 8);
378
379         nvdec->last_ret = GST_FLOW_NOT_NEGOTIATED;
380         return 0;
381       }
382       break;
383     case cudaVideoChromaFormat_420:
384       if (format->bit_depth_luma_minus8 == 0) {
385         out_format = GST_VIDEO_FORMAT_NV12;
386       } else if (format->bit_depth_luma_minus8 == 2) {
387 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
388         out_format = GST_VIDEO_FORMAT_P010_10LE;
389 #else
390         out_format = GST_VIDEO_FORMAT_P010_10BE;
391 #endif
392       } else if (format->bit_depth_luma_minus8 == 4) {
393 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
394         out_format = GST_VIDEO_FORMAT_P016_LE;
395 #else
396         out_format = GST_VIDEO_FORMAT_P016_BE;
397 #endif
398       } else {
399         GST_ERROR_OBJECT (nvdec, "Unknown 4:2:0 format bitdepth %d",
400             format->bit_depth_luma_minus8 + 8);
401
402         nvdec->last_ret = GST_FLOW_NOT_NEGOTIATED;
403         return 0;
404       }
405       break;
406     default:
407       GST_ERROR_OBJECT (nvdec, "unhandled chroma format %d, bitdepth %d",
408           format->chroma_format, format->bit_depth_luma_minus8 + 8);
409
410       nvdec->last_ret = GST_FLOW_NOT_NEGOTIATED;
411       return 0;
412   }
413
414   GST_DEBUG_OBJECT (nvdec,
415       "out format: %s", gst_video_format_to_string (out_format));
416
417   GST_DEBUG_OBJECT (nvdec, "width: %u, height: %u", width, height);
418
419   gst_video_info_set_format (out_info, out_format, width, height);
420   GST_VIDEO_INFO_FPS_N (out_info) = GST_VIDEO_INFO_FPS_N (in_info);
421   GST_VIDEO_INFO_FPS_D (out_info) = GST_VIDEO_INFO_FPS_D (in_info);
422
423   if (GST_VIDEO_INFO_FPS_N (out_info) < 1 ||
424       GST_VIDEO_INFO_FPS_D (out_info) < 1) {
425     GST_VIDEO_INFO_FPS_N (out_info) = format->frame_rate.numerator;
426     GST_VIDEO_INFO_FPS_D (out_info) = MAX (1, format->frame_rate.denominator);
427   }
428
429   GST_LOG_OBJECT (nvdec,
430       "Reading colorimetry information full-range %d matrix %d transfer %d primaries %d",
431       format->video_signal_description.video_full_range_flag,
432       format->video_signal_description.matrix_coefficients,
433       format->video_signal_description.transfer_characteristics,
434       format->video_signal_description.color_primaries);
435
436   if (nvdec->input_state->caps)
437     in_s = gst_caps_get_structure (nvdec->input_state->caps, 0);
438
439   /* Set colorimetry when upstream did not provide it */
440   if (in_s && !gst_structure_has_field (in_s, "colorimetry")) {
441     GstVideoColorimetry colorimetry = { 0, };
442
443     if (format->video_signal_description.video_full_range_flag)
444       colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
445     else
446       colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235;
447
448     colorimetry.primaries =
449         gst_video_color_primaries_from_iso
450         (format->video_signal_description.color_primaries);
451
452     colorimetry.transfer =
453         gst_video_transfer_function_from_iso
454         (format->video_signal_description.transfer_characteristics);
455
456     colorimetry.matrix =
457         gst_video_color_matrix_from_iso
458         (format->video_signal_description.matrix_coefficients);
459
460     /* Use a colorimetry having at least one valid colorimetry entry,
461      * because we don't know whether the returned
462      * colorimetry (by nvdec) was actually parsed information or not.
463      * Otherwise let GstVideoInfo handle it with default colorimetry */
464     if (colorimetry.primaries != GST_VIDEO_COLOR_PRIMARIES_UNKNOWN ||
465         colorimetry.transfer != GST_VIDEO_TRANSFER_UNKNOWN ||
466         colorimetry.matrix != GST_VIDEO_COLOR_MATRIX_UNKNOWN) {
467       GST_DEBUG_OBJECT (nvdec,
468           "Found valid colorimetry, update output colorimetry");
469       out_info->colorimetry = colorimetry;
470     }
471   } else {
472     out_info->colorimetry = in_info->colorimetry;
473   }
474
475   if (format->progressive_sequence) {
476     out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
477
478     /* nvdec doesn't seem to deal with interlacing with hevc so rely
479      * on upstream's value */
480     if (format->codec == cudaVideoCodec_HEVC) {
481       out_info->interlace_mode = in_info->interlace_mode;
482     }
483   } else {
484     out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED;
485   }
486
487   if (gst_cuvid_get_api_version (&major_api_ver, NULL) && major_api_ver >= 9) {
488     /* min_num_decode_surfaces was introduced in nvcodec sdk 9.0 header */
489     nvdec->num_decode_surface = format->min_num_decode_surfaces;
490
491     GST_DEBUG_OBJECT (nvdec,
492         "Num decode surface: %d", nvdec->num_decode_surface);
493   } else {
494     nvdec->num_decode_surface =
495         calculate_num_decode_surface (format->codec, width, height);
496
497     GST_DEBUG_OBJECT (nvdec,
498         "Calculated num decode surface: %d", nvdec->num_decode_surface);
499   }
500
501   /* Update the latency if it has changed */
502   curr_latency = gst_nvdec_get_latency (nvdec);
503   if (old_latency != curr_latency)
504     gst_video_decoder_set_latency (GST_VIDEO_DECODER (nvdec), curr_latency,
505         curr_latency);
506
507   if (!nvdec->decoder || !gst_video_info_is_equal (out_info, &prev_out_info)) {
508     updata = TRUE;
509
510     if (!gst_cuda_context_push (ctx)) {
511       GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
512       goto error;
513     }
514
515     if (nvdec->decoder) {
516       GST_DEBUG_OBJECT (nvdec, "destroying decoder");
517       if (!gst_cuda_result (CuvidDestroyDecoder (nvdec->decoder))) {
518         GST_ERROR_OBJECT (nvdec, "failed to destroy decoder");
519         goto error;
520       } else
521         nvdec->decoder = NULL;
522     }
523
524     GST_DEBUG_OBJECT (nvdec, "creating decoder");
525     create_info.ulWidth = width;
526     create_info.ulHeight = height;
527     create_info.ulNumDecodeSurfaces = nvdec->num_decode_surface;
528     create_info.CodecType = format->codec;
529     create_info.ChromaFormat = format->chroma_format;
530     create_info.ulCreationFlags = cudaVideoCreate_Default;
531     create_info.display_area.left = format->display_area.left;
532     create_info.display_area.top = format->display_area.top;
533     create_info.display_area.right = format->display_area.right;
534     create_info.display_area.bottom = format->display_area.bottom;
535     create_info.OutputFormat = get_cuda_surface_format_from_gst (out_format);
536     create_info.bitDepthMinus8 = format->bit_depth_luma_minus8;
537     create_info.DeinterlaceMode = cudaVideoDeinterlaceMode_Weave;
538     create_info.ulTargetWidth = width;
539     create_info.ulTargetHeight = height;
540     create_info.ulNumOutputSurfaces = 1;
541     create_info.target_rect.left = 0;
542     create_info.target_rect.top = 0;
543     create_info.target_rect.right = width;
544     create_info.target_rect.bottom = height;
545
546     if (nvdec->decoder
547         || !gst_cuda_result (CuvidCreateDecoder (&nvdec->decoder,
548                 &create_info))) {
549       GST_ERROR_OBJECT (nvdec, "failed to create decoder");
550       goto error;
551     }
552
553     if (!gst_cuda_context_pop (NULL)) {
554       GST_ERROR_OBJECT (nvdec, "failed to unlock CUDA context");
555       goto error;
556     }
557   }
558
559   if (!gst_pad_has_current_caps (GST_VIDEO_DECODER_SRC_PAD (nvdec)) || updata) {
560     if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (nvdec))) {
561       nvdec->last_ret = GST_FLOW_NOT_NEGOTIATED;
562       return 0;
563     }
564   }
565
566   return nvdec->num_decode_surface;
567
568 error:
569   nvdec->last_ret = GST_FLOW_ERROR;
570   return 0;
571 }
572
573 static gboolean
574 gst_nvdec_negotiate (GstVideoDecoder * decoder)
575 {
576   GstNvDec *nvdec = GST_NVDEC (decoder);
577   GstVideoCodecState *state;
578   GstVideoInfo *vinfo;
579   GstVideoInfo *out_info = &nvdec->out_info;
580   gboolean ret;
581
582   GST_DEBUG_OBJECT (nvdec, "negotiate");
583
584   state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (nvdec),
585       GST_VIDEO_INFO_FORMAT (out_info), GST_VIDEO_INFO_WIDTH (out_info),
586       GST_VIDEO_INFO_HEIGHT (out_info), nvdec->input_state);
587   vinfo = &state->info;
588
589   /* update output info with CUvidparser provided one */
590   vinfo->interlace_mode = out_info->interlace_mode;
591   vinfo->fps_n = out_info->fps_n;
592   vinfo->fps_d = out_info->fps_d;
593
594   state->caps = gst_video_info_to_caps (&state->info);
595   nvdec->mem_type = GST_NVDEC_MEM_TYPE_SYSTEM;
596
597   {
598     GstCaps *caps;
599     caps = gst_pad_get_allowed_caps (GST_VIDEO_DECODER_SRC_PAD (nvdec));
600     GST_DEBUG_OBJECT (nvdec, "Allowed caps %" GST_PTR_FORMAT, caps);
601
602     if (!caps || gst_caps_is_any (caps)) {
603       GST_DEBUG_OBJECT (nvdec,
604           "cannot determine output format, use system memory");
605     } else {
606       GstCapsFeatures *features;
607       guint size = gst_caps_get_size (caps);
608       guint i;
609       gboolean have_cuda = FALSE;
610       gboolean have_gl = FALSE;
611
612       for (i = 0; i < size; i++) {
613         features = gst_caps_get_features (caps, i);
614         if (features && gst_caps_features_contains (features,
615                 GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY)) {
616           GST_DEBUG_OBJECT (nvdec, "found CUDA memory feature");
617           have_cuda = TRUE;
618           break;
619         }
620 #ifdef HAVE_NVCODEC_GST_GL
621         if (nvdec->gl_display &&
622             features && gst_caps_features_contains (features,
623                 GST_CAPS_FEATURE_MEMORY_GL_MEMORY)) {
624           GST_DEBUG_OBJECT (nvdec, "found GL memory feature");
625           have_gl = TRUE;
626         }
627 #endif
628       }
629
630       if (have_cuda)
631         nvdec->mem_type = GST_NVDEC_MEM_TYPE_CUDA;
632       else if (have_gl)
633         nvdec->mem_type = GST_NVDEC_MEM_TYPE_GL;
634     }
635     gst_clear_caps (&caps);
636   }
637
638 #ifdef HAVE_NVCODEC_GST_GL
639   if (nvdec->mem_type == GST_NVDEC_MEM_TYPE_GL &&
640       !gst_nvdec_ensure_gl_context (nvdec)) {
641     GST_WARNING_OBJECT (nvdec,
642         "OpenGL context is not CUDA-compatible, fallback to system memory");
643     nvdec->mem_type = GST_NVDEC_MEM_TYPE_SYSTEM;
644   }
645 #endif
646
647   switch (nvdec->mem_type) {
648     case GST_NVDEC_MEM_TYPE_CUDA:
649       GST_DEBUG_OBJECT (nvdec, "use cuda memory");
650       gst_caps_set_features (state->caps, 0,
651           gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, NULL));
652       break;
653 #ifdef HAVE_NVCODEC_GST_GL
654     case GST_NVDEC_MEM_TYPE_GL:
655       GST_DEBUG_OBJECT (nvdec, "use gl memory");
656       gst_caps_set_features (state->caps, 0,
657           gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, NULL));
658       gst_caps_set_simple (state->caps, "texture-target", G_TYPE_STRING,
659           "2D", NULL);
660       break;
661 #endif
662     default:
663       GST_DEBUG_OBJECT (nvdec, "use system memory");
664       break;
665   }
666
667   if (nvdec->output_state)
668     gst_video_codec_state_unref (nvdec->output_state);
669
670   nvdec->output_state = state;
671
672   ret = GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
673
674   if (!ret) {
675     GST_ERROR_OBJECT (nvdec, "failed to negotiate with downstream");
676     nvdec->last_ret = GST_FLOW_NOT_NEGOTIATED;
677   }
678
679   return ret;
680 }
681
682 static gboolean CUDAAPI
683 parser_decode_callback (GstNvDec * nvdec, CUVIDPICPARAMS * params)
684 {
685   GList *iter, *pending_frames;
686   GstCudaContext *ctx = nvdec->cuda_ctx;
687
688   GST_LOG_OBJECT (nvdec, "picture index: %u", params->CurrPicIdx);
689
690   if (!gst_cuda_context_push (ctx)) {
691     GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
692     goto error;
693   }
694
695   if (!gst_cuda_result (CuvidDecodePicture (nvdec->decoder, params))) {
696     GST_ERROR_OBJECT (nvdec, "failed to decode picture");
697     goto error;
698   }
699
700   if (!gst_cuda_context_pop (NULL)) {
701     GST_ERROR_OBJECT (nvdec, "failed to unlock CUDA context");
702     goto error;
703   }
704
705   pending_frames = gst_video_decoder_get_frames (GST_VIDEO_DECODER (nvdec));
706
707   /* NOTE: this decode callback could be invoked multiple times for
708    * one cuvidParseVideoData() call. Most likely it can be related to "decode only"
709    * frame of VPX codec but no document available.
710    * In that case, the last decoded frame seems to be displayed */
711
712   for (iter = pending_frames; iter; iter = g_list_next (iter)) {
713     guint id;
714     GstVideoCodecFrame *frame = (GstVideoCodecFrame *) iter->data;
715     gboolean set_data = FALSE;
716
717     id = GPOINTER_TO_UINT (gst_video_codec_frame_get_user_data (frame));
718     if (G_UNLIKELY (nvdec->state == GST_NVDEC_STATE_DECODE)) {
719       if (id) {
720         GST_LOG_OBJECT (nvdec, "reset the last user data");
721         set_data = TRUE;
722       }
723     } else if (!id) {
724       set_data = TRUE;
725     }
726
727     if (set_data) {
728       gst_video_codec_frame_set_user_data (frame,
729           GUINT_TO_POINTER (params->CurrPicIdx + 1), NULL);
730       break;
731     }
732   }
733
734   nvdec->state = GST_NVDEC_STATE_DECODE;
735
736   g_list_free_full (pending_frames,
737       (GDestroyNotify) gst_video_codec_frame_unref);
738
739   return TRUE;
740
741 error:
742   nvdec->last_ret = GST_FLOW_ERROR;
743   return FALSE;
744 }
745
746 static gboolean CUDAAPI
747 parser_display_callback (GstNvDec * nvdec, CUVIDPARSERDISPINFO * dispinfo)
748 {
749   GList *iter, *pending_frames;
750   GstVideoCodecFrame *frame = NULL;
751   GstBuffer *output_buffer = NULL;
752   GstFlowReturn ret = GST_FLOW_OK;
753   gboolean copy_ret = FALSE;
754
755   GST_LOG_OBJECT (nvdec, "picture index: %u", dispinfo->picture_index);
756
757   pending_frames = gst_video_decoder_get_frames (GST_VIDEO_DECODER (nvdec));
758   for (iter = pending_frames; iter; iter = g_list_next (iter)) {
759     guint id;
760     GstVideoCodecFrame *tmp = (GstVideoCodecFrame *) iter->data;
761
762     id = GPOINTER_TO_UINT (gst_video_codec_frame_get_user_data (tmp));
763     if (id == dispinfo->picture_index + 1) {
764       frame = gst_video_codec_frame_ref (tmp);
765       break;
766     }
767   }
768   g_list_free_full (pending_frames,
769       (GDestroyNotify) gst_video_codec_frame_unref);
770
771   if (G_UNLIKELY (frame == NULL)) {
772     GST_WARNING_OBJECT (nvdec, "no frame for picture index %u",
773         dispinfo->picture_index);
774
775     output_buffer =
776         gst_video_decoder_allocate_output_buffer (GST_VIDEO_DECODER (nvdec));
777
778     if (!output_buffer) {
779       GST_ERROR_OBJECT (nvdec, "Couldn't allocate output buffer");
780       nvdec->last_ret = GST_FLOW_ERROR;
781       return FALSE;
782     }
783
784     GST_BUFFER_PTS (output_buffer) = dispinfo->timestamp;
785     GST_BUFFER_DTS (output_buffer) = GST_CLOCK_TIME_NONE;
786     /* assume buffer duration from framerate */
787     GST_BUFFER_DURATION (output_buffer) =
788         gst_util_uint64_scale (GST_SECOND,
789         GST_VIDEO_INFO_FPS_D (&nvdec->out_info),
790         GST_VIDEO_INFO_FPS_N (&nvdec->out_info));
791   } else {
792     ret = gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (nvdec),
793         frame);
794
795     if (ret != GST_FLOW_OK) {
796       GST_WARNING_OBJECT (nvdec, "failed to allocate output frame");
797       nvdec->last_ret = ret;
798       return FALSE;
799     }
800
801     output_buffer = frame->output_buffer;
802
803     if (dispinfo->timestamp != frame->pts) {
804       GST_INFO_OBJECT (nvdec,
805           "timestamp mismatch, diff: %" GST_STIME_FORMAT,
806           GST_STIME_ARGS (GST_CLOCK_DIFF (dispinfo->timestamp, frame->pts)));
807     }
808   }
809
810 #ifdef HAVE_NVCODEC_GST_GL
811   if (nvdec->mem_type == GST_NVDEC_MEM_TYPE_GL) {
812     copy_ret = gst_nvdec_copy_device_to_gl (nvdec, dispinfo, output_buffer);
813
814     /* FIXME: This is the case where OpenGL context of downstream glbufferpool
815      * belongs to non-nvidia (or different device).
816      * There should be enhancement to ensure nvdec has compatible OpenGL context
817      */
818     if (!copy_ret) {
819       GST_WARNING_OBJECT (nvdec,
820           "Couldn't copy frame to GL memory, fallback to system memory");
821       nvdec->mem_type = GST_NVDEC_MEM_TYPE_SYSTEM;
822     }
823   }
824
825   if (!copy_ret)
826 #endif
827   {
828     copy_ret = gst_nvdec_copy_device_to_memory (nvdec, dispinfo, output_buffer);
829   }
830
831   if (!copy_ret) {
832     GST_ERROR_OBJECT (nvdec, "failed to copy decoded picture to output buffer");
833     nvdec->last_ret = GST_FLOW_ERROR;
834
835     if (frame)
836       gst_video_decoder_drop_frame (GST_VIDEO_DECODER (nvdec), frame);
837     else
838       gst_buffer_unref (output_buffer);
839
840     return FALSE;
841   }
842
843   if (!dispinfo->progressive_frame) {
844     GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED);
845
846     if (dispinfo->top_field_first) {
847       GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_TFF);
848     }
849
850     if (dispinfo->repeat_first_field == -1) {
851       GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_ONEFIELD);
852     } else {
853       GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_RFF);
854     }
855   }
856
857   if (frame) {
858     ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (nvdec), frame);
859   } else {
860     ret = gst_pad_push (GST_VIDEO_DECODER_SRC_PAD (nvdec), output_buffer);
861   }
862
863   if (ret != GST_FLOW_OK) {
864     GST_DEBUG_OBJECT (nvdec, "failed to finish frame %s",
865         gst_flow_get_name (ret));
866     nvdec->last_ret = ret;
867     return FALSE;
868   }
869
870   return TRUE;
871 }
872
873 static gboolean
874 gst_nvdec_open (GstVideoDecoder * decoder)
875 {
876   GstNvDec *nvdec = GST_NVDEC (decoder);
877   GstNvDecClass *klass = GST_NVDEC_GET_CLASS (nvdec);
878   CUresult cuda_ret;
879
880   GST_DEBUG_OBJECT (nvdec, "creating CUDA context");
881
882   if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (decoder),
883           klass->cuda_device_id, &nvdec->cuda_ctx)) {
884     GST_ERROR_OBJECT (nvdec, "failed to create CUDA context");
885     return FALSE;
886   }
887
888   if (gst_cuda_context_push (nvdec->cuda_ctx)) {
889     cuda_ret = CuStreamCreate (&nvdec->cuda_stream, CU_STREAM_DEFAULT);
890     if (!gst_cuda_result (cuda_ret)) {
891       GST_WARNING_OBJECT (nvdec,
892           "Could not create CUDA stream, will use default stream");
893       nvdec->cuda_stream = NULL;
894     }
895     gst_cuda_context_pop (NULL);
896   }
897 #if HAVE_NVCODEC_GST_GL
898   gst_gl_ensure_element_data (GST_ELEMENT (nvdec),
899       &nvdec->gl_display, &nvdec->other_gl_context);
900   if (nvdec->gl_display)
901     gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvdec->gl_display),
902         SUPPORTED_GL_APIS);
903 #endif
904
905   return TRUE;
906 }
907
908 static gboolean
909 gst_nvdec_start (GstVideoDecoder * decoder)
910 {
911   GstNvDec *nvdec = GST_NVDEC (decoder);
912
913   nvdec->state = GST_NVDEC_STATE_INIT;
914   nvdec->last_ret = GST_FLOW_OK;
915   gst_video_info_init (&nvdec->out_info);
916
917   return TRUE;
918 }
919
920 static gboolean
921 maybe_destroy_decoder_and_parser (GstNvDec * nvdec)
922 {
923   gboolean ret = TRUE;
924
925   if (!gst_cuda_context_push (nvdec->cuda_ctx)) {
926     GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
927     return FALSE;
928   }
929
930   if (nvdec->decoder) {
931     GST_DEBUG_OBJECT (nvdec, "destroying decoder");
932     ret = gst_cuda_result (CuvidDestroyDecoder (nvdec->decoder));
933     nvdec->decoder = NULL;
934
935     if (!ret)
936       GST_ERROR_OBJECT (nvdec, "failed to destroy decoder");
937   }
938
939   if (nvdec->parser) {
940     GST_DEBUG_OBJECT (nvdec, "destroying parser");
941     if (!gst_cuda_result (CuvidDestroyVideoParser (nvdec->parser))) {
942       GST_ERROR_OBJECT (nvdec, "failed to destroy parser");
943       ret = FALSE;
944     }
945     nvdec->parser = NULL;
946   }
947
948   if (!gst_cuda_context_pop (NULL)) {
949     GST_WARNING_OBJECT (nvdec, "failed to pop CUDA context");
950   }
951
952   return ret;
953 }
954
955 static gboolean
956 gst_nvdec_stop (GstVideoDecoder * decoder)
957 {
958   GstNvDec *nvdec = GST_NVDEC (decoder);
959
960   GST_DEBUG_OBJECT (nvdec, "stop");
961
962   if (!maybe_destroy_decoder_and_parser (nvdec))
963     return FALSE;
964
965 #ifdef HAVE_NVCODEC_GST_GL
966   if (nvdec->gl_context) {
967     gst_object_unref (nvdec->gl_context);
968     nvdec->gl_context = NULL;
969   }
970
971   if (nvdec->other_gl_context) {
972     gst_object_unref (nvdec->other_gl_context);
973     nvdec->other_gl_context = NULL;
974   }
975
976   if (nvdec->gl_display) {
977     gst_object_unref (nvdec->gl_display);
978     nvdec->gl_display = NULL;
979   }
980 #endif
981
982   if (nvdec->input_state) {
983     gst_video_codec_state_unref (nvdec->input_state);
984     nvdec->input_state = NULL;
985   }
986
987   if (nvdec->output_state) {
988     gst_video_codec_state_unref (nvdec->output_state);
989     nvdec->output_state = NULL;
990   }
991
992   gst_clear_buffer (&nvdec->codec_data);
993
994   return TRUE;
995 }
996
997 static gboolean
998 gst_nvdec_close (GstVideoDecoder * decoder)
999 {
1000   GstNvDec *nvdec = GST_NVDEC (decoder);
1001
1002   if (nvdec->cuda_ctx && nvdec->cuda_stream) {
1003     if (gst_cuda_context_push (nvdec->cuda_ctx)) {
1004       gst_cuda_result (CuStreamDestroy (nvdec->cuda_stream));
1005       gst_cuda_context_pop (NULL);
1006     }
1007   }
1008
1009   gst_clear_object (&nvdec->cuda_ctx);
1010   nvdec->cuda_stream = NULL;
1011
1012   return TRUE;
1013 }
1014
1015 static gboolean
1016 gst_nvdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
1017 {
1018   GstNvDec *nvdec = GST_NVDEC (decoder);
1019   GstNvDecClass *klass = GST_NVDEC_GET_CLASS (decoder);
1020   CUVIDPARSERPARAMS parser_params = { 0, };
1021   GstQuery *query;
1022   gboolean ret = TRUE;
1023
1024   GST_DEBUG_OBJECT (nvdec, "set format");
1025
1026   if (nvdec->input_state)
1027     gst_video_codec_state_unref (nvdec->input_state);
1028
1029   nvdec->input_state = gst_video_codec_state_ref (state);
1030
1031   if (!maybe_destroy_decoder_and_parser (nvdec))
1032     return FALSE;
1033
1034   /* Check if pipeline is live */
1035   nvdec->is_live = FALSE;
1036   query = gst_query_new_latency ();
1037   if (gst_pad_peer_query (GST_VIDEO_DECODER_SINK_PAD (decoder), query))
1038     gst_query_parse_latency (query, &nvdec->is_live, NULL, NULL);
1039   gst_query_unref (query);
1040
1041   parser_params.CodecType = klass->codec_type;
1042   /* ulMaxNumDecodeSurfaces will be updated by the return value of
1043    * SequenceCallback */
1044   parser_params.ulMaxNumDecodeSurfaces = 1;
1045   parser_params.ulErrorThreshold = 100;
1046   parser_params.ulMaxDisplayDelay = gst_nvdec_get_max_display_delay (nvdec);
1047   parser_params.ulClockRate = GST_SECOND;
1048   parser_params.pUserData = nvdec;
1049   parser_params.pfnSequenceCallback =
1050       (PFNVIDSEQUENCECALLBACK) parser_sequence_callback;
1051   parser_params.pfnDecodePicture =
1052       (PFNVIDDECODECALLBACK) parser_decode_callback;
1053   parser_params.pfnDisplayPicture =
1054       (PFNVIDDISPLAYCALLBACK) parser_display_callback;
1055
1056   gst_cuda_context_push (nvdec->cuda_ctx);
1057   GST_DEBUG_OBJECT (nvdec, "creating parser");
1058   if (!gst_cuda_result (CuvidCreateVideoParser (&nvdec->parser,
1059               &parser_params))) {
1060     GST_ERROR_OBJECT (nvdec, "failed to create parser");
1061     ret = FALSE;
1062   }
1063
1064   gst_cuda_context_pop (NULL);
1065
1066   /* store codec data */
1067   if (ret && nvdec->input_state->caps) {
1068     const GValue *codec_data_value;
1069     GstStructure *str;
1070
1071     str = gst_caps_get_structure (nvdec->input_state->caps, 0);
1072     codec_data_value = gst_structure_get_value (str, "codec_data");
1073     if (codec_data_value && GST_VALUE_HOLDS_BUFFER (codec_data_value)) {
1074       GstBuffer *codec_data = gst_value_get_buffer (codec_data_value);
1075       gst_buffer_replace (&nvdec->codec_data, codec_data);
1076     }
1077
1078     /* For all CODEC we get complete picture ... */
1079     nvdec->recv_complete_picture = TRUE;
1080
1081     /* Except for JPEG, for which it depends on the caps */
1082     if (klass->codec_type == cudaVideoCodec_JPEG) {
1083       gboolean parsed;
1084       if (gst_structure_get_boolean (str, "parsed", &parsed))
1085         nvdec->recv_complete_picture = parsed;
1086       else
1087         nvdec->recv_complete_picture = FALSE;
1088     }
1089   }
1090
1091   return ret;
1092 }
1093
1094 #ifdef HAVE_NVCODEC_GST_GL
1095 typedef struct
1096 {
1097   GstNvDec *nvdec;
1098   CUVIDPARSERDISPINFO *dispinfo;
1099   gboolean ret;
1100   GstBuffer *output_buffer;
1101 } GstNvDecCopyToGLData;
1102
1103 static void
1104 copy_video_frame_to_gl_textures (GstGLContext * context,
1105     GstNvDecCopyToGLData * data)
1106 {
1107   GstNvDec *nvdec = data->nvdec;
1108   CUVIDPARSERDISPINFO *dispinfo = data->dispinfo;
1109   GstCudaGraphicsResource **resources;
1110   guint num_resources;
1111   CUVIDPROCPARAMS proc_params = { 0, };
1112   guintptr dptr;
1113   guint pitch, i;
1114   CUDA_MEMCPY2D mcpy2d = { 0, };
1115   GstVideoInfo *info = &nvdec->output_state->info;
1116
1117   GST_LOG_OBJECT (nvdec, "picture index: %u", dispinfo->picture_index);
1118
1119   proc_params.progressive_frame = dispinfo->progressive_frame;
1120   proc_params.top_field_first = dispinfo->top_field_first;
1121   proc_params.unpaired_field = dispinfo->repeat_first_field == -1;
1122
1123   data->ret = TRUE;
1124
1125   num_resources = gst_buffer_n_memory (data->output_buffer);
1126   resources = g_newa (GstCudaGraphicsResource *, num_resources);
1127
1128   for (i = 0; i < num_resources; i++) {
1129     GstMemory *mem;
1130
1131     mem = gst_buffer_peek_memory (data->output_buffer, i);
1132     resources[i] = ensure_cuda_graphics_resource (mem, nvdec);
1133     if (!resources[i]) {
1134       GST_WARNING_OBJECT (nvdec, "could not register %dth memory", i);
1135       data->ret = FALSE;
1136
1137       return;
1138     }
1139
1140     /* Need PBO -> texture */
1141     GST_MINI_OBJECT_FLAG_SET (mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD);
1142   }
1143
1144   if (!gst_cuda_context_push (nvdec->cuda_ctx)) {
1145     GST_WARNING_OBJECT (nvdec, "failed to lock CUDA context");
1146     data->ret = FALSE;
1147     return;
1148   }
1149
1150   if (!gst_cuda_result (CuvidMapVideoFrame (nvdec->decoder,
1151               dispinfo->picture_index, &dptr, &pitch, &proc_params))) {
1152     GST_WARNING_OBJECT (nvdec, "failed to map CUDA video frame");
1153     data->ret = FALSE;
1154     goto unlock_cuda_context;
1155   }
1156
1157   mcpy2d.srcMemoryType = CU_MEMORYTYPE_DEVICE;
1158   mcpy2d.srcPitch = pitch;
1159   mcpy2d.dstMemoryType = CU_MEMORYTYPE_DEVICE;
1160
1161   for (i = 0; i < num_resources; i++) {
1162     CUdeviceptr cuda_ptr;
1163     gsize size;
1164     CUgraphicsResource cuda_resource =
1165         gst_cuda_graphics_resource_map (resources[i], nvdec->cuda_stream,
1166         CU_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD);
1167
1168     if (!cuda_resource) {
1169       GST_WARNING_OBJECT (nvdec, "failed to map CUDA resources");
1170       data->ret = FALSE;
1171       goto unmap_video_frame;
1172     }
1173
1174     if (!gst_cuda_result (CuGraphicsResourceGetMappedPointer (&cuda_ptr, &size,
1175                 cuda_resource))) {
1176       GST_WARNING_OBJECT (nvdec, "failed to map CUDA resource");
1177       data->ret = FALSE;
1178       break;
1179     }
1180
1181     mcpy2d.dstPitch = GST_VIDEO_INFO_PLANE_STRIDE (info, i);
1182     mcpy2d.WidthInBytes = GST_VIDEO_INFO_COMP_WIDTH (info, i)
1183         * GST_VIDEO_INFO_COMP_PSTRIDE (info, i);
1184
1185     mcpy2d.srcDevice = dptr + (i * pitch * GST_VIDEO_INFO_HEIGHT (info));
1186     mcpy2d.dstDevice = cuda_ptr;
1187     mcpy2d.Height = GST_VIDEO_INFO_COMP_HEIGHT (info, i);
1188
1189     if (!gst_cuda_result (CuMemcpy2DAsync (&mcpy2d, nvdec->cuda_stream))) {
1190       GST_WARNING_OBJECT (nvdec, "memcpy to mapped array failed");
1191       data->ret = FALSE;
1192     }
1193   }
1194
1195   gst_cuda_result (CuStreamSynchronize (nvdec->cuda_stream));
1196
1197 unmap_video_frame:
1198   for (i = 0; i < num_resources; i++) {
1199     gst_cuda_graphics_resource_unmap (resources[i], nvdec->cuda_stream);
1200   }
1201
1202   if (!gst_cuda_result (CuvidUnmapVideoFrame (nvdec->decoder, dptr)))
1203     GST_WARNING_OBJECT (nvdec, "failed to unmap CUDA video frame");
1204
1205 unlock_cuda_context:
1206   if (!gst_cuda_context_pop (NULL))
1207     GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
1208 }
1209
1210 static gboolean
1211 gst_nvdec_copy_device_to_gl (GstNvDec * nvdec,
1212     CUVIDPARSERDISPINFO * dispinfo, GstBuffer * output_buffer)
1213 {
1214   GstNvDecCopyToGLData data = { 0, };
1215
1216   data.nvdec = nvdec;
1217   data.dispinfo = dispinfo;
1218   data.output_buffer = output_buffer;
1219
1220   gst_gl_context_thread_add (nvdec->gl_context,
1221       (GstGLContextThreadFunc) copy_video_frame_to_gl_textures, &data);
1222
1223   return data.ret;
1224 }
1225 #endif
1226
1227 static gboolean
1228 gst_nvdec_copy_device_to_memory (GstNvDec * nvdec,
1229     CUVIDPARSERDISPINFO * dispinfo, GstBuffer * output_buffer)
1230 {
1231   CUVIDPROCPARAMS params = { 0, };
1232   CUDA_MEMCPY2D copy_params = { 0, };
1233   guintptr dptr;
1234   guint pitch;
1235   GstVideoFrame video_frame;
1236   GstVideoInfo *info = &nvdec->output_state->info;
1237   gint i;
1238   GstMemory *mem;
1239   GstCudaMemory *cuda_mem = NULL;
1240
1241   if (!gst_cuda_context_push (nvdec->cuda_ctx)) {
1242     GST_WARNING_OBJECT (nvdec, "failed to lock CUDA context");
1243     return FALSE;
1244   }
1245
1246   if (nvdec->mem_type == GST_NVDEC_MEM_TYPE_CUDA &&
1247       (mem = gst_buffer_peek_memory (output_buffer, 0)) &&
1248       gst_is_cuda_memory (mem)) {
1249     GstCudaMemory *cmem = GST_CUDA_MEMORY_CAST (mem);
1250
1251     if (cmem->context == nvdec->cuda_ctx ||
1252         gst_cuda_context_get_handle (cmem->context) ==
1253         gst_cuda_context_get_handle (nvdec->cuda_ctx) ||
1254         (gst_cuda_context_can_access_peer (cmem->context, nvdec->cuda_ctx) &&
1255             gst_cuda_context_can_access_peer (nvdec->cuda_ctx,
1256                 cmem->context))) {
1257       cuda_mem = cmem;
1258     }
1259   }
1260
1261   if (!cuda_mem) {
1262     if (!gst_video_frame_map (&video_frame, info, output_buffer, GST_MAP_WRITE)) {
1263       GST_ERROR_OBJECT (nvdec, "frame map failure");
1264       gst_cuda_context_pop (NULL);
1265       return FALSE;
1266     }
1267   }
1268
1269   params.progressive_frame = dispinfo->progressive_frame;
1270   params.second_field = dispinfo->repeat_first_field + 1;
1271   params.top_field_first = dispinfo->top_field_first;
1272   params.unpaired_field = dispinfo->repeat_first_field < 0;
1273
1274   if (!gst_cuda_result (CuvidMapVideoFrame (nvdec->decoder,
1275               dispinfo->picture_index, &dptr, &pitch, &params))) {
1276     GST_ERROR_OBJECT (nvdec, "failed to map video frame");
1277     gst_cuda_context_pop (NULL);
1278     return FALSE;
1279   }
1280
1281   copy_params.srcMemoryType = CU_MEMORYTYPE_DEVICE;
1282   copy_params.srcPitch = pitch;
1283   copy_params.dstMemoryType =
1284       cuda_mem ? CU_MEMORYTYPE_DEVICE : CU_MEMORYTYPE_HOST;
1285
1286   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
1287     copy_params.srcDevice = dptr + (i * pitch * GST_VIDEO_INFO_HEIGHT (info));
1288     if (cuda_mem) {
1289       copy_params.dstDevice = cuda_mem->data + cuda_mem->offset[i];
1290       copy_params.dstPitch = cuda_mem->stride;
1291     } else {
1292       copy_params.dstHost = GST_VIDEO_FRAME_PLANE_DATA (&video_frame, i);
1293       copy_params.dstPitch = GST_VIDEO_FRAME_PLANE_STRIDE (&video_frame, i);
1294     }
1295     copy_params.WidthInBytes = GST_VIDEO_INFO_COMP_WIDTH (info, i)
1296         * GST_VIDEO_INFO_COMP_PSTRIDE (info, i);
1297     copy_params.Height = GST_VIDEO_INFO_COMP_HEIGHT (info, i);
1298
1299     if (!gst_cuda_result (CuMemcpy2DAsync (&copy_params, nvdec->cuda_stream))) {
1300       GST_ERROR_OBJECT (nvdec, "failed to copy %dth plane", i);
1301       CuvidUnmapVideoFrame (nvdec->decoder, dptr);
1302       if (!cuda_mem)
1303         gst_video_frame_unmap (&video_frame);
1304       gst_cuda_context_pop (NULL);
1305       return FALSE;
1306     }
1307   }
1308
1309   gst_cuda_result (CuStreamSynchronize (nvdec->cuda_stream));
1310
1311   if (!cuda_mem)
1312     gst_video_frame_unmap (&video_frame);
1313
1314   if (!gst_cuda_result (CuvidUnmapVideoFrame (nvdec->decoder, dptr)))
1315     GST_WARNING_OBJECT (nvdec, "failed to unmap video frame");
1316
1317   if (!gst_cuda_context_pop (NULL))
1318     GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
1319
1320   return TRUE;
1321 }
1322
1323 static GstFlowReturn
1324 gst_nvdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
1325 {
1326   GstNvDec *nvdec = GST_NVDEC (decoder);
1327   GstNvDecClass *klass = GST_NVDEC_GET_CLASS (nvdec);
1328   GstMapInfo map_info = GST_MAP_INFO_INIT;
1329   CUVIDSOURCEDATAPACKET packet = { 0, };
1330   GstBuffer *in_buffer;
1331
1332   GST_LOG_OBJECT (nvdec, "handle frame");
1333
1334   /* initialize with zero to keep track of frames */
1335   gst_video_codec_frame_set_user_data (frame, GUINT_TO_POINTER (0), NULL);
1336
1337   in_buffer = gst_buffer_ref (frame->input_buffer);
1338   if (GST_BUFFER_IS_DISCONT (frame->input_buffer)) {
1339     if (nvdec->codec_data && klass->codec_type == cudaVideoCodec_MPEG4) {
1340       in_buffer = gst_buffer_append (gst_buffer_ref (nvdec->codec_data),
1341           in_buffer);
1342     }
1343   }
1344
1345   if (!gst_buffer_map (in_buffer, &map_info, GST_MAP_READ)) {
1346     GST_ERROR_OBJECT (nvdec, "failed to map input buffer");
1347     gst_buffer_unref (in_buffer);
1348     gst_video_codec_frame_unref (frame);
1349     return GST_FLOW_ERROR;
1350   }
1351
1352   packet.payload_size = (gulong) map_info.size;
1353   packet.payload = map_info.data;
1354   packet.timestamp = frame->pts;
1355   packet.flags |= CUVID_PKT_TIMESTAMP;
1356
1357   if (nvdec->recv_complete_picture)
1358     packet.flags |= CUVID_PKT_ENDOFPICTURE;
1359
1360   nvdec->state = GST_NVDEC_STATE_PARSE;
1361   nvdec->last_ret = GST_FLOW_OK;
1362
1363   if (!gst_cuda_result (CuvidParseVideoData (nvdec->parser, &packet)))
1364     GST_WARNING_OBJECT (nvdec, "parser failed");
1365
1366   gst_buffer_unmap (in_buffer, &map_info);
1367   gst_buffer_unref (in_buffer);
1368   gst_video_codec_frame_unref (frame);
1369
1370   return nvdec->last_ret;
1371 }
1372
1373 static gboolean
1374 gst_nvdec_flush (GstVideoDecoder * decoder)
1375 {
1376   GstNvDec *nvdec = GST_NVDEC (decoder);
1377   CUVIDSOURCEDATAPACKET packet = { 0, };
1378
1379   GST_DEBUG_OBJECT (nvdec, "flush");
1380
1381   packet.payload_size = 0;
1382   packet.payload = NULL;
1383   packet.flags = CUVID_PKT_ENDOFSTREAM;
1384
1385   nvdec->state = GST_NVDEC_STATE_PARSE;
1386   nvdec->last_ret = GST_FLOW_OK;
1387
1388   if (nvdec->parser
1389       && !gst_cuda_result (CuvidParseVideoData (nvdec->parser, &packet)))
1390     GST_WARNING_OBJECT (nvdec, "parser failed");
1391
1392   return TRUE;
1393 }
1394
1395 static GstFlowReturn
1396 gst_nvdec_drain (GstVideoDecoder * decoder)
1397 {
1398   GstNvDec *nvdec = GST_NVDEC (decoder);
1399   CUVIDSOURCEDATAPACKET packet = { 0, };
1400
1401   GST_DEBUG_OBJECT (nvdec, "draining decoder");
1402
1403   packet.payload_size = 0;
1404   packet.payload = NULL;
1405   packet.flags = CUVID_PKT_ENDOFSTREAM;
1406
1407   nvdec->state = GST_NVDEC_STATE_PARSE;
1408   nvdec->last_ret = GST_FLOW_OK;
1409
1410   if (nvdec->parser
1411       && !gst_cuda_result (CuvidParseVideoData (nvdec->parser, &packet)))
1412     GST_WARNING_OBJECT (nvdec, "parser failed");
1413
1414   return nvdec->last_ret;
1415 }
1416
1417 static GstFlowReturn
1418 gst_nvdec_finish (GstVideoDecoder * decoder)
1419 {
1420   GST_DEBUG_OBJECT (decoder, "finish");
1421
1422   return gst_nvdec_drain (decoder);
1423 }
1424
1425 #ifdef HAVE_NVCODEC_GST_GL
1426 static void
1427 gst_nvdec_check_cuda_device_from_context (GstGLContext * context,
1428     gboolean * ret)
1429 {
1430   guint device_count = 0;
1431   CUdevice device_list[1] = { 0, };
1432   CUresult cuda_ret;
1433
1434   *ret = FALSE;
1435
1436   cuda_ret = CuGLGetDevices (&device_count,
1437       device_list, 1, CU_GL_DEVICE_LIST_ALL);
1438
1439   if (!gst_cuda_result (cuda_ret) || device_count == 0)
1440     return;
1441
1442   *ret = TRUE;
1443
1444   return;
1445 }
1446
1447 static gboolean
1448 gst_nvdec_ensure_gl_context (GstNvDec * nvdec)
1449 {
1450   gboolean ret;
1451
1452   if (!nvdec->gl_display) {
1453     GST_DEBUG_OBJECT (nvdec, "No available OpenGL display");
1454     return FALSE;
1455   }
1456
1457   if (!gst_gl_query_local_gl_context (GST_ELEMENT (nvdec), GST_PAD_SRC,
1458           &nvdec->gl_context)) {
1459     GST_INFO_OBJECT (nvdec, "failed to query local OpenGL context");
1460     if (nvdec->gl_context)
1461       gst_object_unref (nvdec->gl_context);
1462     nvdec->gl_context =
1463         gst_gl_display_get_gl_context_for_thread (nvdec->gl_display, NULL);
1464     if (!nvdec->gl_context
1465         || !gst_gl_display_add_context (nvdec->gl_display, nvdec->gl_context)) {
1466       if (nvdec->gl_context)
1467         gst_object_unref (nvdec->gl_context);
1468       if (!gst_gl_display_create_context (nvdec->gl_display,
1469               nvdec->other_gl_context, &nvdec->gl_context, NULL)) {
1470         GST_ERROR_OBJECT (nvdec, "failed to create OpenGL context");
1471         return FALSE;
1472       }
1473       if (!gst_gl_display_add_context (nvdec->gl_display, nvdec->gl_context)) {
1474         GST_ERROR_OBJECT (nvdec,
1475             "failed to add the OpenGL context to the display");
1476         return FALSE;
1477       }
1478     }
1479   }
1480
1481   if (!gst_gl_context_check_gl_version (nvdec->gl_context,
1482           SUPPORTED_GL_APIS, 3, 0)) {
1483     GST_WARNING_OBJECT (nvdec, "OpenGL context could not support PBO download");
1484     return FALSE;
1485   }
1486
1487   gst_gl_context_thread_add (nvdec->gl_context,
1488       (GstGLContextThreadFunc) gst_nvdec_check_cuda_device_from_context, &ret);
1489
1490   if (!ret) {
1491     GST_WARNING_OBJECT (nvdec, "Current OpenGL context is not CUDA-compatible");
1492     return FALSE;
1493   }
1494
1495   return TRUE;
1496 }
1497
1498 static gboolean
1499 gst_nvdec_ensure_gl_pool (GstNvDec * nvdec, GstQuery * query)
1500 {
1501   GstCaps *outcaps;
1502   GstBufferPool *pool = NULL;
1503   guint n, size, min, max;
1504   GstVideoInfo vinfo = { 0, };
1505   GstStructure *config;
1506
1507   GST_DEBUG_OBJECT (nvdec, "decide allocation");
1508
1509   gst_query_parse_allocation (query, &outcaps, NULL);
1510   n = gst_query_get_n_allocation_pools (query);
1511   if (n > 0)
1512     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
1513
1514   if (pool && !GST_IS_GL_BUFFER_POOL (pool)) {
1515     gst_object_unref (pool);
1516     pool = NULL;
1517   }
1518
1519   if (!pool) {
1520     GST_DEBUG_OBJECT (nvdec, "no downstream pool, create our pool");
1521     pool = gst_gl_buffer_pool_new (nvdec->gl_context);
1522
1523     if (outcaps)
1524       gst_video_info_from_caps (&vinfo, outcaps);
1525     size = (guint) vinfo.size;
1526     min = max = 0;
1527   }
1528
1529   config = gst_buffer_pool_get_config (pool);
1530   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
1531   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1532   gst_buffer_pool_set_config (pool, config);
1533   if (n > 0)
1534     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
1535   else
1536     gst_query_add_allocation_pool (query, pool, size, min, max);
1537   gst_object_unref (pool);
1538
1539   return TRUE;
1540 }
1541 #endif
1542
1543 static gboolean
1544 gst_nvdec_ensure_cuda_pool (GstNvDec * nvdec, GstQuery * query)
1545 {
1546   GstCaps *outcaps;
1547   GstBufferPool *pool = NULL;
1548   guint n, size, min, max;
1549   GstVideoInfo vinfo = { 0, };
1550   GstStructure *config;
1551
1552   gst_query_parse_allocation (query, &outcaps, NULL);
1553   n = gst_query_get_n_allocation_pools (query);
1554   if (n > 0) {
1555     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
1556     if (pool && !GST_IS_CUDA_BUFFER_POOL (pool)) {
1557       gst_object_unref (pool);
1558       pool = NULL;
1559     }
1560   }
1561
1562   if (!pool) {
1563     GST_DEBUG_OBJECT (nvdec, "no downstream pool, create our pool");
1564     pool = gst_cuda_buffer_pool_new (nvdec->cuda_ctx);
1565
1566     if (outcaps)
1567       gst_video_info_from_caps (&vinfo, outcaps);
1568     size = (guint) vinfo.size;
1569     min = max = 0;
1570   }
1571
1572   config = gst_buffer_pool_get_config (pool);
1573   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
1574   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1575   gst_buffer_pool_set_config (pool, config);
1576   if (n > 0)
1577     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
1578   else
1579     gst_query_add_allocation_pool (query, pool, size, min, max);
1580   gst_object_unref (pool);
1581
1582   return TRUE;
1583 }
1584
1585 static gboolean
1586 gst_nvdec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
1587 {
1588   GstNvDec *nvdec = GST_NVDEC (decoder);
1589
1590   GST_DEBUG_OBJECT (nvdec, "decide allocation");
1591
1592   if (nvdec->mem_type == GST_NVDEC_MEM_TYPE_SYSTEM)
1593     goto done;
1594
1595 #ifdef HAVE_NVCODEC_GST_GL
1596   if (nvdec->mem_type == GST_NVDEC_MEM_TYPE_GL) {
1597     if (!gst_nvdec_ensure_gl_pool (nvdec, query))
1598       return FALSE;
1599   } else
1600 #endif
1601   if (!gst_nvdec_ensure_cuda_pool (nvdec, query)) {
1602     return FALSE;
1603   }
1604
1605 done:
1606   return GST_VIDEO_DECODER_CLASS (gst_nvdec_parent_class)->decide_allocation
1607       (decoder, query);
1608 }
1609
1610 static gboolean
1611 gst_nvdec_src_query (GstVideoDecoder * decoder, GstQuery * query)
1612 {
1613   GstNvDec *nvdec = GST_NVDEC (decoder);
1614
1615   switch (GST_QUERY_TYPE (query)) {
1616     case GST_QUERY_CONTEXT:
1617       if (gst_cuda_handle_context_query (GST_ELEMENT (decoder),
1618               query, nvdec->cuda_ctx)) {
1619         return TRUE;
1620       }
1621 #ifdef HAVE_NVCODEC_GST_GL
1622       if (gst_gl_handle_context_query (GST_ELEMENT (decoder), query,
1623               nvdec->gl_display, nvdec->gl_context, nvdec->other_gl_context)) {
1624         if (nvdec->gl_display)
1625           gst_gl_display_filter_gl_api (GST_GL_DISPLAY (nvdec->gl_display),
1626               SUPPORTED_GL_APIS);
1627         return TRUE;
1628       }
1629 #endif
1630       break;
1631     default:
1632       break;
1633   }
1634
1635   return GST_VIDEO_DECODER_CLASS (gst_nvdec_parent_class)->src_query (decoder,
1636       query);
1637 }
1638
1639 static void
1640 gst_nvdec_set_context (GstElement * element, GstContext * context)
1641 {
1642   GstNvDec *nvdec = GST_NVDEC (element);
1643   GstNvDecClass *klass = GST_NVDEC_GET_CLASS (nvdec);
1644
1645   GST_DEBUG_OBJECT (nvdec, "set context %s",
1646       gst_context_get_context_type (context));
1647
1648   if (gst_cuda_handle_set_context (element,
1649           context, klass->cuda_device_id, &nvdec->cuda_ctx)) {
1650     goto done;
1651   }
1652 #ifdef HAVE_NVCODEC_GST_GL
1653   gst_gl_handle_set_context (element, context, &nvdec->gl_display,
1654       &nvdec->other_gl_context);
1655 #endif
1656
1657 done:
1658   GST_ELEMENT_CLASS (gst_nvdec_parent_class)->set_context (element, context);
1659 }
1660
1661 typedef struct
1662 {
1663   GstCaps *sink_caps;
1664   GstCaps *src_caps;
1665   cudaVideoCodec codec_type;
1666   gchar *codec;
1667   guint cuda_device_id;
1668   gboolean is_default;
1669 } GstNvDecClassData;
1670
1671 static void
1672 gst_nvdec_subclass_init (gpointer g_class, gpointer data)
1673 {
1674   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1675   GstNvDecClass *nvdec_class = GST_NVDEC_CLASS (g_class);
1676   GstNvDecClassData *cdata = data;
1677   gchar *long_name;
1678
1679   if (cdata->is_default) {
1680     long_name = g_strdup_printf ("NVDEC %s Video Decoder", cdata->codec);
1681   } else {
1682     long_name = g_strdup_printf ("NVDEC %s Video Decoder with device %d",
1683         cdata->codec, cdata->cuda_device_id);
1684   }
1685
1686   gst_element_class_set_metadata (element_class, long_name,
1687       "Codec/Decoder/Video/Hardware", "NVDEC video decoder",
1688       "Ericsson AB, http://www.ericsson.com, "
1689       "Seungha Yang <seungha.yang@navercorp.com>");
1690   g_free (long_name);
1691
1692   gst_element_class_add_pad_template (element_class,
1693       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
1694           cdata->sink_caps));
1695   gst_element_class_add_pad_template (element_class,
1696       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
1697           cdata->src_caps));
1698
1699   nvdec_class->codec_type = cdata->codec_type;
1700   nvdec_class->cuda_device_id = cdata->cuda_device_id;
1701
1702   gst_caps_unref (cdata->sink_caps);
1703   gst_caps_unref (cdata->src_caps);
1704   g_free (cdata->codec);
1705   g_free (cdata);
1706 }
1707
1708 static void
1709 gst_nvdec_subclass_register (GstPlugin * plugin, GType type,
1710     cudaVideoCodec codec_type, const gchar * codec, guint device_id, guint rank,
1711     GstCaps * sink_caps, GstCaps * src_caps)
1712 {
1713   GTypeQuery type_query;
1714   GTypeInfo type_info = { 0, };
1715   GType subtype;
1716   gchar *type_name;
1717   GstNvDecClassData *cdata;
1718   gboolean is_default = TRUE;
1719
1720   cdata = g_new0 (GstNvDecClassData, 1);
1721   cdata->sink_caps = gst_caps_ref (sink_caps);
1722   cdata->src_caps = gst_caps_ref (src_caps);
1723   cdata->codec_type = codec_type;
1724   cdata->codec = g_strdup (codec);
1725   cdata->cuda_device_id = device_id;
1726
1727   g_type_query (type, &type_query);
1728   memset (&type_info, 0, sizeof (type_info));
1729   type_info.class_size = type_query.class_size;
1730   type_info.instance_size = type_query.instance_size;
1731   type_info.class_init = gst_nvdec_subclass_init;
1732   type_info.class_data = cdata;
1733
1734   type_name = g_strdup_printf ("nv%sdec", codec);
1735
1736   if (g_type_from_name (type_name) != 0) {
1737     g_free (type_name);
1738     type_name = g_strdup_printf ("nv%sdevice%ddec", codec, device_id);
1739     is_default = FALSE;
1740   }
1741
1742   cdata->is_default = is_default;
1743   subtype = g_type_register_static (type, type_name, &type_info, 0);
1744
1745   /* make lower rank than default device */
1746   if (rank > 0 && !is_default)
1747     rank--;
1748
1749   if (!gst_element_register (plugin, type_name, rank, subtype))
1750     GST_WARNING ("Failed to register plugin '%s'", type_name);
1751
1752   g_free (type_name);
1753 }
1754
1755 void
1756 gst_nvdec_plugin_init (GstPlugin * plugin, guint device_index,
1757     cudaVideoCodec codec, const gchar * codec_name, GstCaps * sink_template,
1758     GstCaps * src_template)
1759 {
1760   gst_nvdec_subclass_register (plugin, GST_TYPE_NVDEC, codec,
1761       codec_name, device_index, GST_RANK_PRIMARY, sink_template, src_template);
1762 }