nvdec: implement flush/drain
[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 static gboolean gst_nvdec_flush (GstVideoDecoder * decoder);
220 static GstFlowReturn gst_nvdec_drain (GstVideoDecoder * decoder);
221
222 static GstStaticPadTemplate gst_nvdec_sink_template =
223     GST_STATIC_PAD_TEMPLATE (GST_VIDEO_DECODER_SINK_NAME,
224     GST_PAD_SINK, GST_PAD_ALWAYS,
225     GST_STATIC_CAPS ("video/x-h264, stream-format=byte-stream, alignment=au; "
226         "video/x-h265, stream-format=byte-stream, alignment=au; "
227         "video/mpeg, mpegversion={ 1, 2, 4 }, systemstream=false; "
228         "image/jpeg")
229     );
230
231 static GstStaticPadTemplate gst_nvdec_src_template =
232 GST_STATIC_PAD_TEMPLATE (GST_VIDEO_DECODER_SRC_NAME,
233     GST_PAD_SRC, GST_PAD_ALWAYS,
234     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
235         (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "NV12") ", texture-target=2D")
236     );
237
238 G_DEFINE_TYPE_WITH_CODE (GstNvDec, gst_nvdec, GST_TYPE_VIDEO_DECODER,
239     GST_DEBUG_CATEGORY_INIT (gst_nvdec_debug_category, "nvdec", 0,
240         "Debug category for the nvdec element"));
241
242 static void
243 gst_nvdec_class_init (GstNvDecClass * klass)
244 {
245   GstVideoDecoderClass *video_decoder_class = GST_VIDEO_DECODER_CLASS (klass);
246   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
247
248   gst_element_class_add_static_pad_template (element_class,
249       &gst_nvdec_sink_template);
250   gst_element_class_add_static_pad_template (element_class,
251       &gst_nvdec_src_template);
252
253   gst_element_class_set_static_metadata (element_class, "NVDEC video decoder",
254       "Decoder/Video", "NVDEC video decoder",
255       "Ericsson AB, http://www.ericsson.com");
256
257   video_decoder_class->start = GST_DEBUG_FUNCPTR (gst_nvdec_start);
258   video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_nvdec_stop);
259   video_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_nvdec_set_format);
260   video_decoder_class->handle_frame =
261       GST_DEBUG_FUNCPTR (gst_nvdec_handle_frame);
262   video_decoder_class->decide_allocation =
263       GST_DEBUG_FUNCPTR (gst_nvdec_decide_allocation);
264   video_decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_nvdec_src_query);
265   video_decoder_class->drain = GST_DEBUG_FUNCPTR (gst_nvdec_drain);
266   video_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_nvdec_flush);
267
268   element_class->set_context = GST_DEBUG_FUNCPTR (gst_nvdec_set_context);
269 }
270
271 static void
272 gst_nvdec_init (GstNvDec * nvdec)
273 {
274   gst_video_decoder_set_packetized (GST_VIDEO_DECODER (nvdec), TRUE);
275   gst_video_decoder_set_needs_format (GST_VIDEO_DECODER (nvdec), TRUE);
276 }
277
278 static gboolean
279 parser_sequence_callback (GstNvDec * nvdec, CUVIDEOFORMAT * format)
280 {
281   GstNvDecQueueItem *item;
282   guint width, height;
283   CUVIDDECODECREATEINFO create_info = { 0, };
284   gboolean ret = TRUE;
285
286   width = format->display_area.right - format->display_area.left;
287   height = format->display_area.bottom - format->display_area.top;
288   GST_DEBUG_OBJECT (nvdec, "width: %u, height: %u", width, height);
289
290   if (!nvdec->decoder || (nvdec->width != width || nvdec->height != height)) {
291     if (!cuda_OK (cuvidCtxLock (nvdec->cuda_context->lock, 0))) {
292       GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
293       return FALSE;
294     }
295
296     if (nvdec->decoder) {
297       GST_DEBUG_OBJECT (nvdec, "destroying decoder");
298       if (!cuda_OK (cuvidDestroyDecoder (nvdec->decoder))) {
299         GST_ERROR_OBJECT (nvdec, "failed to destroy decoder");
300         ret = FALSE;
301       } else
302         nvdec->decoder = NULL;
303     }
304
305     GST_DEBUG_OBJECT (nvdec, "creating decoder");
306     create_info.ulWidth = width;
307     create_info.ulHeight = height;
308     create_info.ulNumDecodeSurfaces = 20;
309     create_info.CodecType = format->codec;
310     create_info.ChromaFormat = format->chroma_format;
311     create_info.ulCreationFlags = cudaVideoCreate_Default;
312     create_info.display_area.left = format->display_area.left;
313     create_info.display_area.top = format->display_area.top;
314     create_info.display_area.right = format->display_area.right;
315     create_info.display_area.bottom = format->display_area.bottom;
316     create_info.OutputFormat = cudaVideoSurfaceFormat_NV12;
317     create_info.DeinterlaceMode = cudaVideoDeinterlaceMode_Weave;
318     create_info.ulTargetWidth = width;
319     create_info.ulTargetHeight = height;
320     create_info.ulNumOutputSurfaces = 1;
321     create_info.vidLock = nvdec->cuda_context->lock;
322     create_info.target_rect.left = 0;
323     create_info.target_rect.top = 0;
324     create_info.target_rect.right = width;
325     create_info.target_rect.bottom = height;
326
327     if (nvdec->decoder
328         || !cuda_OK (cuvidCreateDecoder (&nvdec->decoder, &create_info))) {
329       GST_ERROR_OBJECT (nvdec, "failed to create decoder");
330       ret = FALSE;
331     }
332
333     if (!cuda_OK (cuvidCtxUnlock (nvdec->cuda_context->lock, 0))) {
334       GST_ERROR_OBJECT (nvdec, "failed to unlock CUDA context");
335       ret = FALSE;
336     }
337   }
338
339   item = g_slice_new (GstNvDecQueueItem);
340   item->type = GST_NVDEC_QUEUE_ITEM_TYPE_SEQUENCE;
341   item->data = g_memdup (format, sizeof (CUVIDEOFORMAT));
342   g_async_queue_push (nvdec->decode_queue, item);
343
344   return ret;
345 }
346
347 static gboolean
348 parser_decode_callback (GstNvDec * nvdec, CUVIDPICPARAMS * params)
349 {
350   GstNvDecQueueItem *item;
351
352   GST_LOG_OBJECT (nvdec, "picture index: %u", params->CurrPicIdx);
353
354   if (!cuda_OK (cuvidCtxLock (nvdec->cuda_context->lock, 0)))
355     GST_WARNING_OBJECT (nvdec, "failed to lock CUDA context");
356
357   if (!cuda_OK (cuvidDecodePicture (nvdec->decoder, params)))
358     GST_WARNING_OBJECT (nvdec, "failed to decode picture");
359
360   if (!cuda_OK (cuvidCtxUnlock (nvdec->cuda_context->lock, 0)))
361     GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
362
363   item = g_slice_new (GstNvDecQueueItem);
364   item->type = GST_NVDEC_QUEUE_ITEM_TYPE_DECODE;
365   item->data = g_memdup (params, sizeof (CUVIDPICPARAMS));
366   ((CUVIDPICPARAMS *) item->data)->pBitstreamData = NULL;
367   ((CUVIDPICPARAMS *) item->data)->pSliceDataOffsets = NULL;
368   g_async_queue_push (nvdec->decode_queue, item);
369
370   return TRUE;
371 }
372
373 static gboolean
374 parser_display_callback (GstNvDec * nvdec, CUVIDPARSERDISPINFO * dispinfo)
375 {
376   GstNvDecQueueItem *item;
377
378   GST_LOG_OBJECT (nvdec, "picture index: %u", dispinfo->picture_index);
379
380   item = g_slice_new (GstNvDecQueueItem);
381   item->type = GST_NVDEC_QUEUE_ITEM_TYPE_DISPLAY;
382   item->data = g_memdup (dispinfo, sizeof (CUVIDPARSERDISPINFO));
383   g_async_queue_push (nvdec->decode_queue, item);
384
385   return TRUE;
386 }
387
388 static gboolean
389 gst_nvdec_start (GstVideoDecoder * decoder)
390 {
391   GstNvDec *nvdec = GST_NVDEC (decoder);
392
393   GST_DEBUG_OBJECT (nvdec, "creating CUDA context");
394   nvdec->cuda_context = g_object_new (gst_nvdec_cuda_context_get_type (), NULL);
395   nvdec->decode_queue = g_async_queue_new ();
396
397   if (!nvdec->cuda_context->context || !nvdec->cuda_context->lock) {
398     GST_ERROR_OBJECT (nvdec, "failed to create CUDA context or lock");
399     return FALSE;
400   }
401
402   return TRUE;
403 }
404
405 static gboolean
406 maybe_destroy_decoder_and_parser (GstNvDec * nvdec)
407 {
408   gboolean ret = TRUE;
409
410   if (!cuda_OK (cuvidCtxLock (nvdec->cuda_context->lock, 0))) {
411     GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
412     return FALSE;
413   }
414
415   if (nvdec->decoder) {
416     GST_DEBUG_OBJECT (nvdec, "destroying decoder");
417     ret = cuda_OK (cuvidDestroyDecoder (nvdec->decoder));
418     if (ret)
419       nvdec->decoder = NULL;
420     else
421       GST_ERROR_OBJECT (nvdec, "failed to destroy decoder");
422   }
423
424   if (!cuda_OK (cuvidCtxUnlock (nvdec->cuda_context->lock, 0))) {
425     GST_ERROR_OBJECT (nvdec, "failed to unlock CUDA context");
426     return FALSE;
427   }
428
429   if (nvdec->parser) {
430     GST_DEBUG_OBJECT (nvdec, "destroying parser");
431     if (!cuda_OK (cuvidDestroyVideoParser (nvdec->parser))) {
432       GST_ERROR_OBJECT (nvdec, "failed to destroy parser");
433       return FALSE;
434     }
435     nvdec->parser = NULL;
436   }
437
438   return ret;
439 }
440
441 static gboolean
442 gst_nvdec_stop (GstVideoDecoder * decoder)
443 {
444   GstNvDec *nvdec = GST_NVDEC (decoder);
445   GstNvDecQueueItem *item;
446
447   GST_DEBUG_OBJECT (nvdec, "stop");
448
449   if (!maybe_destroy_decoder_and_parser (nvdec))
450     return FALSE;
451
452   if (nvdec->cuda_context) {
453     g_object_unref (nvdec->cuda_context);
454     nvdec->cuda_context = NULL;
455   }
456
457   if (nvdec->gl_context) {
458     gst_object_unref (nvdec->gl_context);
459     nvdec->gl_context = NULL;
460   }
461
462   if (nvdec->other_gl_context) {
463     gst_object_unref (nvdec->other_gl_context);
464     nvdec->other_gl_context = NULL;
465   }
466
467   if (nvdec->gl_display) {
468     gst_object_unref (nvdec->gl_display);
469     nvdec->gl_display = NULL;
470   }
471
472   if (nvdec->input_state) {
473     gst_video_codec_state_unref (nvdec->input_state);
474     nvdec->input_state = NULL;
475   }
476
477   if (nvdec->decode_queue) {
478     if (g_async_queue_length (nvdec->decode_queue) > 0) {
479       GST_INFO_OBJECT (nvdec, "decode queue not empty");
480
481       while ((item = g_async_queue_try_pop (nvdec->decode_queue))) {
482         g_free (item->data);
483         g_slice_free (GstNvDecQueueItem, item);
484       }
485     }
486     g_async_queue_unref (nvdec->decode_queue);
487     nvdec->decode_queue = NULL;
488   }
489
490   return TRUE;
491 }
492
493 static gboolean
494 gst_nvdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
495 {
496   GstNvDec *nvdec = GST_NVDEC (decoder);
497   GstStructure *s;
498   const gchar *caps_name;
499   gint mpegversion = 0;
500   CUVIDPARSERPARAMS parser_params = { 0, };
501
502   GST_DEBUG_OBJECT (nvdec, "set format");
503
504   if (nvdec->input_state)
505     gst_video_codec_state_unref (nvdec->input_state);
506
507   nvdec->input_state = gst_video_codec_state_ref (state);
508
509   if (!maybe_destroy_decoder_and_parser (nvdec))
510     return FALSE;
511
512   s = gst_caps_get_structure (state->caps, 0);
513   caps_name = gst_structure_get_name (s);
514   GST_DEBUG_OBJECT (nvdec, "codec is %s", caps_name);
515
516   if (!g_strcmp0 (caps_name, "video/mpeg")) {
517     if (gst_structure_get_int (s, "mpegversion", &mpegversion)) {
518       switch (mpegversion) {
519         case 1:
520           parser_params.CodecType = cudaVideoCodec_MPEG1;
521           break;
522         case 2:
523           parser_params.CodecType = cudaVideoCodec_MPEG2;
524           break;
525         case 4:
526           parser_params.CodecType = cudaVideoCodec_MPEG4;
527           break;
528       }
529     }
530     if (!mpegversion) {
531       GST_ERROR_OBJECT (nvdec, "could not get MPEG version");
532       return FALSE;
533     }
534   } else if (!g_strcmp0 (caps_name, "video/x-h264")) {
535     parser_params.CodecType = cudaVideoCodec_H264;
536   } else if (!g_strcmp0 (caps_name, "image/jpeg")) {
537     parser_params.CodecType = cudaVideoCodec_JPEG;
538   } else if (!g_strcmp0 (caps_name, "video/x-h265")) {
539     parser_params.CodecType = cudaVideoCodec_HEVC;
540   } else {
541     GST_ERROR_OBJECT (nvdec, "failed to determine codec type");
542     return FALSE;
543   }
544
545   parser_params.ulMaxNumDecodeSurfaces = 20;
546   parser_params.ulErrorThreshold = 100;
547   parser_params.ulMaxDisplayDelay = 0;
548   parser_params.ulClockRate = GST_SECOND;
549   parser_params.pUserData = nvdec;
550   parser_params.pfnSequenceCallback =
551       (PFNVIDSEQUENCECALLBACK) parser_sequence_callback;
552   parser_params.pfnDecodePicture =
553       (PFNVIDDECODECALLBACK) parser_decode_callback;
554   parser_params.pfnDisplayPicture =
555       (PFNVIDDISPLAYCALLBACK) parser_display_callback;
556
557   GST_DEBUG_OBJECT (nvdec, "creating parser");
558   if (!cuda_OK (cuvidCreateVideoParser (&nvdec->parser, &parser_params))) {
559     GST_ERROR_OBJECT (nvdec, "failed to create parser");
560     return FALSE;
561   }
562
563   return TRUE;
564 }
565
566 static void
567 copy_video_frame_to_gl_textures (GstGLContext * context, gpointer * args)
568 {
569   GstNvDec *nvdec = GST_NVDEC (args[0]);
570   CUVIDPARSERDISPINFO *dispinfo = (CUVIDPARSERDISPINFO *) args[1];
571   CUgraphicsResource *resources = (CUgraphicsResource *) args[2];
572   guint num_resources = GPOINTER_TO_UINT (args[3]);
573   CUVIDPROCPARAMS proc_params = { 0, };
574   CUdeviceptr dptr;
575   CUarray array;
576   guint pitch, i;
577   CUDA_MEMCPY2D mcpy2d = { 0, };
578
579   GST_LOG_OBJECT (nvdec, "picture index: %u", dispinfo->picture_index);
580
581   proc_params.progressive_frame = dispinfo->progressive_frame;
582   proc_params.top_field_first = dispinfo->top_field_first;
583   proc_params.unpaired_field = dispinfo->repeat_first_field == -1;
584
585   if (!cuda_OK (cuvidCtxLock (nvdec->cuda_context->lock, 0))) {
586     GST_WARNING_OBJECT (nvdec, "failed to lock CUDA context");
587     return;
588   }
589
590   if (!cuda_OK (cuvidMapVideoFrame (nvdec->decoder, dispinfo->picture_index,
591               &dptr, &pitch, &proc_params))) {
592     GST_WARNING_OBJECT (nvdec, "failed to map CUDA video frame");
593     goto unlock_cuda_context;
594   }
595
596   if (!cuda_OK (cuGraphicsMapResources (num_resources, resources, NULL))) {
597     GST_WARNING_OBJECT (nvdec, "failed to map CUDA resources");
598     goto unmap_video_frame;
599   }
600
601   mcpy2d.srcMemoryType = CU_MEMORYTYPE_DEVICE;
602   mcpy2d.srcPitch = pitch;
603   mcpy2d.dstMemoryType = CU_MEMORYTYPE_ARRAY;
604   mcpy2d.dstPitch = nvdec->width;
605   mcpy2d.WidthInBytes = nvdec->width;
606
607   for (i = 0; i < num_resources; i++) {
608     if (!cuda_OK (cuGraphicsSubResourceGetMappedArray (&array, resources[i], 0,
609                 0))) {
610       GST_WARNING_OBJECT (nvdec, "failed to map CUDA array");
611       break;
612     }
613
614     mcpy2d.srcDevice = dptr + (i * pitch * nvdec->height);
615     mcpy2d.dstArray = array;
616     mcpy2d.Height = nvdec->height / (i + 1);
617
618     if (!cuda_OK (cuMemcpy2D (&mcpy2d)))
619       GST_WARNING_OBJECT (nvdec, "memcpy to mapped array failed");
620   }
621
622   if (!cuda_OK (cuGraphicsUnmapResources (num_resources, resources, NULL)))
623     GST_WARNING_OBJECT (nvdec, "failed to unmap CUDA resources");
624
625 unmap_video_frame:
626   if (!cuda_OK (cuvidUnmapVideoFrame (nvdec->decoder, dptr)))
627     GST_WARNING_OBJECT (nvdec, "failed to unmap CUDA video frame");
628
629 unlock_cuda_context:
630   if (!cuda_OK (cuvidCtxUnlock (nvdec->cuda_context->lock, 0)))
631     GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
632 }
633
634 static GstFlowReturn
635 handle_pending_frames (GstNvDec * nvdec)
636 {
637   GstVideoDecoder *decoder = GST_VIDEO_DECODER (nvdec);
638   GList *pending_frames, *list, *tmp;
639   GstVideoCodecFrame *pending_frame;
640   guint frame_number;
641   GstClockTime latency = 0;
642   GstNvDecQueueItem *item;
643   CUVIDEOFORMAT *format;
644   GstVideoCodecState *state;
645   guint width, height, fps_n, fps_d, i, num_resources;
646   CUVIDPICPARAMS *decode_params;
647   CUVIDPARSERDISPINFO *dispinfo;
648   CUgraphicsResource *resources;
649   gpointer args[4];
650   GstMemory *mem;
651   GstFlowReturn ret = GST_FLOW_OK;
652
653   /* find the oldest unused, unfinished frame */
654   pending_frames = list = gst_video_decoder_get_frames (decoder);
655   for (; pending_frames; pending_frames = pending_frames->next) {
656     pending_frame = pending_frames->data;
657     frame_number =
658         GPOINTER_TO_UINT (gst_video_codec_frame_get_user_data (pending_frame));
659     if (!frame_number)
660       break;
661     latency += pending_frame->duration;
662   }
663
664   while (ret == GST_FLOW_OK && pending_frames
665       && (item =
666           (GstNvDecQueueItem *) g_async_queue_try_pop (nvdec->decode_queue))) {
667     switch (item->type) {
668       case GST_NVDEC_QUEUE_ITEM_TYPE_SEQUENCE:
669         if (!nvdec->decoder) {
670           GST_ERROR_OBJECT (nvdec, "no decoder");
671           ret = GST_FLOW_ERROR;
672           break;
673         }
674
675         format = (CUVIDEOFORMAT *) item->data;
676         width = format->display_area.right - format->display_area.left;
677         height = format->display_area.bottom - format->display_area.top;
678         fps_n = format->frame_rate.numerator;
679         fps_d = MAX (1, format->frame_rate.denominator);
680
681         if (!gst_pad_has_current_caps (GST_VIDEO_DECODER_SRC_PAD (decoder))
682             || width != nvdec->width || height != nvdec->height
683             || fps_n != nvdec->fps_n || fps_d != nvdec->fps_d) {
684           nvdec->width = width;
685           nvdec->height = height;
686           nvdec->fps_n = fps_n;
687           nvdec->fps_d = fps_d;
688
689           state = gst_video_decoder_set_output_state (decoder,
690               GST_VIDEO_FORMAT_NV12, nvdec->width, nvdec->height,
691               nvdec->input_state);
692           state->caps = gst_caps_new_simple ("video/x-raw",
693               "format", G_TYPE_STRING, "NV12",
694               "width", G_TYPE_INT, nvdec->width,
695               "height", G_TYPE_INT, nvdec->height,
696               "framerate", GST_TYPE_FRACTION, nvdec->fps_n, nvdec->fps_d,
697               "interlace-mode", G_TYPE_STRING, format->progressive_sequence
698               ? "progressive" : "interleaved",
699               "texture-target", G_TYPE_STRING, "2D", NULL);
700           gst_caps_set_features (state->caps, 0,
701               gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_GL_MEMORY, NULL));
702           gst_video_codec_state_unref (state);
703
704           if (!gst_video_decoder_negotiate (decoder)) {
705             GST_WARNING_OBJECT (nvdec, "failed to negotiate with downstream");
706             ret = GST_FLOW_NOT_NEGOTIATED;
707             break;
708           }
709         }
710
711         break;
712
713       case GST_NVDEC_QUEUE_ITEM_TYPE_DECODE:
714         decode_params = (CUVIDPICPARAMS *) item->data;
715         pending_frame = pending_frames->data;
716         frame_number = decode_params->CurrPicIdx + 1;
717         gst_video_codec_frame_set_user_data (pending_frame,
718             GUINT_TO_POINTER (frame_number), NULL);
719
720         if (decode_params->intra_pic_flag)
721           GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (pending_frame);
722
723         if (!GST_CLOCK_TIME_IS_VALID (pending_frame->duration)) {
724           pending_frame->duration =
725               nvdec->fps_n ? GST_SECOND * nvdec->fps_d / nvdec->fps_n : 0;
726         }
727         latency += pending_frame->duration;
728
729         pending_frames = pending_frames->next;
730
731         break;
732
733       case GST_NVDEC_QUEUE_ITEM_TYPE_DISPLAY:
734         dispinfo = (CUVIDPARSERDISPINFO *) item->data;
735         for (pending_frame = NULL, tmp = list; !pending_frame && tmp;
736             tmp = tmp->next) {
737           frame_number =
738               GPOINTER_TO_UINT (gst_video_codec_frame_get_user_data
739               (tmp->data));
740           if (frame_number == dispinfo->picture_index + 1)
741             pending_frame = tmp->data;
742         }
743         if (!pending_frame) {
744           GST_INFO_OBJECT (nvdec, "no frame with number %u",
745               dispinfo->picture_index + 1);
746           break;
747         }
748
749         if (dispinfo->timestamp != pending_frame->pts) {
750           GST_INFO_OBJECT (nvdec,
751               "timestamp mismatch, diff: %" GST_STIME_FORMAT,
752               GST_STIME_ARGS (GST_CLOCK_DIFF (dispinfo->timestamp,
753                       pending_frame->pts)));
754           pending_frame->pts = dispinfo->timestamp;
755         }
756
757         if (latency > nvdec->min_latency) {
758           nvdec->min_latency = latency;
759           gst_video_decoder_set_latency (decoder, nvdec->min_latency,
760               nvdec->min_latency);
761           GST_DEBUG_OBJECT (nvdec, "latency: %" GST_TIME_FORMAT,
762               GST_TIME_ARGS (latency));
763         }
764         latency -= pending_frame->duration;
765
766         ret = gst_video_decoder_allocate_output_frame (decoder, pending_frame);
767         if (ret != GST_FLOW_OK) {
768           GST_WARNING_OBJECT (nvdec, "failed to allocate output frame");
769           break;
770         }
771
772         num_resources = gst_buffer_n_memory (pending_frame->output_buffer);
773         resources = g_new (CUgraphicsResource, num_resources);
774
775         for (i = 0; i < num_resources; i++) {
776           mem = gst_buffer_get_memory (pending_frame->output_buffer, i);
777           resources[i] =
778               ensure_cuda_graphics_resource (mem, nvdec->cuda_context);
779           GST_MINI_OBJECT_FLAG_SET (mem,
780               GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD);
781           gst_memory_unref (mem);
782         }
783
784         args[0] = nvdec;
785         args[1] = dispinfo;
786         args[2] = resources;
787         args[3] = GUINT_TO_POINTER (num_resources);
788         gst_gl_context_thread_add (nvdec->gl_context,
789             (GstGLContextThreadFunc) copy_video_frame_to_gl_textures, args);
790         g_free (resources);
791
792         if (!dispinfo->progressive_frame) {
793           GST_BUFFER_FLAG_SET (pending_frame->output_buffer,
794               GST_VIDEO_BUFFER_FLAG_INTERLACED);
795
796           if (dispinfo->top_field_first) {
797             GST_BUFFER_FLAG_SET (pending_frame->output_buffer,
798                 GST_VIDEO_BUFFER_FLAG_TFF);
799           }
800           if (dispinfo->repeat_first_field == -1) {
801             GST_BUFFER_FLAG_SET (pending_frame->output_buffer,
802                 GST_VIDEO_BUFFER_FLAG_ONEFIELD);
803           } else {
804             GST_BUFFER_FLAG_SET (pending_frame->output_buffer,
805                 GST_VIDEO_BUFFER_FLAG_RFF);
806           }
807         }
808
809         list = g_list_remove (list, pending_frame);
810         ret = gst_video_decoder_finish_frame (decoder, pending_frame);
811         if (ret != GST_FLOW_OK)
812           GST_INFO_OBJECT (nvdec, "failed to finish frame");
813
814         break;
815
816       default:
817         g_assert_not_reached ();
818     }
819
820     g_free (item->data);
821     g_slice_free (GstNvDecQueueItem, item);
822   }
823
824   g_list_free_full (list, (GDestroyNotify) gst_video_codec_frame_unref);
825
826   return ret;
827 }
828
829 static GstFlowReturn
830 gst_nvdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
831 {
832   GstNvDec *nvdec = GST_NVDEC (decoder);
833   GstMapInfo map_info = GST_MAP_INFO_INIT;
834   CUVIDSOURCEDATAPACKET packet = { 0, };
835
836   GST_LOG_OBJECT (nvdec, "handle frame");
837
838   gst_video_codec_frame_set_user_data (frame, GUINT_TO_POINTER (0), NULL);
839
840   if (!gst_buffer_map (frame->input_buffer, &map_info, GST_MAP_READ)) {
841     GST_ERROR_OBJECT (nvdec, "failed to map input buffer");
842     gst_video_codec_frame_unref (frame);
843     return GST_FLOW_ERROR;
844   }
845
846   packet.payload_size = (gulong) map_info.size;
847   packet.payload = map_info.data;
848   packet.timestamp = frame->pts;
849   packet.flags = CUVID_PKT_TIMESTAMP;
850
851   if (GST_BUFFER_IS_DISCONT (frame->input_buffer))
852     packet.flags |= CUVID_PKT_DISCONTINUITY;
853
854   if (!cuda_OK (cuvidParseVideoData (nvdec->parser, &packet)))
855     GST_WARNING_OBJECT (nvdec, "parser failed");
856
857   gst_buffer_unmap (frame->input_buffer, &map_info);
858   gst_video_codec_frame_unref (frame);
859
860   return handle_pending_frames (nvdec);
861 }
862
863 static gboolean
864 gst_nvdec_flush (GstVideoDecoder * decoder)
865 {
866   GstNvDec *nvdec = GST_NVDEC (decoder);
867   CUVIDSOURCEDATAPACKET packet = { 0, };
868
869   GST_DEBUG_OBJECT (nvdec, "flush");
870
871   packet.payload_size = 0;
872   packet.payload = NULL;
873   packet.flags = CUVID_PKT_ENDOFSTREAM;
874
875   if (!cuda_OK (cuvidParseVideoData (nvdec->parser, &packet)))
876     GST_WARNING_OBJECT (nvdec, "parser failed");
877
878   handle_pending_frames (nvdec);
879
880   return TRUE;
881 }
882
883 static GstFlowReturn
884 gst_nvdec_drain (GstVideoDecoder * decoder)
885 {
886   GstNvDec *nvdec = GST_NVDEC (decoder);
887   CUVIDSOURCEDATAPACKET packet = { 0, };
888
889   GST_DEBUG_OBJECT (nvdec, "draining decoder");
890
891   packet.payload_size = 0;
892   packet.payload = NULL;
893   packet.flags = CUVID_PKT_ENDOFSTREAM;
894
895   if (!cuda_OK (cuvidParseVideoData (nvdec->parser, &packet)))
896     GST_WARNING_OBJECT (nvdec, "parser failed");
897
898   return handle_pending_frames (nvdec);
899 }
900
901 static gboolean
902 gst_nvdec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
903 {
904   GstNvDec *nvdec = GST_NVDEC (decoder);
905   GstCaps *outcaps;
906   GstBufferPool *pool = NULL;
907   guint n, size, min, max;
908   GstVideoInfo vinfo = { 0, };
909   GstStructure *config;
910
911   GST_DEBUG_OBJECT (nvdec, "decide allocation");
912
913   if (!gst_gl_ensure_element_data (nvdec, &nvdec->gl_display,
914           &nvdec->other_gl_context)) {
915     GST_ERROR_OBJECT (nvdec, "failed to ensure OpenGL display");
916     return FALSE;
917   }
918
919   if (!gst_gl_query_local_gl_context (GST_ELEMENT (decoder), GST_PAD_SRC,
920           &nvdec->gl_context)) {
921     GST_INFO_OBJECT (nvdec, "failed to query local OpenGL context");
922     if (nvdec->gl_context)
923       gst_object_unref (nvdec->gl_context);
924     nvdec->gl_context =
925         gst_gl_display_get_gl_context_for_thread (nvdec->gl_display, NULL);
926     if (!nvdec->gl_context
927         || !gst_gl_display_add_context (nvdec->gl_display, nvdec->gl_context)) {
928       if (nvdec->gl_context)
929         gst_object_unref (nvdec->gl_context);
930       if (!gst_gl_display_create_context (nvdec->gl_display,
931               nvdec->other_gl_context, &nvdec->gl_context, NULL)) {
932         GST_ERROR_OBJECT (nvdec, "failed to create OpenGL context");
933         return FALSE;
934       }
935       if (!gst_gl_display_add_context (nvdec->gl_display, nvdec->gl_context)) {
936         GST_ERROR_OBJECT (nvdec,
937             "failed to add the OpenGL context to the display");
938         return FALSE;
939       }
940     }
941   }
942
943   gst_query_parse_allocation (query, &outcaps, NULL);
944   n = gst_query_get_n_allocation_pools (query);
945   if (n > 0) {
946     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
947     if (!GST_IS_GL_BUFFER_POOL (pool)) {
948       gst_object_unref (pool);
949       pool = NULL;
950     }
951   }
952
953   if (!pool) {
954     pool = gst_gl_buffer_pool_new (nvdec->gl_context);
955
956     if (outcaps)
957       gst_video_info_from_caps (&vinfo, outcaps);
958     size = (guint) vinfo.size;
959     min = max = 0;
960   }
961
962   config = gst_buffer_pool_get_config (pool);
963   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
964   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
965   gst_buffer_pool_set_config (pool, config);
966   if (n > 0)
967     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
968   else
969     gst_query_add_allocation_pool (query, pool, size, min, max);
970   gst_object_unref (pool);
971
972   return GST_VIDEO_DECODER_CLASS (gst_nvdec_parent_class)->decide_allocation
973       (decoder, query);
974 }
975
976 static gboolean
977 gst_nvdec_src_query (GstVideoDecoder * decoder, GstQuery * query)
978 {
979   GstNvDec *nvdec = GST_NVDEC (decoder);
980
981   switch (GST_QUERY_TYPE (query)) {
982     case GST_QUERY_CONTEXT:
983       if (gst_gl_handle_context_query (GST_ELEMENT (decoder), query,
984               nvdec->gl_display, nvdec->gl_context, nvdec->other_gl_context))
985         return TRUE;
986       break;
987     default:
988       break;
989   }
990
991   return GST_VIDEO_DECODER_CLASS (gst_nvdec_parent_class)->src_query (decoder,
992       query);
993 }
994
995 static void
996 gst_nvdec_set_context (GstElement * element, GstContext * context)
997 {
998   GstNvDec *nvdec = GST_NVDEC (element);
999   GST_DEBUG_OBJECT (nvdec, "set context");
1000
1001   gst_gl_handle_set_context (element, context, &nvdec->gl_display,
1002       &nvdec->other_gl_context);
1003
1004   GST_ELEMENT_CLASS (gst_nvdec_parent_class)->set_context (element, context);
1005 }