618c99088a0024f8c2bd8c7c6fb217ea1ec40a7f
[platform/upstream/gst-plugins-bad.git] / sys / nvdec / 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
34 #include <gst/gl/gstglfuncs.h>
35 #include <cudaGL.h>
36
37 typedef enum
38 {
39   GST_NVDEC_QUEUE_ITEM_TYPE_SEQUENCE,
40   GST_NVDEC_QUEUE_ITEM_TYPE_DECODE,
41   GST_NVDEC_QUEUE_ITEM_TYPE_DISPLAY
42 } GstNvDecQueueItemType;
43
44 typedef struct _GstNvDecQueueItem
45 {
46   GstNvDecQueueItemType type;
47   gpointer data;
48 } GstNvDecQueueItem;
49
50 GST_DEBUG_CATEGORY_STATIC (gst_nvdec_debug_category);
51 #define GST_CAT_DEFAULT gst_nvdec_debug_category
52
53 static inline gboolean
54 cuda_OK (CUresult result)
55 {
56   const gchar *error_name, *error_text;
57
58   if (result != CUDA_SUCCESS) {
59     cuGetErrorName (result, &error_name);
60     cuGetErrorString (result, &error_text);
61     GST_WARNING ("CUDA call failed: %s, %s", error_name, error_text);
62     return FALSE;
63   }
64
65   return TRUE;
66 }
67
68 G_DEFINE_TYPE (GstNvDecCudaContext, gst_nvdec_cuda_context, G_TYPE_OBJECT);
69
70 static void
71 gst_nvdec_cuda_context_finalize (GObject * object)
72 {
73   GstNvDecCudaContext *self = (GstNvDecCudaContext *) object;
74
75   if (self->lock) {
76     GST_DEBUG ("destroying CUDA context lock");
77     if (cuda_OK (cuvidCtxLockDestroy (self->lock)))
78       self->lock = NULL;
79     else
80       GST_ERROR ("failed to destroy CUDA context lock");
81   }
82
83   if (self->context) {
84     GST_DEBUG ("destroying CUDA context");
85     if (cuda_OK (cuCtxDestroy (self->context)))
86       self->context = NULL;
87     else
88       GST_ERROR ("failed to destroy CUDA context");
89   }
90
91   G_OBJECT_CLASS (gst_nvdec_cuda_context_parent_class)->finalize (object);
92 }
93
94 static void
95 gst_nvdec_cuda_context_class_init (GstNvDecCudaContextClass * klass)
96 {
97   G_OBJECT_CLASS (klass)->finalize = gst_nvdec_cuda_context_finalize;
98 }
99
100 static void
101 gst_nvdec_cuda_context_init (GstNvDecCudaContext * self)
102 {
103   if (!cuda_OK (cuInit (0)))
104     GST_ERROR ("failed to init CUDA");
105
106   if (!cuda_OK (cuCtxCreate (&self->context, CU_CTX_SCHED_AUTO, 0)))
107     GST_ERROR ("failed to create CUDA context");
108
109   if (!cuda_OK (cuCtxPopCurrent (NULL)))
110     GST_ERROR ("failed to pop current CUDA context");
111
112   if (!cuda_OK (cuvidCtxLockCreate (&self->lock, self->context)))
113     GST_ERROR ("failed to create CUDA context lock");
114 }
115
116 typedef struct _GstNvDecCudaGraphicsResourceInfo
117 {
118   GstGLContext *gl_context;
119   GstNvDecCudaContext *cuda_context;
120   CUgraphicsResource resource;
121 } GstNvDecCudaGraphicsResourceInfo;
122
123 static void
124 register_cuda_resource (GstGLContext * context, gpointer * args)
125 {
126   GstMemory *mem = GST_MEMORY_CAST (args[0]);
127   GstNvDecCudaGraphicsResourceInfo *cgr_info =
128       (GstNvDecCudaGraphicsResourceInfo *) args[1];
129   GstMapInfo map_info = GST_MAP_INFO_INIT;
130   guint texture_id;
131
132   if (!cuda_OK (cuvidCtxLock (cgr_info->cuda_context->lock, 0)))
133     GST_WARNING ("failed to lock CUDA context");
134
135   if (gst_memory_map (mem, &map_info, GST_MAP_READ | GST_MAP_GL)) {
136     texture_id = *(guint *) map_info.data;
137
138     if (!cuda_OK (cuGraphicsGLRegisterImage (&cgr_info->resource, texture_id,
139                 GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD)))
140       GST_WARNING ("failed to register texture with CUDA");
141
142     gst_memory_unmap (mem, &map_info);
143   } else
144     GST_WARNING ("failed to map memory");
145
146   if (!cuda_OK (cuvidCtxUnlock (cgr_info->cuda_context->lock, 0)))
147     GST_WARNING ("failed to unlock CUDA context");
148 }
149
150 static void
151 unregister_cuda_resource (GstGLContext * context,
152     GstNvDecCudaGraphicsResourceInfo * cgr_info)
153 {
154   if (!cuda_OK (cuvidCtxLock (cgr_info->cuda_context->lock, 0)))
155     GST_WARNING ("failed to lock CUDA context");
156
157   if (!cuda_OK (cuGraphicsUnregisterResource ((const CUgraphicsResource)
158               cgr_info->resource)))
159     GST_WARNING ("failed to unregister resource");
160
161   if (!cuda_OK (cuvidCtxUnlock (cgr_info->cuda_context->lock, 0)))
162     GST_WARNING ("failed to unlock CUDA context");
163 }
164
165 static void
166 free_cgr_info (GstNvDecCudaGraphicsResourceInfo * cgr_info)
167 {
168   gst_gl_context_thread_add (cgr_info->gl_context,
169       (GstGLContextThreadFunc) unregister_cuda_resource, cgr_info);
170   gst_object_unref (cgr_info->gl_context);
171   g_object_unref (cgr_info->cuda_context);
172   g_slice_free (GstNvDecCudaGraphicsResourceInfo, cgr_info);
173 }
174
175 static CUgraphicsResource
176 ensure_cuda_graphics_resource (GstMemory * mem,
177     GstNvDecCudaContext * cuda_context)
178 {
179   static GQuark quark = 0;
180   GstNvDecCudaGraphicsResourceInfo *cgr_info;
181   gpointer args[2];
182
183   if (!gst_is_gl_base_memory (mem)) {
184     GST_WARNING ("memory is not GL base memory");
185     return NULL;
186   }
187
188   if (!quark)
189     quark = g_quark_from_static_string ("GstNvDecCudaGraphicsResourceInfo");
190
191   cgr_info = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem), quark);
192   if (!cgr_info) {
193     cgr_info = g_slice_new (GstNvDecCudaGraphicsResourceInfo);
194     cgr_info->gl_context =
195         gst_object_ref (GST_GL_BASE_MEMORY_CAST (mem)->context);
196     cgr_info->cuda_context = g_object_ref (cuda_context);
197     args[0] = mem;
198     args[1] = cgr_info;
199     gst_gl_context_thread_add (cgr_info->gl_context,
200         (GstGLContextThreadFunc) register_cuda_resource, args);
201     gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), quark, cgr_info,
202         (GDestroyNotify) free_cgr_info);
203   }
204
205   return cgr_info->resource;
206 }
207
208 static gboolean gst_nvdec_start (GstVideoDecoder * decoder);
209 static gboolean gst_nvdec_stop (GstVideoDecoder * decoder);
210 static gboolean gst_nvdec_set_format (GstVideoDecoder * decoder,
211     GstVideoCodecState * state);
212 static GstFlowReturn gst_nvdec_handle_frame (GstVideoDecoder * decoder,
213     GstVideoCodecFrame * frame);
214 static gboolean gst_nvdec_decide_allocation (GstVideoDecoder * decoder,
215     GstQuery * query);
216 static void gst_nvdec_set_context (GstElement * element, GstContext * context);
217 static gboolean gst_nvdec_src_query (GstVideoDecoder * decoder,
218     GstQuery * query);
219
220 static GstStaticPadTemplate gst_nvdec_sink_template =
221     GST_STATIC_PAD_TEMPLATE (GST_VIDEO_DECODER_SINK_NAME,
222     GST_PAD_SINK, GST_PAD_ALWAYS,
223     GST_STATIC_CAPS ("video/x-h264, stream-format=byte-stream, alignment=au; "
224         "video/x-h265, stream-format=byte-stream, alignment=au; "
225         "video/mpeg, mpegversion={ 1, 2, 4 }, systemstream=false; "
226         "image/jpeg")
227     );
228
229 static GstStaticPadTemplate gst_nvdec_src_template =
230 GST_STATIC_PAD_TEMPLATE (GST_VIDEO_DECODER_SRC_NAME,
231     GST_PAD_SRC, GST_PAD_ALWAYS,
232     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
233         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "NV12") ", texture-target=2D")
234     );
235
236 G_DEFINE_TYPE_WITH_CODE (GstNvDec, gst_nvdec, GST_TYPE_VIDEO_DECODER,
237     GST_DEBUG_CATEGORY_INIT (gst_nvdec_debug_category, "nvdec", 0,
238         "Debug category for the nvdec element"));
239
240 static void
241 gst_nvdec_class_init (GstNvDecClass * klass)
242 {
243   GstVideoDecoderClass *video_decoder_class = GST_VIDEO_DECODER_CLASS (klass);
244   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
245
246   gst_element_class_add_static_pad_template (element_class,
247       &gst_nvdec_sink_template);
248   gst_element_class_add_static_pad_template (element_class,
249       &gst_nvdec_src_template);
250
251   gst_element_class_set_static_metadata (element_class, "NVDEC video decoder",
252       "Decoder/Video", "NVDEC video decoder",
253       "Ericsson AB, http://www.ericsson.com");
254
255   video_decoder_class->start = GST_DEBUG_FUNCPTR (gst_nvdec_start);
256   video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_nvdec_stop);
257   video_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_nvdec_set_format);
258   video_decoder_class->handle_frame =
259       GST_DEBUG_FUNCPTR (gst_nvdec_handle_frame);
260   video_decoder_class->decide_allocation =
261       GST_DEBUG_FUNCPTR (gst_nvdec_decide_allocation);
262   video_decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_nvdec_src_query);
263
264   element_class->set_context = GST_DEBUG_FUNCPTR (gst_nvdec_set_context);
265 }
266
267 static void
268 gst_nvdec_init (GstNvDec * nvdec)
269 {
270   gst_video_decoder_set_packetized (GST_VIDEO_DECODER (nvdec), TRUE);
271   gst_video_decoder_set_needs_format (GST_VIDEO_DECODER (nvdec), TRUE);
272 }
273
274 static gboolean
275 parser_sequence_callback (GstNvDec * nvdec, CUVIDEOFORMAT * format)
276 {
277   GstNvDecQueueItem *item;
278   guint width, height;
279   CUVIDDECODECREATEINFO create_info = { 0, };
280   gboolean ret = TRUE;
281
282   width = format->display_area.right - format->display_area.left;
283   height = format->display_area.bottom - format->display_area.top;
284   GST_DEBUG_OBJECT (nvdec, "width: %u, height: %u", width, height);
285
286   if (!nvdec->decoder || (nvdec->width != width || nvdec->height != height)) {
287     if (!cuda_OK (cuvidCtxLock (nvdec->cuda_context->lock, 0))) {
288       GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
289       return FALSE;
290     }
291
292     if (nvdec->decoder) {
293       GST_DEBUG_OBJECT (nvdec, "destroying decoder");
294       if (!cuda_OK (cuvidDestroyDecoder (nvdec->decoder))) {
295         GST_ERROR_OBJECT (nvdec, "failed to destroy decoder");
296         ret = FALSE;
297       } else
298         nvdec->decoder = NULL;
299     }
300
301     GST_DEBUG_OBJECT (nvdec, "creating decoder");
302     create_info.ulWidth = width;
303     create_info.ulHeight = height;
304     create_info.ulNumDecodeSurfaces = 20;
305     create_info.CodecType = format->codec;
306     create_info.ChromaFormat = format->chroma_format;
307     create_info.ulCreationFlags = cudaVideoCreate_Default;
308     create_info.display_area.left = format->display_area.left;
309     create_info.display_area.top = format->display_area.top;
310     create_info.display_area.right = format->display_area.right;
311     create_info.display_area.bottom = format->display_area.bottom;
312     create_info.OutputFormat = cudaVideoSurfaceFormat_NV12;
313     create_info.DeinterlaceMode = cudaVideoDeinterlaceMode_Weave;
314     create_info.ulTargetWidth = width;
315     create_info.ulTargetHeight = height;
316     create_info.ulNumOutputSurfaces = 1;
317     create_info.vidLock = nvdec->cuda_context->lock;
318     create_info.target_rect.left = 0;
319     create_info.target_rect.top = 0;
320     create_info.target_rect.right = width;
321     create_info.target_rect.bottom = height;
322
323     if (nvdec->decoder
324         || !cuda_OK (cuvidCreateDecoder (&nvdec->decoder, &create_info))) {
325       GST_ERROR_OBJECT (nvdec, "failed to create decoder");
326       ret = FALSE;
327     }
328
329     if (!cuda_OK (cuvidCtxUnlock (nvdec->cuda_context->lock, 0))) {
330       GST_ERROR_OBJECT (nvdec, "failed to unlock CUDA context");
331       ret = FALSE;
332     }
333   }
334
335   item = g_slice_new (GstNvDecQueueItem);
336   item->type = GST_NVDEC_QUEUE_ITEM_TYPE_SEQUENCE;
337   item->data = g_memdup (format, sizeof (CUVIDEOFORMAT));
338   g_async_queue_push (nvdec->decode_queue, item);
339
340   return ret;
341 }
342
343 static gboolean
344 parser_decode_callback (GstNvDec * nvdec, CUVIDPICPARAMS * params)
345 {
346   GstNvDecQueueItem *item;
347
348   GST_LOG_OBJECT (nvdec, "picture index: %u", params->CurrPicIdx);
349
350   if (!cuda_OK (cuvidCtxLock (nvdec->cuda_context->lock, 0)))
351     GST_WARNING_OBJECT (nvdec, "failed to lock CUDA context");
352
353   if (!cuda_OK (cuvidDecodePicture (nvdec->decoder, params)))
354     GST_WARNING_OBJECT (nvdec, "failed to decode picture");
355
356   if (!cuda_OK (cuvidCtxUnlock (nvdec->cuda_context->lock, 0)))
357     GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
358
359   item = g_slice_new (GstNvDecQueueItem);
360   item->type = GST_NVDEC_QUEUE_ITEM_TYPE_DECODE;
361   item->data = g_memdup (params, sizeof (CUVIDPICPARAMS));
362   ((CUVIDPICPARAMS *) item->data)->pBitstreamData = NULL;
363   ((CUVIDPICPARAMS *) item->data)->pSliceDataOffsets = NULL;
364   g_async_queue_push (nvdec->decode_queue, item);
365
366   return TRUE;
367 }
368
369 static gboolean
370 parser_display_callback (GstNvDec * nvdec, CUVIDPARSERDISPINFO * dispinfo)
371 {
372   GstNvDecQueueItem *item;
373
374   GST_LOG_OBJECT (nvdec, "picture index: %u", dispinfo->picture_index);
375
376   item = g_slice_new (GstNvDecQueueItem);
377   item->type = GST_NVDEC_QUEUE_ITEM_TYPE_DISPLAY;
378   item->data = g_memdup (dispinfo, sizeof (CUVIDPARSERDISPINFO));
379   g_async_queue_push (nvdec->decode_queue, item);
380
381   return TRUE;
382 }
383
384 static gboolean
385 gst_nvdec_start (GstVideoDecoder * decoder)
386 {
387   GstNvDec *nvdec = GST_NVDEC (decoder);
388
389   GST_DEBUG_OBJECT (nvdec, "creating CUDA context");
390   nvdec->cuda_context = g_object_new (gst_nvdec_cuda_context_get_type (), NULL);
391   nvdec->decode_queue = g_async_queue_new ();
392
393   if (!nvdec->cuda_context->context || !nvdec->cuda_context->lock) {
394     GST_ERROR_OBJECT (nvdec, "failed to create CUDA context or lock");
395     return FALSE;
396   }
397
398   return TRUE;
399 }
400
401 static gboolean
402 maybe_destroy_decoder_and_parser (GstNvDec * nvdec)
403 {
404   gboolean ret = TRUE;
405
406   if (!cuda_OK (cuvidCtxLock (nvdec->cuda_context->lock, 0))) {
407     GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
408     return FALSE;
409   }
410
411   if (nvdec->decoder) {
412     GST_DEBUG_OBJECT (nvdec, "destroying decoder");
413     ret = cuda_OK (cuvidDestroyDecoder (nvdec->decoder));
414     if (ret)
415       nvdec->decoder = NULL;
416     else
417       GST_ERROR_OBJECT (nvdec, "failed to destroy decoder");
418   }
419
420   if (!cuda_OK (cuvidCtxUnlock (nvdec->cuda_context->lock, 0))) {
421     GST_ERROR_OBJECT (nvdec, "failed to unlock CUDA context");
422     return FALSE;
423   }
424
425   if (nvdec->parser) {
426     GST_DEBUG_OBJECT (nvdec, "destroying parser");
427     if (!cuda_OK (cuvidDestroyVideoParser (nvdec->parser))) {
428       GST_ERROR_OBJECT (nvdec, "failed to destroy parser");
429       return FALSE;
430     }
431     nvdec->parser = NULL;
432   }
433
434   return ret;
435 }
436
437 static gboolean
438 gst_nvdec_stop (GstVideoDecoder * decoder)
439 {
440   GstNvDec *nvdec = GST_NVDEC (decoder);
441   GstNvDecQueueItem *item;
442
443   GST_DEBUG_OBJECT (nvdec, "stop");
444
445   if (!maybe_destroy_decoder_and_parser (nvdec))
446     return FALSE;
447
448   if (nvdec->cuda_context) {
449     g_object_unref (nvdec->cuda_context);
450     nvdec->cuda_context = NULL;
451   }
452
453   if (nvdec->gl_context) {
454     gst_object_unref (nvdec->gl_context);
455     nvdec->gl_context = NULL;
456   }
457
458   if (nvdec->other_gl_context) {
459     gst_object_unref (nvdec->other_gl_context);
460     nvdec->other_gl_context = NULL;
461   }
462
463   if (nvdec->gl_display) {
464     gst_object_unref (nvdec->gl_display);
465     nvdec->gl_display = NULL;
466   }
467
468   if (nvdec->input_state) {
469     gst_video_codec_state_unref (nvdec->input_state);
470     nvdec->input_state = NULL;
471   }
472
473   if (nvdec->decode_queue) {
474     if (g_async_queue_length (nvdec->decode_queue) > 0) {
475       GST_INFO_OBJECT (nvdec, "decode queue not empty");
476
477       while ((item = g_async_queue_try_pop (nvdec->decode_queue))) {
478         g_free (item->data);
479         g_slice_free (GstNvDecQueueItem, item);
480       }
481     }
482     g_async_queue_unref (nvdec->decode_queue);
483     nvdec->decode_queue = NULL;
484   }
485
486   return TRUE;
487 }
488
489 static gboolean
490 gst_nvdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
491 {
492   GstNvDec *nvdec = GST_NVDEC (decoder);
493   GstStructure *s;
494   const gchar *caps_name;
495   gint mpegversion = 0;
496   CUVIDPARSERPARAMS parser_params = { 0, };
497
498   GST_DEBUG_OBJECT (nvdec, "set format");
499
500   if (nvdec->input_state)
501     gst_video_codec_state_unref (nvdec->input_state);
502
503   nvdec->input_state = gst_video_codec_state_ref (state);
504
505   if (!maybe_destroy_decoder_and_parser (nvdec))
506     return FALSE;
507
508   s = gst_caps_get_structure (state->caps, 0);
509   caps_name = gst_structure_get_name (s);
510   GST_DEBUG_OBJECT (nvdec, "codec is %s", caps_name);
511
512   if (!g_strcmp0 (caps_name, "video/mpeg")) {
513     if (gst_structure_get_int (s, "mpegversion", &mpegversion)) {
514       switch (mpegversion) {
515         case 1:
516           parser_params.CodecType = cudaVideoCodec_MPEG1;
517           break;
518         case 2:
519           parser_params.CodecType = cudaVideoCodec_MPEG2;
520           break;
521         case 4:
522           parser_params.CodecType = cudaVideoCodec_MPEG4;
523           break;
524       }
525     }
526     if (!mpegversion) {
527       GST_ERROR_OBJECT (nvdec, "could not get MPEG version");
528       return FALSE;
529     }
530   } else if (!g_strcmp0 (caps_name, "video/x-h264")) {
531     parser_params.CodecType = cudaVideoCodec_H264;
532   } else if (!g_strcmp0 (caps_name, "image/jpeg")) {
533     parser_params.CodecType = cudaVideoCodec_JPEG;
534   } else if (!g_strcmp0 (caps_name, "video/x-h265")) {
535     parser_params.CodecType = cudaVideoCodec_HEVC;
536   } else {
537     GST_ERROR_OBJECT (nvdec, "failed to determine codec type");
538     return FALSE;
539   }
540
541   parser_params.ulMaxNumDecodeSurfaces = 20;
542   parser_params.ulErrorThreshold = 100;
543   parser_params.ulMaxDisplayDelay = 0;
544   parser_params.ulClockRate = GST_SECOND;
545   parser_params.pUserData = nvdec;
546   parser_params.pfnSequenceCallback =
547       (PFNVIDSEQUENCECALLBACK) parser_sequence_callback;
548   parser_params.pfnDecodePicture =
549       (PFNVIDDECODECALLBACK) parser_decode_callback;
550   parser_params.pfnDisplayPicture =
551       (PFNVIDDISPLAYCALLBACK) parser_display_callback;
552
553   GST_DEBUG_OBJECT (nvdec, "creating parser");
554   if (!cuda_OK (cuvidCreateVideoParser (&nvdec->parser, &parser_params))) {
555     GST_ERROR_OBJECT (nvdec, "failed to create parser");
556     return FALSE;
557   }
558
559   return TRUE;
560 }
561
562 static void
563 copy_video_frame_to_gl_textures (GstGLContext * context, gpointer * args)
564 {
565   GstNvDec *nvdec = GST_NVDEC (args[0]);
566   CUVIDPARSERDISPINFO *dispinfo = (CUVIDPARSERDISPINFO *) args[1];
567   CUgraphicsResource *resources = (CUgraphicsResource *) args[2];
568   guint num_resources = GPOINTER_TO_UINT (args[3]);
569   CUVIDPROCPARAMS proc_params = { 0, };
570   CUdeviceptr dptr;
571   CUarray array;
572   guint pitch, i;
573   CUDA_MEMCPY2D mcpy2d = { 0, };
574
575   GST_LOG_OBJECT (nvdec, "picture index: %u", dispinfo->picture_index);
576
577   proc_params.progressive_frame = dispinfo->progressive_frame;
578   proc_params.top_field_first = dispinfo->top_field_first;
579   proc_params.unpaired_field = dispinfo->repeat_first_field == -1;
580
581   if (!cuda_OK (cuvidCtxLock (nvdec->cuda_context->lock, 0))) {
582     GST_WARNING_OBJECT (nvdec, "failed to lock CUDA context");
583     return;
584   }
585
586   if (!cuda_OK (cuvidMapVideoFrame (nvdec->decoder, dispinfo->picture_index,
587               &dptr, &pitch, &proc_params))) {
588     GST_WARNING_OBJECT (nvdec, "failed to map CUDA video frame");
589     goto unlock_cuda_context;
590   }
591
592   if (!cuda_OK (cuGraphicsMapResources (num_resources, resources, NULL))) {
593     GST_WARNING_OBJECT (nvdec, "failed to map CUDA resources");
594     goto unmap_video_frame;
595   }
596
597   mcpy2d.srcMemoryType = CU_MEMORYTYPE_DEVICE;
598   mcpy2d.srcPitch = pitch;
599   mcpy2d.dstMemoryType = CU_MEMORYTYPE_ARRAY;
600   mcpy2d.dstPitch = nvdec->width;
601   mcpy2d.WidthInBytes = nvdec->width;
602
603   for (i = 0; i < num_resources; i++) {
604     if (!cuda_OK (cuGraphicsSubResourceGetMappedArray (&array, resources[i], 0,
605                 0))) {
606       GST_WARNING_OBJECT (nvdec, "failed to map CUDA array");
607       break;
608     }
609
610     mcpy2d.srcDevice = dptr + (i * pitch * nvdec->height);
611     mcpy2d.dstArray = array;
612     mcpy2d.Height = nvdec->height / (i + 1);
613
614     if (!cuda_OK (cuMemcpy2D (&mcpy2d)))
615       GST_WARNING_OBJECT (nvdec, "memcpy to mapped array failed");
616   }
617
618   if (!cuda_OK (cuGraphicsUnmapResources (num_resources, resources, NULL)))
619     GST_WARNING_OBJECT (nvdec, "failed to unmap CUDA resources");
620
621 unmap_video_frame:
622   if (!cuda_OK (cuvidUnmapVideoFrame (nvdec->decoder, dptr)))
623     GST_WARNING_OBJECT (nvdec, "failed to unmap CUDA video frame");
624
625 unlock_cuda_context:
626   if (!cuda_OK (cuvidCtxUnlock (nvdec->cuda_context->lock, 0)))
627     GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
628 }
629
630 static GstFlowReturn
631 handle_pending_frames (GstNvDec * nvdec)
632 {
633   GstVideoDecoder *decoder = GST_VIDEO_DECODER (nvdec);
634   GList *pending_frames, *list, *tmp;
635   GstVideoCodecFrame *pending_frame;
636   guint frame_number;
637   GstClockTime latency = 0;
638   GstNvDecQueueItem *item;
639   CUVIDEOFORMAT *format;
640   GstVideoCodecState *state;
641   guint width, height, fps_n, fps_d, i, num_resources;
642   CUVIDPICPARAMS *decode_params;
643   CUVIDPARSERDISPINFO *dispinfo;
644   CUgraphicsResource *resources;
645   gpointer args[4];
646   GstMemory *mem;
647   GstFlowReturn ret = GST_FLOW_OK;
648
649   /* find the oldest unused, unfinished frame */
650   pending_frames = list = gst_video_decoder_get_frames (decoder);
651   for (; pending_frames; pending_frames = pending_frames->next) {
652     pending_frame = pending_frames->data;
653     frame_number =
654         GPOINTER_TO_UINT (gst_video_codec_frame_get_user_data (pending_frame));
655     if (!frame_number)
656       break;
657     latency += pending_frame->duration;
658   }
659
660   while (ret == GST_FLOW_OK && pending_frames
661       && (item =
662           (GstNvDecQueueItem *) g_async_queue_try_pop (nvdec->decode_queue))) {
663     switch (item->type) {
664       case GST_NVDEC_QUEUE_ITEM_TYPE_SEQUENCE:
665         if (!nvdec->decoder) {
666           GST_ERROR_OBJECT (nvdec, "no decoder");
667           ret = GST_FLOW_ERROR;
668           break;
669         }
670
671         format = (CUVIDEOFORMAT *) item->data;
672         width = format->display_area.right - format->display_area.left;
673         height = format->display_area.bottom - format->display_area.top;
674         fps_n = format->frame_rate.numerator;
675         fps_d = MAX (1, format->frame_rate.denominator);
676
677         if (!gst_pad_has_current_caps (GST_VIDEO_DECODER_SRC_PAD (decoder))
678             || width != nvdec->width || height != nvdec->height
679             || fps_n != nvdec->fps_n || fps_d != nvdec->fps_d) {
680           nvdec->width = width;
681           nvdec->height = height;
682           nvdec->fps_n = fps_n;
683           nvdec->fps_d = fps_d;
684
685           state = gst_video_decoder_set_output_state (decoder,
686               GST_VIDEO_FORMAT_NV12, nvdec->width, nvdec->height,
687               nvdec->input_state);
688           state->caps = gst_caps_new_simple ("video/x-raw",
689               "format", G_TYPE_STRING, "NV12",
690               "width", G_TYPE_INT, nvdec->width,
691               "height", G_TYPE_INT, nvdec->height,
692               "framerate", GST_TYPE_FRACTION, nvdec->fps_n, nvdec->fps_d,
693               "interlace-mode", G_TYPE_STRING, format->progressive_sequence
694               ? "progressive" : "interleaved",
695               "texture-target", G_TYPE_STRING, "2D", NULL);
696           gst_caps_set_features (state->caps, 0,
697               gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, NULL));
698           gst_video_codec_state_unref (state);
699
700           if (!gst_video_decoder_negotiate (decoder)) {
701             GST_WARNING_OBJECT (nvdec, "failed to negotiate with downstream");
702             ret = GST_FLOW_NOT_NEGOTIATED;
703             break;
704           }
705         }
706
707         break;
708
709       case GST_NVDEC_QUEUE_ITEM_TYPE_DECODE:
710         decode_params = (CUVIDPICPARAMS *) item->data;
711         pending_frame = pending_frames->data;
712         frame_number = decode_params->CurrPicIdx + 1;
713         gst_video_codec_frame_set_user_data (pending_frame,
714             GUINT_TO_POINTER (frame_number), NULL);
715
716         if (decode_params->intra_pic_flag)
717           GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pending_frame);
718
719         if (!GST_CLOCK_TIME_IS_VALID (pending_frame->duration)) {
720           pending_frame->duration =
721               nvdec->fps_n ? GST_SECOND * nvdec->fps_d / nvdec->fps_n : 0;
722         }
723         latency += pending_frame->duration;
724
725         pending_frames = pending_frames->next;
726
727         break;
728
729       case GST_NVDEC_QUEUE_ITEM_TYPE_DISPLAY:
730         dispinfo = (CUVIDPARSERDISPINFO *) item->data;
731         for (pending_frame = NULL, tmp = list; !pending_frame && tmp;
732             tmp = tmp->next) {
733           frame_number =
734               GPOINTER_TO_UINT (gst_video_codec_frame_get_user_data
735               (tmp->data));
736           if (frame_number == dispinfo->picture_index + 1)
737             pending_frame = tmp->data;
738         }
739         if (!pending_frame) {
740           GST_INFO_OBJECT (nvdec, "no frame with number %u",
741               dispinfo->picture_index + 1);
742           break;
743         }
744
745         if (dispinfo->timestamp != pending_frame->pts) {
746           GST_INFO_OBJECT (nvdec,
747               "timestamp mismatch, diff: %" GST_STIME_FORMAT,
748               GST_STIME_ARGS (GST_CLOCK_DIFF (dispinfo->timestamp,
749                       pending_frame->pts)));
750           pending_frame->pts = dispinfo->timestamp;
751         }
752
753         if (latency > nvdec->min_latency) {
754           nvdec->min_latency = latency;
755           gst_video_decoder_set_latency (decoder, nvdec->min_latency,
756               nvdec->min_latency);
757           GST_DEBUG_OBJECT (nvdec, "latency: %" GST_TIME_FORMAT,
758               GST_TIME_ARGS (latency));
759         }
760         latency -= pending_frame->duration;
761
762         ret = gst_video_decoder_allocate_output_frame (decoder, pending_frame);
763         if (ret != GST_FLOW_OK) {
764           GST_WARNING_OBJECT (nvdec, "failed to allocate output frame");
765           break;
766         }
767
768         num_resources = gst_buffer_n_memory (pending_frame->output_buffer);
769         resources = g_new (CUgraphicsResource, num_resources);
770
771         for (i = 0; i < num_resources; i++) {
772           mem = gst_buffer_get_memory (pending_frame->output_buffer, i);
773           resources[i] =
774               ensure_cuda_graphics_resource (mem, nvdec->cuda_context);
775           GST_MINI_OBJECT_FLAG_SET (mem,
776               GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD);
777           gst_memory_unref (mem);
778         }
779
780         args[0] = nvdec;
781         args[1] = dispinfo;
782         args[2] = resources;
783         args[3] = GUINT_TO_POINTER (num_resources);
784         gst_gl_context_thread_add (nvdec->gl_context,
785             (GstGLContextThreadFunc) copy_video_frame_to_gl_textures, args);
786         g_free (resources);
787
788         if (!dispinfo->progressive_frame) {
789           GST_BUFFER_FLAG_SET (pending_frame->output_buffer,
790               GST_VIDEO_BUFFER_FLAG_INTERLACED);
791
792           if (dispinfo->top_field_first) {
793             GST_BUFFER_FLAG_SET (pending_frame->output_buffer,
794                 GST_VIDEO_BUFFER_FLAG_TFF);
795           }
796           if (dispinfo->repeat_first_field == -1) {
797             GST_BUFFER_FLAG_SET (pending_frame->output_buffer,
798                 GST_VIDEO_BUFFER_FLAG_ONEFIELD);
799           } else {
800             GST_BUFFER_FLAG_SET (pending_frame->output_buffer,
801                 GST_VIDEO_BUFFER_FLAG_RFF);
802           }
803         }
804
805         list = g_list_remove (list, pending_frame);
806         ret = gst_video_decoder_finish_frame (decoder, pending_frame);
807         if (ret != GST_FLOW_OK)
808           GST_INFO_OBJECT (nvdec, "failed to finish frame");
809
810         break;
811
812       default:
813         g_assert_not_reached ();
814     }
815
816     g_free (item->data);
817     g_slice_free (GstNvDecQueueItem, item);
818   }
819
820   g_list_free_full (list, (GDestroyNotify) gst_video_codec_frame_unref);
821
822   return ret;
823 }
824
825 static GstFlowReturn
826 gst_nvdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
827 {
828   GstNvDec *nvdec = GST_NVDEC (decoder);
829   GstMapInfo map_info = GST_MAP_INFO_INIT;
830   CUVIDSOURCEDATAPACKET packet = { 0, };
831
832   GST_LOG_OBJECT (nvdec, "handle frame");
833
834   gst_video_codec_frame_set_user_data (frame, GUINT_TO_POINTER (0), NULL);
835
836   if (!gst_buffer_map (frame->input_buffer, &map_info, GST_MAP_READ)) {
837     GST_ERROR_OBJECT (nvdec, "failed to map input buffer");
838     gst_video_codec_frame_unref (frame);
839     return GST_FLOW_ERROR;
840   }
841
842   packet.payload_size = (gulong) map_info.size;
843   packet.payload = map_info.data;
844   packet.timestamp = frame->pts;
845   packet.flags = CUVID_PKT_TIMESTAMP;
846
847   if (GST_BUFFER_IS_DISCONT (frame->input_buffer))
848     packet.flags |= CUVID_PKT_DISCONTINUITY;
849
850   if (!cuda_OK (cuvidParseVideoData (nvdec->parser, &packet)))
851     GST_WARNING_OBJECT (nvdec, "parser failed");
852
853   gst_buffer_unmap (frame->input_buffer, &map_info);
854   gst_video_codec_frame_unref (frame);
855
856   return handle_pending_frames (nvdec);
857 }
858
859 static gboolean
860 gst_nvdec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
861 {
862   GstNvDec *nvdec = GST_NVDEC (decoder);
863   GstCaps *outcaps;
864   GstBufferPool *pool = NULL;
865   guint n, size, min, max;
866   GstVideoInfo vinfo = { 0, };
867   GstStructure *config;
868
869   GST_DEBUG_OBJECT (nvdec, "decide allocation");
870
871   if (!gst_gl_ensure_element_data (nvdec, &nvdec->gl_display,
872           &nvdec->other_gl_context)) {
873     GST_ERROR_OBJECT (nvdec, "failed to ensure OpenGL display");
874     return FALSE;
875   }
876
877   if (!gst_gl_query_local_gl_context (GST_ELEMENT (decoder), GST_PAD_SRC,
878           &nvdec->gl_context)) {
879     GST_INFO_OBJECT (nvdec, "failed to query local OpenGL context");
880     if (nvdec->gl_context)
881       gst_object_unref (nvdec->gl_context);
882     nvdec->gl_context =
883         gst_gl_display_get_gl_context_for_thread (nvdec->gl_display, NULL);
884     if (!nvdec->gl_context
885         || !gst_gl_display_add_context (nvdec->gl_display, nvdec->gl_context)) {
886       if (nvdec->gl_context)
887         gst_object_unref (nvdec->gl_context);
888       if (!gst_gl_display_create_context (nvdec->gl_display,
889               nvdec->other_gl_context, &nvdec->gl_context, NULL)) {
890         GST_ERROR_OBJECT (nvdec, "failed to create OpenGL context");
891         return FALSE;
892       }
893       if (!gst_gl_display_add_context (nvdec->gl_display, nvdec->gl_context)) {
894         GST_ERROR_OBJECT (nvdec,
895             "failed to add the OpenGL context to the display");
896         return FALSE;
897       }
898     }
899   }
900
901   gst_query_parse_allocation (query, &outcaps, NULL);
902   n = gst_query_get_n_allocation_pools (query);
903   if (n > 0) {
904     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
905     if (!GST_IS_GL_BUFFER_POOL (pool)) {
906       gst_object_unref (pool);
907       pool = NULL;
908     }
909   }
910
911   if (!pool) {
912     pool = gst_gl_buffer_pool_new (nvdec->gl_context);
913
914     if (outcaps)
915       gst_video_info_from_caps (&vinfo, outcaps);
916     size = (guint) vinfo.size;
917     min = max = 0;
918   }
919
920   config = gst_buffer_pool_get_config (pool);
921   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
922   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
923   gst_buffer_pool_set_config (pool, config);
924   if (n > 0)
925     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
926   else
927     gst_query_add_allocation_pool (query, pool, size, min, max);
928   gst_object_unref (pool);
929
930   return GST_VIDEO_DECODER_CLASS (gst_nvdec_parent_class)->decide_allocation
931       (decoder, query);
932 }
933
934 static gboolean
935 gst_nvdec_src_query (GstVideoDecoder * decoder, GstQuery * query)
936 {
937   GstNvDec *nvdec = GST_NVDEC (decoder);
938
939   switch (GST_QUERY_TYPE (query)) {
940     case GST_QUERY_CONTEXT:
941       if (gst_gl_handle_context_query (GST_ELEMENT (decoder), query,
942               nvdec->gl_display, nvdec->gl_context, nvdec->other_gl_context))
943         return TRUE;
944       break;
945     default:
946       break;
947   }
948
949   return GST_VIDEO_DECODER_CLASS (gst_nvdec_parent_class)->src_query (decoder,
950       query);
951 }
952
953 static void
954 gst_nvdec_set_context (GstElement * element, GstContext * context)
955 {
956   GstNvDec *nvdec = GST_NVDEC (element);
957   GST_DEBUG_OBJECT (nvdec, "set context");
958
959   gst_gl_handle_set_context (element, context, &nvdec->gl_display,
960       &nvdec->other_gl_context);
961
962   GST_ELEMENT_CLASS (gst_nvdec_parent_class)->set_context (element, context);
963 }