2 * Copyright (C) 2017 Ericsson AB. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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
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.
33 #include "gstcudautils.h"
34 #include "gstcudabufferpool.h"
38 GST_DEBUG_CATEGORY_EXTERN (gst_nvdec_debug);
39 #define GST_CAT_DEFAULT gst_nvdec_debug
41 #define DEFAULT_MAX_DISPLAY_DELAY -1
46 PROP_MAX_DISPLAY_DELAY,
49 #ifdef HAVE_NVCODEC_GST_GL
50 #define SUPPORTED_GL_APIS (GST_GL_API_OPENGL | GST_GL_API_OPENGL3 | GST_GL_API_GLES2)
53 gst_nvdec_copy_device_to_gl (GstNvDec * nvdec,
54 CUVIDPARSERDISPINFO * dispinfo, GstBuffer * output_buffer);
58 gst_nvdec_copy_device_to_memory (GstNvDec * nvdec,
59 CUVIDPARSERDISPINFO * dispinfo, GstBuffer * output_buffer);
61 #ifdef HAVE_NVCODEC_GST_GL
62 typedef struct _GstNvDecRegisterResourceData
65 GstCudaGraphicsResource *resource;
68 } GstNvDecRegisterResourceData;
71 register_cuda_resource (GstGLContext * context,
72 GstNvDecRegisterResourceData * data)
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;
82 if (!gst_cuda_context_push (nvdec->cuda_ctx)) {
83 GST_WARNING_OBJECT (nvdec, "failed to push CUDA context");
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;
91 GST_LOG_OBJECT (nvdec,
92 "register glbuffer %d to CUDA resource", gl_buf_obj->id);
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)) {
101 GST_WARNING_OBJECT (nvdec, "failed to register memory");
104 gst_memory_unmap (mem, &map_info);
106 GST_WARNING_OBJECT (nvdec, "failed to map memory");
109 if (!gst_cuda_context_pop (NULL))
110 GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
113 static GstCudaGraphicsResource *
114 ensure_cuda_graphics_resource (GstMemory * mem, GstNvDec * nvdec)
117 GstCudaGraphicsResource *cgr_info;
118 GstNvDecRegisterResourceData data;
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);
126 quark = gst_cuda_quark_from_id (GST_CUDA_QUARK_GRAPHICS_RESOURCE);
128 cgr_info = gst_mini_object_get_qdata (GST_MINI_OBJECT (mem), quark);
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);
134 data.resource = cgr_info;
136 gst_gl_context_thread_add ((GstGLContext *) cgr_info->graphics_context,
137 (GstGLContextThreadFunc) register_cuda_resource, &data);
139 GST_WARNING_OBJECT (nvdec, "could not register resource");
140 gst_cuda_graphics_resource_free (cgr_info);
145 gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), quark, cgr_info,
146 (GDestroyNotify) gst_cuda_graphics_resource_free);
151 #endif /* HAVE_NVCODEC_GST_GL */
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,
163 static void gst_nvdec_set_context (GstElement * element, GstContext * context);
164 static gboolean gst_nvdec_src_query (GstVideoDecoder * decoder,
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);
174 #define gst_nvdec_parent_class parent_class
175 G_DEFINE_ABSTRACT_TYPE (GstNvDec, gst_nvdec, GST_TYPE_VIDEO_DECODER);
178 gst_nv_dec_set_property (GObject * object, guint prop_id, const GValue * value,
181 GstNvDec *nvdec = GST_NVDEC (object);
184 case PROP_MAX_DISPLAY_DELAY:
185 nvdec->max_display_delay = g_value_get_int (value);
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194 gst_nv_dec_get_property (GObject * object, guint prop_id, GValue * value,
197 GstNvDec *nvdec = GST_NVDEC (object);
200 case PROP_MAX_DISPLAY_DELAY:
201 g_value_set_int (value, nvdec->max_display_delay);
204 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
210 gst_nvdec_class_init (GstNvDecClass * klass)
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);
216 gobject_class->set_property = gst_nv_dec_set_property;
217 gobject_class->get_property = gst_nv_dec_get_property;
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);
234 element_class->set_context = GST_DEBUG_FUNCPTR (gst_nvdec_set_context);
235 gst_type_mark_as_plugin_api (GST_TYPE_NVDEC, 0);
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 "
241 -1, G_MAXINT, DEFAULT_MAX_DISPLAY_DELAY,
242 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
246 gst_nvdec_init (GstNvDec * nvdec)
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);
253 static cudaVideoSurfaceFormat
254 get_cuda_surface_format_from_gst (GstVideoFormat 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;
270 g_assert_not_reached ();
274 return cudaVideoSurfaceFormat_NV12;
278 calculate_num_decode_surface (cudaVideoCodec codec, guint width, guint height)
281 case cudaVideoCodec_VP9:
283 case cudaVideoCodec_H264:
284 case cudaVideoCodec_H264_SVC:
285 case cudaVideoCodec_H264_MVC:
287 case cudaVideoCodec_HEVC:{
290 const gint MaxDpbPicBuf = 6;
291 gint PicSizeInSamplesY;
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;
303 max_dpb_size = MaxDpbPicBuf;
305 max_dpb_size = MIN (max_dpb_size, 16);
307 return max_dpb_size + 4;
317 gst_nvdec_get_max_display_delay (GstNvDec * nvdec)
319 return nvdec->max_display_delay >= 0 ? nvdec->max_display_delay :
320 (nvdec->is_live ? 0 : 4);
324 gst_nvdec_get_latency (GstNvDec * nvdec)
328 if (!nvdec->input_state)
330 fps_n = GST_VIDEO_INFO_FPS_N (&nvdec->input_state->info);
331 fps_d = GST_VIDEO_INFO_FPS_D (&nvdec->input_state->info);
333 /* We assume 25 fps if the input framerate is invalid */
334 if (fps_n < 1 || fps_d < 1) {
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);
343 /* 0: fail, 1: succeeded, > 1: override dpb size of parser
344 * (set by CUVIDPARSERPARAMS::ulMaxNumDecodeSurfaces while creating parser) */
346 parser_sequence_callback (GstNvDec * nvdec, CUVIDEOFORMAT * format)
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;
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;
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;
373 out_format = GST_VIDEO_FORMAT_Y444_16BE;
376 GST_ERROR_OBJECT (nvdec, "Unknown 4:4:4 format bitdepth %d",
377 format->bit_depth_luma_minus8 + 8);
379 nvdec->last_ret = GST_FLOW_NOT_NEGOTIATED;
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;
390 out_format = GST_VIDEO_FORMAT_P010_10BE;
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;
396 out_format = GST_VIDEO_FORMAT_P016_BE;
399 GST_ERROR_OBJECT (nvdec, "Unknown 4:2:0 format bitdepth %d",
400 format->bit_depth_luma_minus8 + 8);
402 nvdec->last_ret = GST_FLOW_NOT_NEGOTIATED;
407 GST_ERROR_OBJECT (nvdec, "unhandled chroma format %d, bitdepth %d",
408 format->chroma_format, format->bit_depth_luma_minus8 + 8);
410 nvdec->last_ret = GST_FLOW_NOT_NEGOTIATED;
414 GST_DEBUG_OBJECT (nvdec,
415 "out format: %s", gst_video_format_to_string (out_format));
417 GST_DEBUG_OBJECT (nvdec, "width: %u, height: %u", width, height);
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);
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);
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);
436 if (nvdec->input_state->caps)
437 in_s = gst_caps_get_structure (nvdec->input_state->caps, 0);
439 /* Set colorimetry when upstream did not provide it */
440 if (in_s && !gst_structure_has_field (in_s, "colorimetry")) {
441 GstVideoColorimetry colorimetry = { 0, };
443 if (format->video_signal_description.video_full_range_flag)
444 colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
446 colorimetry.range = GST_VIDEO_COLOR_RANGE_16_235;
448 colorimetry.primaries =
449 gst_video_color_primaries_from_iso
450 (format->video_signal_description.color_primaries);
452 colorimetry.transfer =
453 gst_video_transfer_function_from_iso
454 (format->video_signal_description.transfer_characteristics);
457 gst_video_color_matrix_from_iso
458 (format->video_signal_description.matrix_coefficients);
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;
472 out_info->colorimetry = in_info->colorimetry;
475 if (format->progressive_sequence) {
476 out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
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;
484 out_info->interlace_mode = GST_VIDEO_INTERLACE_MODE_MIXED;
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;
491 GST_DEBUG_OBJECT (nvdec,
492 "Num decode surface: %d", nvdec->num_decode_surface);
494 nvdec->num_decode_surface =
495 calculate_num_decode_surface (format->codec, width, height);
497 GST_DEBUG_OBJECT (nvdec,
498 "Calculated num decode surface: %d", nvdec->num_decode_surface);
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,
507 if (!nvdec->decoder || !gst_video_info_is_equal (out_info, &prev_out_info)) {
510 if (!gst_cuda_context_push (ctx)) {
511 GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
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");
521 nvdec->decoder = NULL;
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;
547 || !gst_cuda_result (CuvidCreateDecoder (&nvdec->decoder,
549 GST_ERROR_OBJECT (nvdec, "failed to create decoder");
553 if (!gst_cuda_context_pop (NULL)) {
554 GST_ERROR_OBJECT (nvdec, "failed to unlock CUDA context");
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;
566 return nvdec->num_decode_surface;
569 nvdec->last_ret = GST_FLOW_ERROR;
574 gst_nvdec_negotiate (GstVideoDecoder * decoder)
576 GstNvDec *nvdec = GST_NVDEC (decoder);
577 GstVideoCodecState *state;
579 GstVideoInfo *out_info = &nvdec->out_info;
582 GST_DEBUG_OBJECT (nvdec, "negotiate");
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;
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;
594 state->caps = gst_video_info_to_caps (&state->info);
595 nvdec->mem_type = GST_NVDEC_MEM_TYPE_SYSTEM;
599 caps = gst_pad_get_allowed_caps (GST_VIDEO_DECODER_SRC_PAD (nvdec));
600 GST_DEBUG_OBJECT (nvdec, "Allowed caps %" GST_PTR_FORMAT, caps);
602 if (!caps || gst_caps_is_any (caps)) {
603 GST_DEBUG_OBJECT (nvdec,
604 "cannot determine output format, use system memory");
606 GstCapsFeatures *features;
607 guint size = gst_caps_get_size (caps);
609 gboolean have_cuda = FALSE;
610 gboolean have_gl = FALSE;
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");
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");
631 nvdec->mem_type = GST_NVDEC_MEM_TYPE_CUDA;
633 nvdec->mem_type = GST_NVDEC_MEM_TYPE_GL;
635 gst_clear_caps (&caps);
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;
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));
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,
663 GST_DEBUG_OBJECT (nvdec, "use system memory");
667 if (nvdec->output_state)
668 gst_video_codec_state_unref (nvdec->output_state);
670 nvdec->output_state = state;
672 ret = GST_VIDEO_DECODER_CLASS (parent_class)->negotiate (decoder);
675 GST_ERROR_OBJECT (nvdec, "failed to negotiate with downstream");
676 nvdec->last_ret = GST_FLOW_NOT_NEGOTIATED;
682 static gboolean CUDAAPI
683 parser_decode_callback (GstNvDec * nvdec, CUVIDPICPARAMS * params)
685 GList *iter, *pending_frames;
686 GstCudaContext *ctx = nvdec->cuda_ctx;
688 GST_LOG_OBJECT (nvdec, "picture index: %u", params->CurrPicIdx);
690 if (!gst_cuda_context_push (ctx)) {
691 GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
695 if (!gst_cuda_result (CuvidDecodePicture (nvdec->decoder, params))) {
696 GST_ERROR_OBJECT (nvdec, "failed to decode picture");
700 if (!gst_cuda_context_pop (NULL)) {
701 GST_ERROR_OBJECT (nvdec, "failed to unlock CUDA context");
705 pending_frames = gst_video_decoder_get_frames (GST_VIDEO_DECODER (nvdec));
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 */
712 for (iter = pending_frames; iter; iter = g_list_next (iter)) {
714 GstVideoCodecFrame *frame = (GstVideoCodecFrame *) iter->data;
715 gboolean set_data = FALSE;
717 id = GPOINTER_TO_UINT (gst_video_codec_frame_get_user_data (frame));
718 if (G_UNLIKELY (nvdec->state == GST_NVDEC_STATE_DECODE)) {
720 GST_LOG_OBJECT (nvdec, "reset the last user data");
728 gst_video_codec_frame_set_user_data (frame,
729 GUINT_TO_POINTER (params->CurrPicIdx + 1), NULL);
734 nvdec->state = GST_NVDEC_STATE_DECODE;
736 g_list_free_full (pending_frames,
737 (GDestroyNotify) gst_video_codec_frame_unref);
742 nvdec->last_ret = GST_FLOW_ERROR;
746 static gboolean CUDAAPI
747 parser_display_callback (GstNvDec * nvdec, CUVIDPARSERDISPINFO * dispinfo)
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;
755 GST_LOG_OBJECT (nvdec, "picture index: %u", dispinfo->picture_index);
757 pending_frames = gst_video_decoder_get_frames (GST_VIDEO_DECODER (nvdec));
758 for (iter = pending_frames; iter; iter = g_list_next (iter)) {
760 GstVideoCodecFrame *tmp = (GstVideoCodecFrame *) iter->data;
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);
768 g_list_free_full (pending_frames,
769 (GDestroyNotify) gst_video_codec_frame_unref);
771 if (G_UNLIKELY (frame == NULL)) {
772 GST_WARNING_OBJECT (nvdec, "no frame for picture index %u",
773 dispinfo->picture_index);
776 gst_video_decoder_allocate_output_buffer (GST_VIDEO_DECODER (nvdec));
778 if (!output_buffer) {
779 GST_ERROR_OBJECT (nvdec, "Couldn't allocate output buffer");
780 nvdec->last_ret = GST_FLOW_ERROR;
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));
792 ret = gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (nvdec),
795 if (ret != GST_FLOW_OK) {
796 GST_WARNING_OBJECT (nvdec, "failed to allocate output frame");
797 nvdec->last_ret = ret;
801 output_buffer = frame->output_buffer;
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)));
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);
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
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;
828 copy_ret = gst_nvdec_copy_device_to_memory (nvdec, dispinfo, output_buffer);
832 GST_ERROR_OBJECT (nvdec, "failed to copy decoded picture to output buffer");
833 nvdec->last_ret = GST_FLOW_ERROR;
836 gst_video_decoder_drop_frame (GST_VIDEO_DECODER (nvdec), frame);
838 gst_buffer_unref (output_buffer);
843 if (!dispinfo->progressive_frame) {
844 GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_INTERLACED);
846 if (dispinfo->top_field_first) {
847 GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_TFF);
850 if (dispinfo->repeat_first_field == -1) {
851 GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_ONEFIELD);
853 GST_BUFFER_FLAG_SET (output_buffer, GST_VIDEO_BUFFER_FLAG_RFF);
858 ret = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (nvdec), frame);
860 ret = gst_pad_push (GST_VIDEO_DECODER_SRC_PAD (nvdec), output_buffer);
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;
874 gst_nvdec_open (GstVideoDecoder * decoder)
876 GstNvDec *nvdec = GST_NVDEC (decoder);
877 GstNvDecClass *klass = GST_NVDEC_GET_CLASS (nvdec);
880 GST_DEBUG_OBJECT (nvdec, "creating CUDA context");
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");
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;
895 gst_cuda_context_pop (NULL);
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),
909 gst_nvdec_start (GstVideoDecoder * decoder)
911 GstNvDec *nvdec = GST_NVDEC (decoder);
913 nvdec->state = GST_NVDEC_STATE_INIT;
914 nvdec->last_ret = GST_FLOW_OK;
915 gst_video_info_init (&nvdec->out_info);
921 maybe_destroy_decoder_and_parser (GstNvDec * nvdec)
925 if (!gst_cuda_context_push (nvdec->cuda_ctx)) {
926 GST_ERROR_OBJECT (nvdec, "failed to lock CUDA context");
930 if (nvdec->decoder) {
931 GST_DEBUG_OBJECT (nvdec, "destroying decoder");
932 ret = gst_cuda_result (CuvidDestroyDecoder (nvdec->decoder));
933 nvdec->decoder = NULL;
936 GST_ERROR_OBJECT (nvdec, "failed to destroy decoder");
940 GST_DEBUG_OBJECT (nvdec, "destroying parser");
941 if (!gst_cuda_result (CuvidDestroyVideoParser (nvdec->parser))) {
942 GST_ERROR_OBJECT (nvdec, "failed to destroy parser");
945 nvdec->parser = NULL;
948 if (!gst_cuda_context_pop (NULL)) {
949 GST_WARNING_OBJECT (nvdec, "failed to pop CUDA context");
956 gst_nvdec_stop (GstVideoDecoder * decoder)
958 GstNvDec *nvdec = GST_NVDEC (decoder);
960 GST_DEBUG_OBJECT (nvdec, "stop");
962 if (!maybe_destroy_decoder_and_parser (nvdec))
965 #ifdef HAVE_NVCODEC_GST_GL
966 if (nvdec->gl_context) {
967 gst_object_unref (nvdec->gl_context);
968 nvdec->gl_context = NULL;
971 if (nvdec->other_gl_context) {
972 gst_object_unref (nvdec->other_gl_context);
973 nvdec->other_gl_context = NULL;
976 if (nvdec->gl_display) {
977 gst_object_unref (nvdec->gl_display);
978 nvdec->gl_display = NULL;
982 if (nvdec->input_state) {
983 gst_video_codec_state_unref (nvdec->input_state);
984 nvdec->input_state = NULL;
987 if (nvdec->output_state) {
988 gst_video_codec_state_unref (nvdec->output_state);
989 nvdec->output_state = NULL;
992 gst_clear_buffer (&nvdec->codec_data);
998 gst_nvdec_close (GstVideoDecoder * decoder)
1000 GstNvDec *nvdec = GST_NVDEC (decoder);
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);
1009 gst_clear_object (&nvdec->cuda_ctx);
1010 nvdec->cuda_stream = NULL;
1016 gst_nvdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
1018 GstNvDec *nvdec = GST_NVDEC (decoder);
1019 GstNvDecClass *klass = GST_NVDEC_GET_CLASS (decoder);
1020 CUVIDPARSERPARAMS parser_params = { 0, };
1022 gboolean ret = TRUE;
1024 GST_DEBUG_OBJECT (nvdec, "set format");
1026 if (nvdec->input_state)
1027 gst_video_codec_state_unref (nvdec->input_state);
1029 nvdec->input_state = gst_video_codec_state_ref (state);
1031 if (!maybe_destroy_decoder_and_parser (nvdec))
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);
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;
1056 gst_cuda_context_push (nvdec->cuda_ctx);
1057 GST_DEBUG_OBJECT (nvdec, "creating parser");
1058 if (!gst_cuda_result (CuvidCreateVideoParser (&nvdec->parser,
1060 GST_ERROR_OBJECT (nvdec, "failed to create parser");
1064 gst_cuda_context_pop (NULL);
1066 /* store codec data */
1067 if (ret && nvdec->input_state->caps) {
1068 const GValue *codec_data_value;
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);
1078 /* For all CODEC we get complete picture ... */
1079 nvdec->recv_complete_picture = TRUE;
1081 /* Except for JPEG, for which it depends on the caps */
1082 if (klass->codec_type == cudaVideoCodec_JPEG) {
1084 if (gst_structure_get_boolean (str, "parsed", &parsed))
1085 nvdec->recv_complete_picture = parsed;
1087 nvdec->recv_complete_picture = FALSE;
1094 #ifdef HAVE_NVCODEC_GST_GL
1098 CUVIDPARSERDISPINFO *dispinfo;
1100 GstBuffer *output_buffer;
1101 } GstNvDecCopyToGLData;
1104 copy_video_frame_to_gl_textures (GstGLContext * context,
1105 GstNvDecCopyToGLData * data)
1107 GstNvDec *nvdec = data->nvdec;
1108 CUVIDPARSERDISPINFO *dispinfo = data->dispinfo;
1109 GstCudaGraphicsResource **resources;
1110 guint num_resources;
1111 CUVIDPROCPARAMS proc_params = { 0, };
1114 CUDA_MEMCPY2D mcpy2d = { 0, };
1115 GstVideoInfo *info = &nvdec->output_state->info;
1117 GST_LOG_OBJECT (nvdec, "picture index: %u", dispinfo->picture_index);
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;
1125 num_resources = gst_buffer_n_memory (data->output_buffer);
1126 resources = g_newa (GstCudaGraphicsResource *, num_resources);
1128 for (i = 0; i < num_resources; i++) {
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);
1140 /* Need PBO -> texture */
1141 GST_MINI_OBJECT_FLAG_SET (mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD);
1144 if (!gst_cuda_context_push (nvdec->cuda_ctx)) {
1145 GST_WARNING_OBJECT (nvdec, "failed to lock CUDA context");
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");
1154 goto unlock_cuda_context;
1157 mcpy2d.srcMemoryType = CU_MEMORYTYPE_DEVICE;
1158 mcpy2d.srcPitch = pitch;
1159 mcpy2d.dstMemoryType = CU_MEMORYTYPE_DEVICE;
1161 for (i = 0; i < num_resources; i++) {
1162 CUdeviceptr cuda_ptr;
1164 CUgraphicsResource cuda_resource =
1165 gst_cuda_graphics_resource_map (resources[i], nvdec->cuda_stream,
1166 CU_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD);
1168 if (!cuda_resource) {
1169 GST_WARNING_OBJECT (nvdec, "failed to map CUDA resources");
1171 goto unmap_video_frame;
1174 if (!gst_cuda_result (CuGraphicsResourceGetMappedPointer (&cuda_ptr, &size,
1176 GST_WARNING_OBJECT (nvdec, "failed to map CUDA resource");
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);
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);
1189 if (!gst_cuda_result (CuMemcpy2DAsync (&mcpy2d, nvdec->cuda_stream))) {
1190 GST_WARNING_OBJECT (nvdec, "memcpy to mapped array failed");
1195 gst_cuda_result (CuStreamSynchronize (nvdec->cuda_stream));
1198 for (i = 0; i < num_resources; i++) {
1199 gst_cuda_graphics_resource_unmap (resources[i], nvdec->cuda_stream);
1202 if (!gst_cuda_result (CuvidUnmapVideoFrame (nvdec->decoder, dptr)))
1203 GST_WARNING_OBJECT (nvdec, "failed to unmap CUDA video frame");
1205 unlock_cuda_context:
1206 if (!gst_cuda_context_pop (NULL))
1207 GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
1211 gst_nvdec_copy_device_to_gl (GstNvDec * nvdec,
1212 CUVIDPARSERDISPINFO * dispinfo, GstBuffer * output_buffer)
1214 GstNvDecCopyToGLData data = { 0, };
1217 data.dispinfo = dispinfo;
1218 data.output_buffer = output_buffer;
1220 gst_gl_context_thread_add (nvdec->gl_context,
1221 (GstGLContextThreadFunc) copy_video_frame_to_gl_textures, &data);
1228 gst_nvdec_copy_device_to_memory (GstNvDec * nvdec,
1229 CUVIDPARSERDISPINFO * dispinfo, GstBuffer * output_buffer)
1231 CUVIDPROCPARAMS params = { 0, };
1232 CUDA_MEMCPY2D copy_params = { 0, };
1235 GstVideoFrame video_frame;
1236 GstVideoInfo *info = &nvdec->output_state->info;
1239 GstCudaMemory *cuda_mem = NULL;
1241 if (!gst_cuda_context_push (nvdec->cuda_ctx)) {
1242 GST_WARNING_OBJECT (nvdec, "failed to lock CUDA context");
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);
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,
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);
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;
1274 if (!gst_cuda_result (CuvidMapVideoFrame (nvdec->decoder,
1275 dispinfo->picture_index, &dptr, &pitch, ¶ms))) {
1276 GST_ERROR_OBJECT (nvdec, "failed to map video frame");
1277 gst_cuda_context_pop (NULL);
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;
1286 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (info); i++) {
1287 copy_params.srcDevice = dptr + (i * pitch * GST_VIDEO_INFO_HEIGHT (info));
1289 copy_params.dstDevice = cuda_mem->data + cuda_mem->offset[i];
1290 copy_params.dstPitch = cuda_mem->stride;
1292 copy_params.dstHost = GST_VIDEO_FRAME_PLANE_DATA (&video_frame, i);
1293 copy_params.dstPitch = GST_VIDEO_FRAME_PLANE_STRIDE (&video_frame, i);
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);
1299 if (!gst_cuda_result (CuMemcpy2DAsync (©_params, nvdec->cuda_stream))) {
1300 GST_ERROR_OBJECT (nvdec, "failed to copy %dth plane", i);
1301 CuvidUnmapVideoFrame (nvdec->decoder, dptr);
1303 gst_video_frame_unmap (&video_frame);
1304 gst_cuda_context_pop (NULL);
1309 gst_cuda_result (CuStreamSynchronize (nvdec->cuda_stream));
1312 gst_video_frame_unmap (&video_frame);
1314 if (!gst_cuda_result (CuvidUnmapVideoFrame (nvdec->decoder, dptr)))
1315 GST_WARNING_OBJECT (nvdec, "failed to unmap video frame");
1317 if (!gst_cuda_context_pop (NULL))
1318 GST_WARNING_OBJECT (nvdec, "failed to unlock CUDA context");
1323 static GstFlowReturn
1324 gst_nvdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
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;
1332 GST_LOG_OBJECT (nvdec, "handle frame");
1334 /* initialize with zero to keep track of frames */
1335 gst_video_codec_frame_set_user_data (frame, GUINT_TO_POINTER (0), NULL);
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),
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;
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;
1357 if (nvdec->recv_complete_picture)
1358 packet.flags |= CUVID_PKT_ENDOFPICTURE;
1360 nvdec->state = GST_NVDEC_STATE_PARSE;
1361 nvdec->last_ret = GST_FLOW_OK;
1363 if (!gst_cuda_result (CuvidParseVideoData (nvdec->parser, &packet)))
1364 GST_WARNING_OBJECT (nvdec, "parser failed");
1366 gst_buffer_unmap (in_buffer, &map_info);
1367 gst_buffer_unref (in_buffer);
1368 gst_video_codec_frame_unref (frame);
1370 return nvdec->last_ret;
1374 gst_nvdec_flush (GstVideoDecoder * decoder)
1376 GstNvDec *nvdec = GST_NVDEC (decoder);
1377 CUVIDSOURCEDATAPACKET packet = { 0, };
1379 GST_DEBUG_OBJECT (nvdec, "flush");
1381 packet.payload_size = 0;
1382 packet.payload = NULL;
1383 packet.flags = CUVID_PKT_ENDOFSTREAM;
1385 nvdec->state = GST_NVDEC_STATE_PARSE;
1386 nvdec->last_ret = GST_FLOW_OK;
1389 && !gst_cuda_result (CuvidParseVideoData (nvdec->parser, &packet)))
1390 GST_WARNING_OBJECT (nvdec, "parser failed");
1395 static GstFlowReturn
1396 gst_nvdec_drain (GstVideoDecoder * decoder)
1398 GstNvDec *nvdec = GST_NVDEC (decoder);
1399 CUVIDSOURCEDATAPACKET packet = { 0, };
1401 GST_DEBUG_OBJECT (nvdec, "draining decoder");
1403 packet.payload_size = 0;
1404 packet.payload = NULL;
1405 packet.flags = CUVID_PKT_ENDOFSTREAM;
1407 nvdec->state = GST_NVDEC_STATE_PARSE;
1408 nvdec->last_ret = GST_FLOW_OK;
1411 && !gst_cuda_result (CuvidParseVideoData (nvdec->parser, &packet)))
1412 GST_WARNING_OBJECT (nvdec, "parser failed");
1414 return nvdec->last_ret;
1417 static GstFlowReturn
1418 gst_nvdec_finish (GstVideoDecoder * decoder)
1420 GST_DEBUG_OBJECT (decoder, "finish");
1422 return gst_nvdec_drain (decoder);
1425 #ifdef HAVE_NVCODEC_GST_GL
1427 gst_nvdec_check_cuda_device_from_context (GstGLContext * context,
1430 guint device_count = 0;
1431 CUdevice device_list[1] = { 0, };
1436 cuda_ret = CuGLGetDevices (&device_count,
1437 device_list, 1, CU_GL_DEVICE_LIST_ALL);
1439 if (!gst_cuda_result (cuda_ret) || device_count == 0)
1448 gst_nvdec_ensure_gl_context (GstNvDec * nvdec)
1452 if (!nvdec->gl_display) {
1453 GST_DEBUG_OBJECT (nvdec, "No available OpenGL display");
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);
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");
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");
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");
1487 gst_gl_context_thread_add (nvdec->gl_context,
1488 (GstGLContextThreadFunc) gst_nvdec_check_cuda_device_from_context, &ret);
1491 GST_WARNING_OBJECT (nvdec, "Current OpenGL context is not CUDA-compatible");
1499 gst_nvdec_ensure_gl_pool (GstNvDec * nvdec, GstQuery * query)
1502 GstBufferPool *pool = NULL;
1503 guint n, size, min, max;
1504 GstVideoInfo vinfo = { 0, };
1505 GstStructure *config;
1507 GST_DEBUG_OBJECT (nvdec, "decide allocation");
1509 gst_query_parse_allocation (query, &outcaps, NULL);
1510 n = gst_query_get_n_allocation_pools (query);
1512 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
1514 if (pool && !GST_IS_GL_BUFFER_POOL (pool)) {
1515 gst_object_unref (pool);
1520 GST_DEBUG_OBJECT (nvdec, "no downstream pool, create our pool");
1521 pool = gst_gl_buffer_pool_new (nvdec->gl_context);
1524 gst_video_info_from_caps (&vinfo, outcaps);
1525 size = (guint) vinfo.size;
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);
1534 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
1536 gst_query_add_allocation_pool (query, pool, size, min, max);
1537 gst_object_unref (pool);
1544 gst_nvdec_ensure_cuda_pool (GstNvDec * nvdec, GstQuery * query)
1547 GstBufferPool *pool = NULL;
1548 guint n, size, min, max;
1549 GstVideoInfo vinfo = { 0, };
1550 GstStructure *config;
1552 gst_query_parse_allocation (query, &outcaps, NULL);
1553 n = gst_query_get_n_allocation_pools (query);
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);
1563 GST_DEBUG_OBJECT (nvdec, "no downstream pool, create our pool");
1564 pool = gst_cuda_buffer_pool_new (nvdec->cuda_ctx);
1567 gst_video_info_from_caps (&vinfo, outcaps);
1568 size = (guint) vinfo.size;
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);
1577 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
1579 gst_query_add_allocation_pool (query, pool, size, min, max);
1580 gst_object_unref (pool);
1586 gst_nvdec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
1588 GstNvDec *nvdec = GST_NVDEC (decoder);
1590 GST_DEBUG_OBJECT (nvdec, "decide allocation");
1592 if (nvdec->mem_type == GST_NVDEC_MEM_TYPE_SYSTEM)
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))
1601 if (!gst_nvdec_ensure_cuda_pool (nvdec, query)) {
1606 return GST_VIDEO_DECODER_CLASS (gst_nvdec_parent_class)->decide_allocation
1611 gst_nvdec_src_query (GstVideoDecoder * decoder, GstQuery * query)
1613 GstNvDec *nvdec = GST_NVDEC (decoder);
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)) {
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),
1635 return GST_VIDEO_DECODER_CLASS (gst_nvdec_parent_class)->src_query (decoder,
1640 gst_nvdec_set_context (GstElement * element, GstContext * context)
1642 GstNvDec *nvdec = GST_NVDEC (element);
1643 GstNvDecClass *klass = GST_NVDEC_GET_CLASS (nvdec);
1645 GST_DEBUG_OBJECT (nvdec, "set context %s",
1646 gst_context_get_context_type (context));
1648 if (gst_cuda_handle_set_context (element,
1649 context, klass->cuda_device_id, &nvdec->cuda_ctx)) {
1652 #ifdef HAVE_NVCODEC_GST_GL
1653 gst_gl_handle_set_context (element, context, &nvdec->gl_display,
1654 &nvdec->other_gl_context);
1658 GST_ELEMENT_CLASS (gst_nvdec_parent_class)->set_context (element, context);
1665 cudaVideoCodec codec_type;
1667 guint cuda_device_id;
1668 gboolean is_default;
1669 } GstNvDecClassData;
1672 gst_nvdec_subclass_init (gpointer g_class, gpointer data)
1674 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1675 GstNvDecClass *nvdec_class = GST_NVDEC_CLASS (g_class);
1676 GstNvDecClassData *cdata = data;
1679 if (cdata->is_default) {
1680 long_name = g_strdup_printf ("NVDEC %s Video Decoder", cdata->codec);
1682 long_name = g_strdup_printf ("NVDEC %s Video Decoder with device %d",
1683 cdata->codec, cdata->cuda_device_id);
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>");
1692 gst_element_class_add_pad_template (element_class,
1693 gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
1695 gst_element_class_add_pad_template (element_class,
1696 gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
1699 nvdec_class->codec_type = cdata->codec_type;
1700 nvdec_class->cuda_device_id = cdata->cuda_device_id;
1702 gst_caps_unref (cdata->sink_caps);
1703 gst_caps_unref (cdata->src_caps);
1704 g_free (cdata->codec);
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)
1713 GTypeQuery type_query;
1714 GTypeInfo type_info = { 0, };
1717 GstNvDecClassData *cdata;
1718 gboolean is_default = TRUE;
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;
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;
1734 type_name = g_strdup_printf ("nv%sdec", codec);
1736 if (g_type_from_name (type_name) != 0) {
1738 type_name = g_strdup_printf ("nv%sdevice%ddec", codec, device_id);
1742 cdata->is_default = is_default;
1743 subtype = g_type_register_static (type, type_name, &type_info, 0);
1745 /* make lower rank than default device */
1746 if (rank > 0 && !is_default)
1749 if (!gst_element_register (plugin, type_name, rank, subtype))
1750 GST_WARNING ("Failed to register plugin '%s'", type_name);
1756 gst_nvdec_plugin_init (GstPlugin * plugin, guint device_index,
1757 cudaVideoCodec codec, const gchar * codec_name, GstCaps * sink_template,
1758 GstCaps * src_template)
1760 gst_nvdec_subclass_register (plugin, GST_TYPE_NVDEC, codec,
1761 codec_name, device_index, GST_RANK_PRIMARY, sink_template, src_template);