nvencoder: Add support for dynamic GPU device selection
authorSeungha Yang <seungha@centricular.com>
Wed, 22 Jun 2022 16:10:38 +0000 (01:10 +0900)
committerSeungha Yang <seungha@centricular.com>
Thu, 30 Jun 2022 12:38:08 +0000 (12:38 +0000)
Adding nvautogpu{h264,h265}enc class which will accept upstream logical
GPU device object (GstCudaContext or GstD3D11Device) instead of
using pre-assigned GPU instance.

If upstream logical GPU device object is not NVENC compatible
(e.g., D3D11 device of non-NVIDIA GPU) or it's system memory,
then user specified "cuda-device-id" or "adapter-luid" property
will be used for GPU device selection.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2666>

subprojects/gst-plugins-bad/sys/nvcodec/gstnvencoder.cpp
subprojects/gst-plugins-bad/sys/nvcodec/gstnvencoder.h
subprojects/gst-plugins-bad/sys/nvcodec/gstnvh264encoder.cpp
subprojects/gst-plugins-bad/sys/nvcodec/gstnvh264encoder.h
subprojects/gst-plugins-bad/sys/nvcodec/gstnvh265encoder.cpp
subprojects/gst-plugins-bad/sys/nvcodec/gstnvh265encoder.h
subprojects/gst-plugins-bad/sys/nvcodec/plugin.c

index 99de903..3ea8203 100644 (file)
@@ -62,9 +62,10 @@ struct _GstNvEncoderPrivate
   GstD3D11Device *device;
 #endif
 
+  GstNvEncoderDeviceMode subclass_device_mode;
+  GstNvEncoderDeviceMode selected_device_mode;
   gint64 dxgi_adapter_luid;
   guint cuda_device_id;
-  gboolean d3d11_mode;
 
   NV_ENC_INITIALIZE_PARAMS init_params;
   NV_ENC_CONFIG config;
@@ -85,6 +86,8 @@ struct _GstNvEncoderPrivate
   GMutex lock;
   GCond cond;
 
+  GRecMutex context_lock;
+
   GThread *encoding_thread;
 
   GstFlowReturn last_flow;
@@ -159,6 +162,8 @@ gst_nv_encoder_init (GstNvEncoder * self)
   g_mutex_init (&priv->lock);
   g_cond_init (&priv->cond);
 
+  g_rec_mutex_init (&priv->context_lock);
+
   gst_video_encoder_set_min_pts (GST_VIDEO_ENCODER (self),
       GST_SECOND * 60 * 60 * 1000);
 }
@@ -171,6 +176,11 @@ gst_nv_encoder_finalize (GObject * object)
 
   g_array_unref (priv->task_pool);
 
+  g_mutex_clear (&priv->lock);
+  g_cond_clear (&priv->cond);
+
+  g_rec_mutex_clear (&priv->context_lock);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -180,15 +190,24 @@ gst_nv_encoder_set_context (GstElement * element, GstContext * context)
   GstNvEncoder *self = GST_NV_ENCODER (element);
   GstNvEncoderPrivate *priv = self->priv;
 
+  g_rec_mutex_lock (&priv->context_lock);
+
+  switch (priv->selected_device_mode) {
 #ifdef GST_CUDA_HAS_D3D
-  if (priv->d3d11_mode) {
-    gst_d3d11_handle_set_context_for_adapter_luid (element,
-        context, priv->dxgi_adapter_luid, &priv->device);
-  }
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      gst_d3d11_handle_set_context_for_adapter_luid (element,
+          context, priv->dxgi_adapter_luid, &priv->device);
+      break;
 #endif
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      gst_cuda_handle_set_context (element, context, priv->cuda_device_id,
+          &priv->context);
+      break;
+    default:
+      break;
+  }
 
-  gst_cuda_handle_set_context (element, context, priv->cuda_device_id,
-      &priv->context);
+  g_rec_mutex_unlock (&priv->context_lock);
 
   GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
 }
@@ -224,30 +243,44 @@ static gboolean
 gst_nv_encoder_device_lock (GstNvEncoder * self)
 {
   GstNvEncoderPrivate *priv = self->priv;
+  gboolean ret = TRUE;
 
+  switch (priv->selected_device_mode) {
 #ifdef GST_CUDA_HAS_D3D
-  if (priv->d3d11_mode) {
-    gst_d3d11_device_lock (priv->device);
-    return TRUE;
-  }
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      gst_d3d11_device_lock (priv->device);
+      break;
 #endif
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      ret = gst_cuda_context_push (priv->context);
+      break;
+    default:
+      break;
+  }
 
-  return gst_cuda_context_push (priv->context);
+  return ret;
 }
 
 static gboolean
 gst_nv_encoder_device_unlock (GstNvEncoder * self)
 {
-#ifdef GST_CUDA_HAS_D3D
   GstNvEncoderPrivate *priv = self->priv;
+  gboolean ret = TRUE;
 
-  if (priv->d3d11_mode) {
-    gst_d3d11_device_unlock (priv->device);
-    return TRUE;
-  }
+  switch (priv->selected_device_mode) {
+#ifdef GST_CUDA_HAS_D3D
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      gst_d3d11_device_unlock (priv->device);
+      break;
 #endif
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      ret = gst_cuda_context_pop (nullptr);
+      break;
+    default:
+      break;
+  }
 
-  return gst_cuda_context_pop (NULL);
+  return ret;
 }
 
 static GstFlowReturn
@@ -375,16 +408,24 @@ gst_nv_encoder_open (GstVideoEncoder * encoder)
   GstNvEncoder *self = GST_NV_ENCODER (encoder);
   GstNvEncoderPrivate *priv = self->priv;
 
+  switch (priv->selected_device_mode) {
+    case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
+      /* Will open GPU later */
+      return TRUE;
 #ifdef GST_CUDA_HAS_D3D
-  if (priv->d3d11_mode) {
-    return gst_nv_encoder_open_d3d11_device (self);
-  }
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      return gst_nv_encoder_open_d3d11_device (self);
 #endif
-
-  if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (encoder),
-          priv->cuda_device_id, &priv->context)) {
-    GST_ERROR_OBJECT (self, "failed to create CUDA context");
-    return FALSE;
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      if (!gst_cuda_ensure_element_context (GST_ELEMENT_CAST (encoder),
+              priv->cuda_device_id, &priv->context)) {
+        GST_ERROR_OBJECT (self, "failed to create CUDA context");
+        return FALSE;
+      }
+      break;
+    default:
+      g_assert_not_reached ();
+      return FALSE;
   }
 
   return TRUE;
@@ -414,6 +455,14 @@ gst_nv_encoder_stop (GstVideoEncoder * encoder)
 
   gst_nv_encoder_drain (self, FALSE);
 
+  if (priv->subclass_device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) {
+    gst_clear_object (&priv->context);
+#ifdef GST_CUDA_HAS_D3D
+    gst_clear_object (&priv->device);
+#endif
+    priv->selected_device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT;
+  }
+
   g_clear_pointer (&priv->input_state, gst_video_codec_state_unref);
 
   return TRUE;
@@ -423,16 +472,28 @@ static gboolean
 gst_nv_encoder_handle_context_query (GstNvEncoder * self, GstQuery * query)
 {
   GstNvEncoderPrivate *priv = self->priv;
+  gboolean ret = FALSE;
+
+  g_rec_mutex_lock (&priv->context_lock);
 
+  switch (priv->selected_device_mode) {
 #ifdef GST_CUDA_HAS_D3D
-  if (priv->d3d11_mode) {
-    return gst_d3d11_handle_context_query (GST_ELEMENT (self),
-        query, priv->device);
-  }
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      ret = gst_d3d11_handle_context_query (GST_ELEMENT (self),
+          query, priv->device);
+      break;
 #endif
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      ret = gst_cuda_handle_context_query (GST_ELEMENT (self),
+          query, priv->context);
+      break;
+    default:
+      break;
+  }
 
-  return gst_cuda_handle_context_query (GST_ELEMENT (self),
-      query, priv->context);
+  g_rec_mutex_unlock (&priv->context_lock);
+
+  return ret;
 }
 
 static gboolean
@@ -494,18 +555,34 @@ gst_nv_encoder_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
   }
 
   features = gst_caps_get_features (caps, 0);
+  min_buffers = gst_nv_encoder_get_task_size (self);
+
+  switch (priv->subclass_device_mode) {
+    case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
+      /* Use upstream pool in case of auto select mode. We don't know which
+       * GPU to use at this moment */
+      gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, nullptr);
+      gst_query_add_allocation_pool (query, nullptr, info.size, min_buffers, 0);
+      return TRUE;
 #ifdef GST_CUDA_HAS_D3D
-  if (priv->d3d11_mode && features && gst_caps_features_contains (features,
-          GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
-    GST_DEBUG_OBJECT (self, "upstream support d3d11 memory");
-    pool = gst_d3d11_buffer_pool_new (priv->device);
-  }
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      if (features && gst_caps_features_contains (features,
+              GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY)) {
+        GST_DEBUG_OBJECT (self, "upstream support d3d11 memory");
+        pool = gst_d3d11_buffer_pool_new (priv->device);
+      }
+      break;
 #endif
-
-  if (!priv->d3d11_mode && features && gst_caps_features_contains (features,
-          GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY)) {
-    GST_DEBUG_OBJECT (self, "upstream support CUDA memory");
-    pool = gst_cuda_buffer_pool_new (priv->context);
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      if (features && gst_caps_features_contains (features,
+              GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY)) {
+        GST_DEBUG_OBJECT (self, "upstream support CUDA memory");
+        pool = gst_cuda_buffer_pool_new (priv->context);
+      }
+      break;
+    default:
+      g_assert_not_reached ();
+      return FALSE;
   }
 
   if (!pool)
@@ -514,7 +591,6 @@ gst_nv_encoder_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
   config = gst_buffer_pool_get_config (pool);
   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
 
-  min_buffers = gst_nv_encoder_get_task_size (GST_NV_ENCODER (self));
 
   size = GST_VIDEO_INFO_SIZE (&info);
   gst_buffer_pool_config_set_params (config, caps, size, min_buffers, 0);
@@ -968,15 +1044,20 @@ gst_nv_encoder_open_encode_session (GstNvEncoder * self, gpointer * session)
   session_params.apiVersion = gst_nvenc_get_api_version ();
   NVENCSTATUS status;
 
+  switch (priv->selected_device_mode) {
 #ifdef GST_CUDA_HAS_D3D
-  if (priv->d3d11_mode) {
-    session_params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX;
-    session_params.device = gst_d3d11_device_get_device_handle (priv->device);
-  } else
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      session_params.deviceType = NV_ENC_DEVICE_TYPE_DIRECTX;
+      session_params.device = gst_d3d11_device_get_device_handle (priv->device);
+      break;
 #endif
-  {
-    session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
-    session_params.device = gst_cuda_context_get_handle (priv->context);
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      session_params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
+      session_params.device = gst_cuda_context_get_handle (priv->context);
+      break;
+    default:
+      g_assert_not_reached ();
+      return FALSE;
   }
 
   status = NvEncOpenEncodeSessionEx (&session_params, session);
@@ -1033,12 +1114,20 @@ gst_nv_encoder_create_pool (GstNvEncoder * self, GstVideoCodecState * state)
   GstNvEncoderPrivate *priv = self->priv;
   GstStructure *config;
   GstBufferPool *pool = NULL;
+
+  /* At this moment device type must be selected already */
+  switch (priv->selected_device_mode) {
 #ifdef GST_CUDA_HAS_D3D
-  if (priv->d3d11_mode)
-    return gst_nv_encoder_create_d3d11_pool (self, state);
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      return gst_nv_encoder_create_d3d11_pool (self, state);
 #endif
-
-  pool = gst_cuda_buffer_pool_new (priv->context);
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      pool = gst_cuda_buffer_pool_new (priv->context);
+      break;
+    default:
+      g_assert_not_reached ();
+      return FALSE;
+  }
 
   config = gst_buffer_pool_get_config (pool);
   gst_buffer_pool_config_set_params (config, state->caps,
@@ -1060,7 +1149,7 @@ gst_nv_encoder_create_pool (GstNvEncoder * self, GstVideoCodecState * state)
 }
 
 static gboolean
-gst_nv_encoder_init_session (GstNvEncoder * self)
+gst_nv_encoder_init_session (GstNvEncoder * self, GstBuffer * in_buf)
 {
   GstNvEncoderPrivate *priv = self->priv;
   GstNvEncoderClass *klass = GST_NV_ENCODER_GET_CLASS (self);
@@ -1077,6 +1166,52 @@ gst_nv_encoder_init_session (GstNvEncoder * self)
   memset (&priv->init_params, 0, sizeof (NV_ENC_INITIALIZE_PARAMS));
   memset (&priv->config, 0, sizeof (NV_ENC_CONFIG));
 
+  if (priv->selected_device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) {
+    GstNvEncoderDeviceData data;
+    gboolean ret;
+
+    if (!in_buf) {
+      GST_DEBUG_OBJECT (self, "Unknown device mode, open session later");
+      return TRUE;
+    }
+
+    if (!klass->select_device (self, info, in_buf, &data)) {
+      GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
+          ("Failed to select device mode"));
+      return FALSE;
+    }
+
+    GST_DEBUG_OBJECT (self,
+        "Selected device mode: %d, cuda-device-id: %d, adapter-luid %"
+        G_GINT64_FORMAT, data.device_mode, data.cuda_device_id,
+        data.adapter_luid);
+
+    g_assert (data.device_mode == GST_NV_ENCODER_DEVICE_CUDA ||
+        data.device_mode == GST_NV_ENCODER_DEVICE_D3D11);
+
+    g_rec_mutex_lock (&priv->context_lock);
+    priv->selected_device_mode = data.device_mode;
+    priv->cuda_device_id = data.cuda_device_id;
+    priv->dxgi_adapter_luid = data.adapter_luid;
+    gst_clear_object (&priv->context);
+    if (data.device_mode == GST_NV_ENCODER_DEVICE_CUDA)
+      priv->context = (GstCudaContext *) data.device;
+#ifdef GST_CUDA_HAS_D3D
+    gst_clear_object (&priv->device);
+    if (data.device_mode == GST_NV_ENCODER_DEVICE_D3D11)
+      priv->device = (GstD3D11Device *) data.device;
+#endif
+
+    ret = gst_nv_encoder_open (GST_VIDEO_ENCODER (self));
+    g_rec_mutex_unlock (&priv->context_lock);
+
+    if (!ret) {
+      GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
+          ("Failed to open device"));
+      return FALSE;
+    }
+  }
+
   priv->internal_pool = gst_nv_encoder_create_pool (self, state);
   if (!priv->internal_pool) {
     GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL),
@@ -1200,7 +1335,7 @@ gst_nv_encoder_reconfigure_session (GstNvEncoder * self)
         "Encoding session was not configured, open session");
     gst_nv_encoder_drain (self, TRUE);
 
-    return gst_nv_encoder_init_session (self);
+    return gst_nv_encoder_init_session (self, nullptr);
   }
 
   params.version = gst_nvenc_get_reconfigure_params_version ();
@@ -1213,7 +1348,7 @@ gst_nv_encoder_reconfigure_session (GstNvEncoder * self)
         GST_NVENC_STATUS_FORMAT, GST_NVENC_STATUS_ARGS (status));
     gst_nv_encoder_drain (self, TRUE);
 
-    return gst_nv_encoder_init_session (self);
+    return gst_nv_encoder_init_session (self, nullptr);
   }
 
   return TRUE;
@@ -1230,10 +1365,13 @@ gst_nv_encoder_set_format (GstVideoEncoder * encoder,
 
   g_clear_pointer (&priv->input_state, gst_video_codec_state_unref);
   priv->input_state = gst_video_codec_state_ref (state);
-
   priv->last_flow = GST_FLOW_OK;
 
-  return gst_nv_encoder_init_session (self);
+  /* select device again on next buffer */
+  if (priv->subclass_device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT)
+    priv->selected_device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT;
+
+  return gst_nv_encoder_init_session (self, nullptr);
 }
 
 static NV_ENC_BUFFER_FORMAT
@@ -1679,16 +1817,26 @@ gst_nv_encoder_prepare_task_input (GstNvEncoder * self,
     const GstVideoInfo * info, GstBuffer * buffer, gpointer session,
     GstBufferPool * pool, GstNvEncoderTask * task)
 {
-#ifdef GST_CUDA_HAS_D3D
   GstNvEncoderPrivate *priv = self->priv;
-  if (priv->d3d11_mode) {
-    return gst_nv_encoder_prepare_task_input_d3d11 (self, info, buffer,
-        session, pool, task);
-  }
+  GstFlowReturn ret = GST_FLOW_ERROR;
+
+  switch (priv->selected_device_mode) {
+#ifdef GST_CUDA_HAS_D3D
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      ret = gst_nv_encoder_prepare_task_input_d3d11 (self, info, buffer,
+          session, pool, task);
+      break;
 #endif
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      ret = gst_nv_encoder_prepare_task_input_cuda (self, info, buffer,
+          session, task);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
 
-  return gst_nv_encoder_prepare_task_input_cuda (self, info, buffer,
-      session, task);
+  return ret;
 }
 
 static GstFlowReturn
@@ -1701,6 +1849,7 @@ gst_nv_encoder_handle_frame (GstVideoEncoder * encoder,
   GstFlowReturn ret = GST_FLOW_ERROR;
   GstNvEncoderTask *task = NULL;
   GstNvEncoderReconfigure reconfig;
+  GstBuffer *in_buf = frame->input_buffer;
 
   GST_TRACE_OBJECT (self, "Handle frame");
 
@@ -1715,7 +1864,7 @@ gst_nv_encoder_handle_frame (GstVideoEncoder * encoder,
     return ret;
   }
 
-  if (!priv->session && !gst_nv_encoder_init_session (self)) {
+  if (!priv->session && !gst_nv_encoder_init_session (self, in_buf)) {
     GST_ERROR_OBJECT (self, "Encoder object was not configured");
     gst_video_encoder_finish_frame (encoder, frame);
 
@@ -1733,7 +1882,7 @@ gst_nv_encoder_handle_frame (GstVideoEncoder * encoder,
     case GST_NV_ENCODER_RECONFIGURE_FULL:
     {
       gst_nv_encoder_drain (self, TRUE);
-      if (!gst_nv_encoder_init_session (self)) {
+      if (!gst_nv_encoder_init_session (self, nullptr)) {
         gst_video_encoder_finish_frame (encoder, frame);
         return GST_FLOW_NOT_NEGOTIATED;
       }
@@ -1763,7 +1912,7 @@ gst_nv_encoder_handle_frame (GstVideoEncoder * encoder,
 
   g_assert (task->buffer == NULL);
   ret = gst_nv_encoder_prepare_task_input (self, &priv->input_state->info,
-      frame->input_buffer, priv->session, priv->internal_pool, task);
+      in_buf, priv->session, priv->internal_pool, task);
   gst_nv_encoder_device_unlock (self);
 
   if (ret != GST_FLOW_OK) {
@@ -1826,22 +1975,15 @@ gst_nv_encoder_get_task_size (GstNvEncoder * encoder)
 }
 
 void
-gst_nv_encoder_set_cuda_device_id (GstNvEncoder * encoder, guint device_id)
-{
-  g_return_if_fail (GST_IS_NV_ENCODER (encoder));
-
-  encoder->priv->cuda_device_id = device_id;
-  encoder->priv->d3d11_mode = FALSE;
-}
-
-void
-gst_nv_encoder_set_dxgi_adapter_luid (GstNvEncoder * encoder,
-    gint64 adapter_luid)
+gst_nv_encoder_set_device_mode (GstNvEncoder * encoder,
+    GstNvEncoderDeviceMode mode, guint cuda_device_id, gint64 adapter_luid)
 {
-  g_return_if_fail (GST_IS_NV_ENCODER (encoder));
+  GstNvEncoderPrivate *priv = encoder->priv;
 
-  encoder->priv->dxgi_adapter_luid = adapter_luid;
-  encoder->priv->d3d11_mode = TRUE;
+  priv->subclass_device_mode = mode;
+  priv->selected_device_mode = mode;
+  priv->cuda_device_id = cuda_device_id;
+  priv->dxgi_adapter_luid = adapter_luid;
 }
 
 GType
@@ -1995,3 +2137,204 @@ gst_nv_encoder_status_to_string (NVENCSTATUS status)
 
   return "Unknown";
 }
+
+GstNvEncoderClassData *
+gst_nv_encoder_class_data_new (void)
+{
+  GstNvEncoderClassData *data = g_new0 (GstNvEncoderClassData, 1);
+  data->ref_count = 1;
+
+  return data;
+}
+
+GstNvEncoderClassData *
+gst_nv_encoder_class_data_ref (GstNvEncoderClassData * cdata)
+{
+  g_atomic_int_add (&cdata->ref_count, 1);
+
+  return cdata;
+}
+
+void
+gst_nv_encoder_class_data_unref (GstNvEncoderClassData * cdata)
+{
+  if (g_atomic_int_dec_and_test (&cdata->ref_count)) {
+    gst_clear_caps (&cdata->sink_caps);
+    gst_clear_caps (&cdata->src_caps);
+    if (cdata->formats)
+      g_list_free_full (cdata->formats, (GDestroyNotify) g_free);
+    if (cdata->profiles)
+      g_list_free_full (cdata->profiles, (GDestroyNotify) g_free);
+    g_free (cdata);
+  }
+}
+
+void
+gst_nv_encoder_get_encoder_caps (gpointer session, const GUID * encode_guid,
+    GstNvEncoderDeviceCaps * device_caps)
+{
+  GstNvEncoderDeviceCaps dev_caps = { 0, };
+  NV_ENC_CAPS_PARAM caps_param = { 0, };
+  NVENCSTATUS status;
+  GUID guid = *encode_guid;
+
+  GST_DEBUG_CATEGORY_INIT (gst_nv_encoder_debug, "nvencoder", 0, "nvencoder");
+
+  caps_param.version = gst_nvenc_get_caps_param_version ();
+
+#define CHECK_CAPS(to_query,val,default_val) G_STMT_START { \
+  gint _val; \
+  caps_param.capsToQuery = to_query; \
+  status = NvEncGetEncodeCaps (session, guid, &caps_param, \
+      &_val); \
+  if (status != NV_ENC_SUCCESS) { \
+    GST_WARNING ("Unable to query %s, status: %" \
+        GST_NVENC_STATUS_FORMAT, G_STRINGIFY (to_query), \
+        GST_NVENC_STATUS_ARGS (status)); \
+    val = default_val; \
+  } else { \
+    GST_DEBUG ("%s: %d", G_STRINGIFY (to_query), _val); \
+    val = _val; \
+  } \
+} G_STMT_END
+
+  CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_BFRAMES, dev_caps.max_bframes, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES,
+      dev_caps.ratecontrol_modes, NV_ENC_PARAMS_RC_VBR);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FIELD_ENCODING, dev_caps.field_encoding, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MONOCHROME, dev_caps.monochrome, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FMO, dev_caps.fmo, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_QPELMV, dev_caps.qpelmv, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BDIRECT_MODE, dev_caps.bdirect_mode, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CABAC, dev_caps.cabac, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM,
+      dev_caps.adaptive_transform, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_STEREO_MVC, dev_caps.stereo_mvc, 0);
+  CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS, dev_caps.temoral_layers, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_PFRAMES,
+      dev_caps.hierarchical_pframes, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_BFRAMES,
+      dev_caps.hierarchical_bframes, 0);
+  CHECK_CAPS (NV_ENC_CAPS_LEVEL_MAX, dev_caps.level_max, 0);
+  CHECK_CAPS (NV_ENC_CAPS_LEVEL_MIN, dev_caps.level_min, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SEPARATE_COLOUR_PLANE,
+      dev_caps.separate_colour_plane, 0);
+  CHECK_CAPS (NV_ENC_CAPS_WIDTH_MAX, dev_caps.width_max, 4096);
+  CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MAX, dev_caps.height_max, 4096);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC, dev_caps.temporal_svc, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE, dev_caps.dyn_res_change, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE,
+      dev_caps.dyn_bitrate_change, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_FORCE_CONSTQP,
+      dev_caps.dyn_force_constqp, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RCMODE_CHANGE,
+      dev_caps.dyn_rcmode_change, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SUBFRAME_READBACK,
+      dev_caps.subframe_readback, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING,
+      dev_caps.constrained_encoding, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_INTRA_REFRESH, dev_caps.intra_refresh, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE,
+      dev_caps.custom_vbv_buf_size, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYNAMIC_SLICE_MODE,
+      dev_caps.dynamic_slice_mode, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION,
+      dev_caps.ref_pic_invalidation, 0);
+  CHECK_CAPS (NV_ENC_CAPS_PREPROC_SUPPORT, dev_caps.preproc_support, 0);
+  /* NOTE: Async is Windows only */
+#ifdef G_OS_WIN32
+  CHECK_CAPS (NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT,
+      dev_caps.async_encoding_support, 0);
+#endif
+  CHECK_CAPS (NV_ENC_CAPS_MB_NUM_MAX, dev_caps.mb_num_max, 0);
+  CHECK_CAPS (NV_ENC_CAPS_MB_PER_SEC_MAX, dev_caps.mb_per_sec_max, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, dev_caps.yuv444_encode, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, dev_caps.lossless_encode, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SAO, dev_caps.sao, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MEONLY_MODE, dev_caps.meonly_mode, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOOKAHEAD, dev_caps.lookahead, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, dev_caps.temporal_aq, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_10BIT_ENCODE,
+      dev_caps.supports_10bit_encode, 0);
+  CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_LTR_FRAMES, dev_caps.num_max_ltr_frames, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION,
+      dev_caps.weighted_prediction, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, dev_caps.bframe_ref_mode, 0);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP,
+      dev_caps.emphasis_level_map, 0);
+  CHECK_CAPS (NV_ENC_CAPS_WIDTH_MIN, dev_caps.width_min, 16);
+  CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MIN, dev_caps.height_min, 16);
+  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES,
+      dev_caps.multiple_ref_frames, 0);
+#undef CHECK_CAPS
+
+  *device_caps = dev_caps;
+}
+
+void
+gst_nv_encoder_merge_device_caps (const GstNvEncoderDeviceCaps * a,
+    const GstNvEncoderDeviceCaps * b, GstNvEncoderDeviceCaps * merged)
+{
+  GstNvEncoderDeviceCaps caps;
+
+#define SELECT_MAX(value) G_STMT_START { \
+  caps.value = MAX (a->value, b->value); \
+} G_STMT_END
+
+#define SELECT_MIN(value) G_STMT_START { \
+  caps.value = MAX (MIN (a->value, b->value), 1); \
+} G_STMT_END
+
+  SELECT_MAX (max_bframes);
+  SELECT_MAX (ratecontrol_modes);
+  SELECT_MAX (field_encoding);
+  SELECT_MAX (monochrome);
+  SELECT_MAX (fmo);
+  SELECT_MAX (qpelmv);
+  SELECT_MAX (bdirect_mode);
+  SELECT_MAX (cabac);
+  SELECT_MAX (adaptive_transform);
+  SELECT_MAX (stereo_mvc);
+  SELECT_MAX (temoral_layers);
+  SELECT_MAX (hierarchical_pframes);
+  SELECT_MAX (hierarchical_bframes);
+  SELECT_MAX (level_max);
+  SELECT_MAX (level_min);
+  SELECT_MAX (separate_colour_plane);
+  SELECT_MAX (width_max);
+  SELECT_MAX (height_max);
+  SELECT_MAX (temporal_svc);
+  SELECT_MAX (dyn_res_change);
+  SELECT_MAX (dyn_bitrate_change);
+  SELECT_MAX (dyn_force_constqp);
+  SELECT_MAX (dyn_rcmode_change);
+  SELECT_MAX (subframe_readback);
+  SELECT_MAX (constrained_encoding);
+  SELECT_MAX (intra_refresh);
+  SELECT_MAX (custom_vbv_buf_size);
+  SELECT_MAX (dynamic_slice_mode);
+  SELECT_MAX (ref_pic_invalidation);
+  SELECT_MAX (preproc_support);
+  SELECT_MAX (async_encoding_support);
+  SELECT_MAX (mb_num_max);
+  SELECT_MAX (mb_per_sec_max);
+  SELECT_MAX (yuv444_encode);
+  SELECT_MAX (lossless_encode);
+  SELECT_MAX (sao);
+  SELECT_MAX (meonly_mode);
+  SELECT_MAX (lookahead);
+  SELECT_MAX (temporal_aq);
+  SELECT_MAX (supports_10bit_encode);
+  SELECT_MAX (num_max_ltr_frames);
+  SELECT_MAX (weighted_prediction);
+  SELECT_MAX (bframe_ref_mode);
+  SELECT_MAX (emphasis_level_map);
+  SELECT_MIN (width_min);
+  SELECT_MIN (height_min);
+  SELECT_MAX (multiple_ref_frames);
+
+#undef SELECT_MAX
+#undef SELECT_MIN
+
+  *merged = caps;
+}
index f862701..7a146bc 100644 (file)
@@ -82,6 +82,88 @@ typedef enum
 
 typedef struct
 {
+  gint max_bframes;
+  gint ratecontrol_modes;
+  gint field_encoding;
+  gint monochrome;
+  gint fmo;
+  gint qpelmv;
+  gint bdirect_mode;
+  gint cabac;
+  gint adaptive_transform;
+  gint stereo_mvc;
+  gint temoral_layers;
+  gint hierarchical_pframes;
+  gint hierarchical_bframes;
+  gint level_max;
+  gint level_min;
+  gint separate_colour_plane;
+  gint width_max;
+  gint height_max;
+  gint temporal_svc;
+  gint dyn_res_change;
+  gint dyn_bitrate_change;
+  gint dyn_force_constqp;
+  gint dyn_rcmode_change;
+  gint subframe_readback;
+  gint constrained_encoding;
+  gint intra_refresh;
+  gint custom_vbv_buf_size;
+  gint dynamic_slice_mode;
+  gint ref_pic_invalidation;
+  gint preproc_support;
+  gint async_encoding_support;
+  gint mb_num_max;
+  gint mb_per_sec_max;
+  gint yuv444_encode;
+  gint lossless_encode;
+  gint sao;
+  gint meonly_mode;
+  gint lookahead;
+  gint temporal_aq;
+  gint supports_10bit_encode;
+  gint num_max_ltr_frames;
+  gint weighted_prediction;
+  gint bframe_ref_mode;
+  gint emphasis_level_map;
+  gint width_min;
+  gint height_min;
+  gint multiple_ref_frames;
+} GstNvEncoderDeviceCaps;
+
+typedef enum
+{
+  GST_NV_ENCODER_DEVICE_D3D11,
+  GST_NV_ENCODER_DEVICE_CUDA,
+  GST_NV_ENCODER_DEVICE_AUTO_SELECT,
+} GstNvEncoderDeviceMode;
+
+typedef struct
+{
+  GstCaps *sink_caps;
+  GstCaps *src_caps;
+
+  guint cuda_device_id;
+  gint64 adapter_luid;
+
+  GstNvEncoderDeviceMode device_mode;
+  GstNvEncoderDeviceCaps device_caps;
+
+  GList *formats;
+  GList *profiles;
+
+  /* auto gpu select mode */
+  guint adapter_luid_size;
+  gint64 adapter_luid_list[8];
+
+  guint cuda_device_id_size;
+  guint cuda_device_id_list[8];
+
+  gint ref_count;
+} GstNvEncoderClassData;
+
+typedef struct
+{
   /* without ref */
   GstNvEncoder *encoder;
 
@@ -101,6 +183,14 @@ typedef struct
   gboolean is_eos;
 } GstNvEncoderTask;
 
+typedef struct
+{
+  GstNvEncoderDeviceMode device_mode;
+  guint cuda_device_id;
+  gint64 adapter_luid;
+  GstObject *device;
+} GstNvEncoderDeviceData;
+
 struct _GstNvEncoder
 {
   GstVideoEncoder parent;
@@ -127,6 +217,11 @@ struct _GstNvEncoderClass
 
   GstNvEncoderReconfigure (*check_reconfigure)  (GstNvEncoder * encoder,
                                                  NV_ENC_CONFIG * config);
+
+  gboolean    (*select_device)        (GstNvEncoder * encoder,
+                                       const GstVideoInfo * info,
+                                       GstBuffer * buffer,
+                                       GstNvEncoderDeviceData * data);
 };
 
 GType gst_nv_encoder_get_type (void);
@@ -142,11 +237,25 @@ void gst_nv_encoder_preset_to_guid (GstNvEncoderPreset preset,
 
 NV_ENC_PARAMS_RC_MODE gst_nv_encoder_rc_mode_to_native (GstNvEncoderRCMode rc_mode);
 
-void gst_nv_encoder_set_cuda_device_id (GstNvEncoder * encoder,
-                                        guint device_id);
+void gst_nv_encoder_set_device_mode (GstNvEncoder * encoder,
+                                     GstNvEncoderDeviceMode mode,
+                                     guint cuda_device_id,
+                                     gint64 adapter_luid);
+
+GstNvEncoderClassData * gst_nv_encoder_class_data_new (void);
+
+GstNvEncoderClassData * gst_nv_encoder_class_data_ref (GstNvEncoderClassData * cdata);
+
+void gst_nv_encoder_class_data_unref (GstNvEncoderClassData * cdata);
+
+void gst_nv_encoder_get_encoder_caps (gpointer session,
+                                      const GUID * encode_guid,
+                                      GstNvEncoderDeviceCaps * device_caps);
+
+void gst_nv_encoder_merge_device_caps (const GstNvEncoderDeviceCaps * a,
+                                       const GstNvEncoderDeviceCaps * b,
+                                       GstNvEncoderDeviceCaps * merged);
 
-void gst_nv_encoder_set_dxgi_adapter_luid (GstNvEncoder * encoder,
-                                           gint64 adapter_luid);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstNvEncoder, gst_object_unref)
 
index c18b0e7..3381547 100644 (file)
 GST_DEBUG_CATEGORY_STATIC (gst_nv_h264_encoder_debug);
 #define GST_CAT_DEFAULT gst_nv_h264_encoder_debug
 
-static GTypeClass *parent_class = NULL;
-
-typedef struct
-{
-  gint max_bframes;
-  gint ratecontrol_modes;
-  gint field_encoding;
-  gint monochrome;
-  gint fmo;
-  gint qpelmv;
-  gint bdirect_mode;
-  gint cabac;
-  gint adaptive_transform;
-  gint stereo_mvc;
-  gint temoral_layers;
-  gint hierarchical_pframes;
-  gint hierarchical_bframes;
-  gint level_max;
-  gint level_min;
-  gint separate_colour_plane;
-  gint width_max;
-  gint height_max;
-  gint temporal_svc;
-  gint dyn_res_change;
-  gint dyn_bitrate_change;
-  gint dyn_force_constqp;
-  gint dyn_rcmode_change;
-  gint subframe_readback;
-  gint constrained_encoding;
-  gint intra_refresh;
-  gint custom_vbv_buf_size;
-  gint dynamic_slice_mode;
-  gint ref_pic_invalidation;
-  gint preproc_support;
-  gint async_encoding_support;
-  gint mb_num_max;
-  gint mb_per_sec_max;
-  gint yuv444_encode;
-  gint lossless_encode;
-  gint meonly_mode;
-  gint lookahead;
-  gint temporal_aq;
-  gint supports_10bit_encode;
-  gint num_max_ltr_frames;
-  gint weighted_prediction;
-  gint bframe_ref_mode;
-  gint emphasis_level_map;
-  gint width_min;
-  gint height_min;
-  gint multiple_ref_frames;
-} GstNvH264EncoderDeviceCaps;
-
-typedef struct
-{
-  GstCaps *sink_caps;
-  GstCaps *src_caps;
-
-  guint cuda_device_id;
-  gint64 adapter_luid;
-  gboolean d3d11_mode;
-
-  GstNvH264EncoderDeviceCaps dev_caps;
-} GstNvH264EncoderClassData;
+static GTypeClass *parent_class = nullptr;
 
 enum
 {
@@ -180,7 +118,12 @@ typedef struct _GstNvH264Encoder
   gboolean packetized;
   GstH264NalParser *parser;
 
+  GstNvEncoderDeviceMode selected_device_mode;
+
   /* Properties */
+  guint cuda_device_id;
+  gint64 adapter_luid;
+
   GstNvEncoderPreset preset;
   gboolean weighted_pred;
 
@@ -219,11 +162,21 @@ typedef struct _GstNvH264Encoder
 typedef struct _GstNvH264EncoderClass
 {
   GstNvEncoderClass parent_class;
-  GstNvH264EncoderDeviceCaps dev_caps;
 
   guint cuda_device_id;
   gint64 adapter_luid;
-  gboolean d3d11_mode;
+
+  GstNvEncoderDeviceMode device_mode;
+
+  /* representative device caps */
+  GstNvEncoderDeviceCaps device_caps;
+
+  /* auto gpu select mode */
+  guint cuda_device_id_size;
+  guint cuda_device_id_list[8];
+
+  guint adapter_luid_size;
+  gint64 adapter_luid_list[8];
 } GstNvH264EncoderClass;
 
 #define GST_NV_H264_ENCODER(object) ((GstNvH264Encoder *) (object))
@@ -247,6 +200,9 @@ static GstBuffer *gst_nv_h264_encoder_create_output_buffer (GstNvEncoder *
 static GstNvEncoderReconfigure
 gst_nv_h264_encoder_check_reconfigure (GstNvEncoder * encoder,
     NV_ENC_CONFIG * config);
+static gboolean gst_nv_h264_encoder_select_device (GstNvEncoder * encoder,
+    const GstVideoInfo * info, GstBuffer * buffer,
+    GstNvEncoderDeviceData * data);
 
 static void
 gst_nv_h264_encoder_class_init (GstNvH264EncoderClass * klass, gpointer data)
@@ -255,8 +211,8 @@ gst_nv_h264_encoder_class_init (GstNvH264EncoderClass * klass, gpointer data)
   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
   GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
   GstNvEncoderClass *nvenc_class = GST_NV_ENCODER_CLASS (klass);
-  GstNvH264EncoderClassData *cdata = (GstNvH264EncoderClassData *) data;
-  GstNvH264EncoderDeviceCaps *dev_caps = &cdata->dev_caps;
+  GstNvEncoderClassData *cdata = (GstNvEncoderClassData *) data;
+  GstNvEncoderDeviceCaps *dev_caps = &cdata->device_caps;
   GParamFlags param_flags = (GParamFlags) (G_PARAM_READWRITE |
       GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS);
   GParamFlags conditional_param_flags = (GParamFlags) (G_PARAM_READWRITE |
@@ -269,19 +225,41 @@ gst_nv_h264_encoder_class_init (GstNvH264EncoderClass * klass, gpointer data)
   object_class->set_property = gst_nv_h264_encoder_set_property;
   object_class->get_property = gst_nv_h264_encoder_get_property;
 
-  if (cdata->d3d11_mode) {
-    g_object_class_install_property (object_class, PROP_ADAPTER_LUID,
-        g_param_spec_int64 ("adapter-luid", "Adapter LUID",
-            "DXGI Adapter LUID (Locally Unique Identifier) of associated GPU",
-            G_MININT64, G_MAXINT64, cdata->adapter_luid,
-            (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
-  } else {
-    g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID,
-        g_param_spec_uint ("cuda-device-id", "CUDA Device ID",
-            "CUDA device ID of associated GPU",
-            0, G_MAXINT, cdata->cuda_device_id,
-            (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+  switch (cdata->device_mode) {
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID,
+          g_param_spec_uint ("cuda-device-id", "CUDA Device ID",
+              "CUDA device ID of associated GPU",
+              0, G_MAXINT, cdata->cuda_device_id,
+              (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+      break;
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      g_object_class_install_property (object_class, PROP_ADAPTER_LUID,
+          g_param_spec_int64 ("adapter-luid", "Adapter LUID",
+              "DXGI Adapter LUID (Locally Unique Identifier) of associated GPU",
+              G_MININT64, G_MAXINT64, cdata->adapter_luid,
+              (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+      break;
+    case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
+      if (cdata->cuda_device_id_size > 0) {
+        g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID,
+            g_param_spec_uint ("cuda-device-id", "CUDA Device ID",
+                "CUDA device ID to use",
+                0, G_MAXINT, cdata->cuda_device_id, conditional_param_flags));
+      }
+      if (cdata->adapter_luid_size > 0) {
+        g_object_class_install_property (object_class, PROP_ADAPTER_LUID,
+            g_param_spec_int64 ("adapter-luid", "Adapter LUID",
+                "DXGI Adapter LUID (Locally Unique Identifier) to use",
+                G_MININT64, G_MAXINT64, cdata->adapter_luid,
+                conditional_param_flags));
+      }
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
   }
+
   g_object_class_install_property (object_class, PROP_PRESET,
       g_param_spec_enum ("preset", "Encoding Preset",
           "Encoding Preset", GST_TYPE_NV_ENCODER_PRESET,
@@ -415,18 +393,31 @@ gst_nv_h264_encoder_class_init (GstNvH264EncoderClass * klass, gpointer data)
           "Insert sequence headers (SPS/PPS) per IDR",
           DEFAULT_REPEAT_SEQUENCE_HEADER, param_flags));
 
-  if (cdata->d3d11_mode) {
-    gst_element_class_set_metadata (element_class,
-        "NVENC H.264 Video Encoder Direct3D11 Mode",
-        "Codec/Encoder/Video/Hardware",
-        "Encode H.264 video streams using NVCODEC API Direct3D11 Mode",
-        "Seungha Yang <seungha@centricular.com>");
-  } else {
-    gst_element_class_set_metadata (element_class,
-        "NVENC H.264 Video Encoder CUDA Mode",
-        "Codec/Encoder/Video/Hardware",
-        "Encode H.264 video streams using NVCODEC API CUDA Mode",
-        "Seungha Yang <seungha@centricular.com>");
+  switch (cdata->device_mode) {
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      gst_element_class_set_static_metadata (element_class,
+          "NVENC H.264 Video Encoder CUDA Mode",
+          "Codec/Encoder/Video/Hardware",
+          "Encode H.264 video streams using NVCODEC API CUDA Mode",
+          "Seungha Yang <seungha@centricular.com>");
+      break;
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      gst_element_class_set_static_metadata (element_class,
+          "NVENC H.264 Video Encoder Direct3D11 Mode",
+          "Codec/Encoder/Video/Hardware",
+          "Encode H.264 video streams using NVCODEC API Direct3D11 Mode",
+          "Seungha Yang <seungha@centricular.com>");
+      break;
+    case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
+      gst_element_class_set_static_metadata (element_class,
+          "NVENC H.264 Video Encoder Auto GPU select Mode",
+          "Codec/Encoder/Video/Hardware",
+          "Encode H.264 video streams using NVCODEC API auto GPU select Mode",
+          "Seungha Yang <seungha@centricular.com>");
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
   }
 
   gst_element_class_add_pad_template (element_class,
@@ -445,15 +436,21 @@ gst_nv_h264_encoder_class_init (GstNvH264EncoderClass * klass, gpointer data)
       GST_DEBUG_FUNCPTR (gst_nv_h264_encoder_create_output_buffer);
   nvenc_class->check_reconfigure =
       GST_DEBUG_FUNCPTR (gst_nv_h264_encoder_check_reconfigure);
+  nvenc_class->select_device =
+      GST_DEBUG_FUNCPTR (gst_nv_h264_encoder_select_device);
 
-  klass->dev_caps = cdata->dev_caps;
+  klass->device_caps = cdata->device_caps;
   klass->cuda_device_id = cdata->cuda_device_id;
   klass->adapter_luid = cdata->adapter_luid;
-  klass->d3d11_mode = cdata->d3d11_mode;
-
-  gst_caps_unref (cdata->sink_caps);
-  gst_caps_unref (cdata->src_caps);
-  g_free (cdata);
+  klass->device_mode = cdata->device_mode;
+  klass->cuda_device_id_size = cdata->cuda_device_id_size;
+  klass->adapter_luid_size = cdata->adapter_luid_size;
+  memcpy (klass->cuda_device_id_list, cdata->cuda_device_id_list,
+      sizeof (klass->cuda_device_id_list));
+  memcpy (klass->adapter_luid_list, cdata->adapter_luid_list,
+      sizeof (klass->adapter_luid_list));
+
+  gst_nv_encoder_class_data_unref (cdata);
 }
 
 static void
@@ -463,6 +460,9 @@ gst_nv_h264_encoder_init (GstNvH264Encoder * self)
 
   g_mutex_init (&self->prop_lock);
 
+  self->selected_device_mode = klass->device_mode;
+  self->cuda_device_id = klass->cuda_device_id;
+  self->adapter_luid = klass->adapter_luid;
   self->preset = DEFAULT_PRESET;
   self->weighted_pred = DEFAULT_WEIGHTED_PRED;
   self->gop_size = DEFAULT_GOP_SIZE;
@@ -491,19 +491,14 @@ gst_nv_h264_encoder_init (GstNvH264Encoder * self)
   self->max_qp_b = DEFAULT_QP;
   self->const_quality = DEFAULT_CONST_QUALITY;
   self->aud = DEFAULT_AUD;
-  if (klass->dev_caps.cabac)
+  if (klass->device_caps.cabac)
     self->cabac = TRUE;
   self->repeat_sequence_header = DEFAULT_REPEAT_SEQUENCE_HEADER;
 
   self->parser = gst_h264_nal_parser_new ();
 
-  if (klass->d3d11_mode) {
-    gst_nv_encoder_set_dxgi_adapter_luid (GST_NV_ENCODER (self),
-        klass->adapter_luid);
-  } else {
-    gst_nv_encoder_set_cuda_device_id (GST_NV_ENCODER (self),
-        klass->cuda_device_id);
-  }
+  gst_nv_encoder_set_device_mode (GST_NV_ENCODER (self), klass->device_mode,
+      klass->cuda_device_id, klass->adapter_luid);
 }
 
 static void
@@ -621,9 +616,43 @@ gst_nv_h264_encoder_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
 {
   GstNvH264Encoder *self = GST_NV_H264_ENCODER (object);
+  GstNvH264EncoderClass *klass = GST_NV_H264_ENCODER_GET_CLASS (self);
 
   g_mutex_lock (&self->prop_lock);
   switch (prop_id) {
+    case PROP_ADAPTER_LUID:{
+      gint64 adapter_luid = g_value_get_int64 (value);
+      gboolean is_valid = FALSE;
+
+      for (guint i = 0; i < klass->adapter_luid_size; i++) {
+        if (klass->adapter_luid_list[i] == adapter_luid) {
+          self->adapter_luid = adapter_luid;
+          is_valid = TRUE;
+          break;
+        }
+      }
+
+      if (!is_valid)
+        g_warning ("%" G_GINT64_FORMAT " is not a valid adapter luid",
+            adapter_luid);
+      break;
+    }
+    case PROP_CUDA_DEVICE_ID:{
+      guint cuda_device_id = g_value_get_uint (value);
+      gboolean is_valid = FALSE;
+
+      for (guint i = 0; i < klass->cuda_device_id_size; i++) {
+        if (klass->cuda_device_id_list[i] == cuda_device_id) {
+          self->cuda_device_id = cuda_device_id;
+          is_valid = TRUE;
+          break;
+        }
+      }
+
+      if (!is_valid)
+        g_warning ("%d is not a valid cuda device id", cuda_device_id);
+      break;
+    }
     case PROP_PRESET:{
       GstNvEncoderPreset preset = (GstNvEncoderPreset) g_value_get_enum (value);
       if (preset != self->preset) {
@@ -739,14 +768,13 @@ gst_nv_h264_encoder_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec)
 {
   GstNvH264Encoder *self = GST_NV_H264_ENCODER (object);
-  GstNvH264EncoderClass *klass = GST_NV_H264_ENCODER_GET_CLASS (self);
 
   switch (prop_id) {
     case PROP_ADAPTER_LUID:
-      g_value_set_int64 (value, klass->adapter_luid);
+      g_value_set_int64 (value, self->adapter_luid);
       break;
     case PROP_CUDA_DEVICE_ID:
-      g_value_set_uint (value, klass->cuda_device_id);
+      g_value_set_uint (value, self->cuda_device_id);
       break;
     case PROP_PRESET:
       g_value_set_enum (value, self->preset);
@@ -915,13 +943,13 @@ gst_nv_h264_encoder_getcaps (GstVideoEncoder * encoder, GstCaps * filter)
   gboolean profile_support_interlaced = FALSE;
 
   gst_nv_h264_encoder_get_downstream_profiles_and_format (self,
-      downstream_profiles, NULL);
+      downstream_profiles, nullptr);
 
   GST_DEBUG_OBJECT (self, "Downstream specified %" G_GSIZE_FORMAT " profiles",
       downstream_profiles.size ());
 
   if (downstream_profiles.size () == 0)
-    return gst_video_encoder_proxy_getcaps (encoder, NULL, filter);
+    return gst_video_encoder_proxy_getcaps (encoder, nullptr, filter);
 
   /* *INDENT-OFF* */
   for (const auto &iter: downstream_profiles) {
@@ -944,9 +972,9 @@ gst_nv_h264_encoder_getcaps (GstVideoEncoder * encoder, GstCaps * filter)
   template_caps = gst_pad_get_pad_template_caps (encoder->sinkpad);
   allowed_caps = gst_caps_copy (template_caps);
 
-  if (klass->dev_caps.field_encoding == 0 || !profile_support_interlaced) {
+  if (klass->device_caps.field_encoding == 0 || !profile_support_interlaced) {
     gst_caps_set_simple (allowed_caps, "interlace-mode", G_TYPE_STRING,
-        "progressive", NULL);
+        "progressive", nullptr);
   }
 
   GValue formats = G_VALUE_INIT;
@@ -986,7 +1014,7 @@ gst_nv_h264_encoder_set_format (GstNvEncoder * encoder,
 {
   GstNvH264Encoder *self = GST_NV_H264_ENCODER (encoder);
   GstNvH264EncoderClass *klass = GST_NV_H264_ENCODER_GET_CLASS (self);
-  GstNvH264EncoderDeviceCaps *dev_caps = &klass->dev_caps;
+  GstNvEncoderDeviceCaps *dev_caps = &klass->device_caps;
   NV_ENC_RC_PARAMS *rc_params;
   GstVideoInfo *info = &state->info;
   NVENCSTATUS status;
@@ -999,6 +1027,10 @@ gst_nv_h264_encoder_set_format (GstNvEncoder * encoder,
   GUID selected_profile = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID;
   gboolean downstream_supports_bframe = FALSE;
   gboolean bframe_aborted = FALSE;
+  gboolean weight_pred_aborted = FALSE;
+  gboolean vbv_buffer_size_aborted = FALSE;
+  gboolean lookahead_aborted = FALSE;
+  gboolean temporal_aq_aborted = FALSE;
 
   self->packetized = FALSE;
 
@@ -1043,6 +1075,52 @@ gst_nv_h264_encoder_set_format (GstNvEncoder * encoder,
 
   g_mutex_lock (&self->prop_lock);
 
+  if (klass->device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) {
+    GstNvEncoderDeviceCaps dev_caps;
+
+    gst_nv_encoder_get_encoder_caps (session,
+        &NV_ENC_CODEC_H264_GUID, &dev_caps);
+
+    if (self->bframes > 0 && !dev_caps.max_bframes) {
+      self->bframes = 0;
+      bframe_aborted = TRUE;
+
+      GST_INFO_OBJECT (self, "B-frame was enabled but not support by device");
+    }
+
+    if (self->weighted_pred && !dev_caps.weighted_prediction) {
+      self->weighted_pred = FALSE;
+      weight_pred_aborted = TRUE;
+
+      GST_INFO_OBJECT (self,
+          "Weighted prediction was enabled but not support by device");
+    }
+
+    if (self->vbv_buffer_size && !dev_caps.custom_vbv_buf_size) {
+      self->vbv_buffer_size = 0;
+      vbv_buffer_size_aborted = TRUE;
+
+      GST_INFO_OBJECT (self,
+          "VBV buffer size was specified but not supported by device");
+    }
+
+    if (self->rc_lookahead && !dev_caps.lookahead) {
+      self->rc_lookahead = 0;
+      lookahead_aborted = TRUE;
+
+      GST_INFO_OBJECT (self,
+          "VBV buffer size was specified but not supported by device");
+    }
+
+    if (self->temporal_aq && !dev_caps.temporal_aq) {
+      self->temporal_aq = FALSE;
+      temporal_aq_aborted = TRUE;
+
+      GST_INFO_OBJECT (self,
+          "temporal-aq was enabled but not supported by device");
+    }
+  }
+
   init_params->version = gst_nvenc_get_initialize_params_version ();
   init_params->encodeGUID = NV_ENC_CODEC_H264_GUID;
 
@@ -1280,6 +1358,14 @@ gst_nv_h264_encoder_set_format (GstNvEncoder * encoder,
 
   if (bframe_aborted)
     g_object_notify (G_OBJECT (self), "b-frames");
+  if (weight_pred_aborted)
+    g_object_notify (G_OBJECT (self), "weighted-pred");
+  if (vbv_buffer_size_aborted)
+    g_object_notify (G_OBJECT (self), "vbv-buffer-size");
+  if (lookahead_aborted)
+    g_object_notify (G_OBJECT (self), "rc-lookahead");
+  if (temporal_aq_aborted)
+    g_object_notify (G_OBJECT (self), "temporal-aq");
 
   return TRUE;
 }
@@ -1299,14 +1385,14 @@ gst_nv_h264_encoder_set_output_state (GstNvEncoder * encoder,
   std::set < std::string > downstream_profiles;
   std::string caps_str;
   GstTagList *tags;
-  GstBuffer *codec_data = NULL;
+  GstBuffer *codec_data = nullptr;
   GstH264NalUnit sps_nalu, pps_nalu;
   GstH264ParserResult rst;
 
   caps_str = "video/x-h264, alignment = (string) au";
 
   gst_nv_h264_encoder_get_downstream_profiles_and_format (self,
-      downstream_profiles, NULL);
+      downstream_profiles, nullptr);
 
   seq_params.version = gst_nvenc_get_sequence_param_payload_version ();
   seq_params.inBufferSize = sizeof (spspps);
@@ -1420,7 +1506,8 @@ gst_nv_h264_encoder_set_output_state (GstNvEncoder * encoder,
   caps = gst_caps_from_string (caps_str.c_str ());
 
   if (self->packetized) {
-    gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
+    gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, codec_data,
+        nullptr);
     gst_buffer_unref (codec_data);
   }
 
@@ -1432,7 +1519,7 @@ gst_nv_h264_encoder_set_output_state (GstNvEncoder * encoder,
 
   tags = gst_tag_list_new_empty ();
   gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER,
-      "nvh264encoder", NULL);
+      "nvh264encoder", nullptr);
 
   gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (encoder),
       tags, GST_TAG_MERGE_REPLACE);
@@ -1503,7 +1590,7 @@ gst_nv_h264_encoder_check_reconfigure (GstNvEncoder * encoder,
 
   if (self->bitrate_updated) {
     GstNvH264EncoderClass *klass = GST_NV_H264_ENCODER_GET_CLASS (self);
-    if (klass->dev_caps.dyn_bitrate_change > 0) {
+    if (klass->device_caps.dyn_bitrate_change > 0) {
       config->rcParams.averageBitRate = self->bitrate * 1024;
       config->rcParams.maxBitRate = self->max_bitrate * 1024;
       reconfig = GST_NV_ENCODER_RECONFIGURE_BITRATE;
@@ -1521,13 +1608,113 @@ done:
   return reconfig;
 }
 
-static GstNvH264EncoderClassData *
+static gboolean
+gst_nv_h264_encoder_select_device (GstNvEncoder * encoder,
+    const GstVideoInfo * info, GstBuffer * buffer,
+    GstNvEncoderDeviceData * data)
+{
+  GstNvH264Encoder *self = GST_NV_H264_ENCODER (encoder);
+  GstNvH264EncoderClass *klass = GST_NV_H264_ENCODER_GET_CLASS (self);
+  GstMemory *mem;
+
+  memset (data, 0, sizeof (GstNvEncoderDeviceData));
+
+  g_assert (klass->device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT);
+
+  mem = gst_buffer_peek_memory (buffer, 0);
+  if (klass->cuda_device_id_size > 0 && gst_is_cuda_memory (mem)) {
+    GstCudaMemory *cmem = GST_CUDA_MEMORY_CAST (mem);
+    GstCudaContext *context = cmem->context;
+    guint device_id;
+    gboolean found = FALSE;
+
+    g_object_get (context, "cuda-device-id", &device_id, nullptr);
+
+    data->device_mode = GST_NV_ENCODER_DEVICE_CUDA;
+    self->selected_device_mode = GST_NV_ENCODER_DEVICE_CUDA;
+
+    for (guint i = 0; i < klass->cuda_device_id_size; i++) {
+      if (klass->cuda_device_id_list[i] == device_id) {
+        data->cuda_device_id = device_id;
+        found = TRUE;
+        break;
+      }
+    }
+
+    if (!found) {
+      GST_INFO_OBJECT (self,
+          "Upstream CUDA device is not in supported device list");
+      data->cuda_device_id = self->cuda_device_id;
+    } else {
+      data->device = (GstObject *) gst_object_ref (context);
+    }
+
+    if (data->cuda_device_id != self->cuda_device_id) {
+      self->cuda_device_id = data->cuda_device_id;
+      g_object_notify (G_OBJECT (self), "cuda-device-id");
+    }
+
+    return TRUE;
+  }
+#ifdef GST_CUDA_HAS_D3D
+  if (klass->adapter_luid_size > 0 && gst_is_d3d11_memory (mem)) {
+    GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
+    GstD3D11Device *device = dmem->device;
+    gint64 adapter_luid;
+    gboolean found = FALSE;
+
+    g_object_get (device, "adapter-luid", &adapter_luid, nullptr);
+
+    data->device_mode = GST_NV_ENCODER_DEVICE_D3D11;
+    self->selected_device_mode = GST_NV_ENCODER_DEVICE_D3D11;
+
+    for (guint i = 0; i < klass->cuda_device_id_size; i++) {
+      if (klass->adapter_luid_list[i] == adapter_luid) {
+        data->adapter_luid = adapter_luid;
+        found = TRUE;
+        break;
+      }
+    }
+
+    if (!found) {
+      GST_INFO_OBJECT (self,
+          "Upstream D3D11 device is not in supported device list");
+      data->adapter_luid = self->adapter_luid;
+    } else {
+      data->device = (GstObject *) gst_object_ref (device);
+    }
+
+    if (data->adapter_luid != self->adapter_luid) {
+      self->adapter_luid = data->adapter_luid;
+      g_object_notify (G_OBJECT (self), "adapter-luid");
+    }
+
+    return TRUE;
+  }
+#endif
+
+  if (klass->cuda_device_id_size > 0 &&
+      (self->selected_device_mode != GST_NV_ENCODER_DEVICE_D3D11)) {
+    GST_INFO_OBJECT (self, "Upstream is system memory, use CUDA mode");
+    data->device_mode = GST_NV_ENCODER_DEVICE_CUDA;
+    data->cuda_device_id = self->cuda_device_id;
+  } else {
+    GST_INFO_OBJECT (self, "Upstream is system memory, use D3D11 mode");
+    data->device_mode = GST_NV_ENCODER_DEVICE_D3D11;
+    data->adapter_luid = klass->adapter_luid;
+  }
+
+  self->selected_device_mode = data->device_mode;
+
+  return TRUE;
+}
+
+static GstNvEncoderClassData *
 gst_nv_h264_encoder_create_class_data (GstObject * device, gpointer session,
-    gboolean d3d11_mode)
+    GstNvEncoderDeviceMode device_mode)
 {
   NVENCSTATUS status;
-  GstNvH264EncoderDeviceCaps dev_caps = { 0, };
-  NV_ENC_CAPS_PARAM caps_param = { 0, };
+  GstNvEncoderDeviceCaps dev_caps = { 0, };
   GUID profile_guids[16];
   NV_ENC_BUFFER_FORMAT input_formats[16];
   guint32 profile_guid_count = 0;
@@ -1539,7 +1726,7 @@ gst_nv_h264_encoder_create_class_data (GstObject * device, gpointer session,
   std::set < std::string > profiles;
   std::string profile_str;
   std::string resolution_str;
-  GstNvH264EncoderClassData *cdata;
+  GstNvEncoderClassData *cdata;
   GstCaps *sink_caps;
   GstCaps *system_caps;
 
@@ -1547,102 +1734,17 @@ gst_nv_h264_encoder_create_class_data (GstObject * device, gpointer session,
       profile_guids, G_N_ELEMENTS (profile_guids), &profile_guid_count);
   if (status != NV_ENC_SUCCESS || profile_guid_count == 0) {
     GST_WARNING_OBJECT (device, "Unable to get supported profiles");
-    return NULL;
+    return nullptr;
   }
 
   status = NvEncGetInputFormats (session, NV_ENC_CODEC_H264_GUID, input_formats,
       G_N_ELEMENTS (input_formats), &input_format_count);
   if (status != NV_ENC_SUCCESS || input_format_count == 0) {
     GST_WARNING_OBJECT (device, "Unable to get supported input formats");
-    return NULL;
-  }
-
-  caps_param.version = gst_nvenc_get_caps_param_version ();
-
-#define CHECK_CAPS(to_query,val,default_val) G_STMT_START { \
-  gint _val; \
-  caps_param.capsToQuery = to_query; \
-  status = NvEncGetEncodeCaps (session, NV_ENC_CODEC_H264_GUID, &caps_param, \
-      &_val); \
-  if (status != NV_ENC_SUCCESS) { \
-    GST_WARNING_OBJECT (device, "Unable to query %s, status: %" \
-        GST_NVENC_STATUS_FORMAT, G_STRINGIFY (to_query), \
-        GST_NVENC_STATUS_ARGS (status)); \
-    val = default_val; \
-  } else { \
-    GST_DEBUG_OBJECT (device, "%s: %d", G_STRINGIFY (to_query), _val); \
-    val = _val; \
-  } \
-} G_STMT_END
+    return nullptr;
+  }
 
-  CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_BFRAMES, dev_caps.max_bframes, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES,
-      dev_caps.ratecontrol_modes, NV_ENC_PARAMS_RC_VBR);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FIELD_ENCODING, dev_caps.field_encoding, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MONOCHROME, dev_caps.monochrome, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FMO, dev_caps.fmo, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_QPELMV, dev_caps.qpelmv, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BDIRECT_MODE, dev_caps.bdirect_mode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CABAC, dev_caps.cabac, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM,
-      dev_caps.adaptive_transform, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_STEREO_MVC, dev_caps.stereo_mvc, 0);
-  CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS, dev_caps.temoral_layers, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_PFRAMES,
-      dev_caps.hierarchical_pframes, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_BFRAMES,
-      dev_caps.hierarchical_bframes, 0);
-  CHECK_CAPS (NV_ENC_CAPS_LEVEL_MAX, dev_caps.level_max, 0);
-  CHECK_CAPS (NV_ENC_CAPS_LEVEL_MIN, dev_caps.level_min, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SEPARATE_COLOUR_PLANE,
-      dev_caps.separate_colour_plane, 0);
-  CHECK_CAPS (NV_ENC_CAPS_WIDTH_MAX, dev_caps.width_max, 4096);
-  CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MAX, dev_caps.height_max, 4096);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC, dev_caps.temporal_svc, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE, dev_caps.dyn_res_change, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE,
-      dev_caps.dyn_bitrate_change, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_FORCE_CONSTQP,
-      dev_caps.dyn_force_constqp, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RCMODE_CHANGE,
-      dev_caps.dyn_rcmode_change, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SUBFRAME_READBACK,
-      dev_caps.subframe_readback, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING,
-      dev_caps.constrained_encoding, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_INTRA_REFRESH, dev_caps.intra_refresh, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE,
-      dev_caps.custom_vbv_buf_size, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYNAMIC_SLICE_MODE,
-      dev_caps.dynamic_slice_mode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION,
-      dev_caps.ref_pic_invalidation, 0);
-  CHECK_CAPS (NV_ENC_CAPS_PREPROC_SUPPORT, dev_caps.preproc_support, 0);
-  /* NOTE: Async is Windows only */
-#ifdef G_OS_WIN32
-  CHECK_CAPS (NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT,
-      dev_caps.async_encoding_support, 0);
-#endif
-  CHECK_CAPS (NV_ENC_CAPS_MB_NUM_MAX, dev_caps.mb_num_max, 0);
-  CHECK_CAPS (NV_ENC_CAPS_MB_PER_SEC_MAX, dev_caps.mb_per_sec_max, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, dev_caps.yuv444_encode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, dev_caps.lossless_encode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MEONLY_MODE, dev_caps.meonly_mode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOOKAHEAD, dev_caps.lookahead, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, dev_caps.temporal_aq, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_10BIT_ENCODE,
-      dev_caps.supports_10bit_encode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_LTR_FRAMES, dev_caps.num_max_ltr_frames, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION,
-      dev_caps.weighted_prediction, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, dev_caps.bframe_ref_mode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP,
-      dev_caps.emphasis_level_map, 0);
-  CHECK_CAPS (NV_ENC_CAPS_WIDTH_MIN, dev_caps.width_min, 16);
-  CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MIN, dev_caps.height_min, 16);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES,
-      dev_caps.multiple_ref_frames, 0);
-#undef CHECK_CAPS
+  gst_nv_encoder_get_encoder_caps (session, &NV_ENC_CODEC_H264_GUID, &dev_caps);
 
   for (guint32 i = 0; i < input_format_count; i++) {
     switch (input_formats[i]) {
@@ -1660,7 +1762,7 @@ gst_nv_h264_encoder_create_class_data (GstObject * device, gpointer session,
 
   if (formats.empty ()) {
     GST_WARNING_OBJECT (device, "Empty supported input format");
-    return NULL;
+    return nullptr;
   }
 #define APPEND_STRING(dst,set,str) G_STMT_START { \
   if (set.find(str) != set.end()) { \
@@ -1701,7 +1803,7 @@ gst_nv_h264_encoder_create_class_data (GstObject * device, gpointer session,
 
   if (profiles.empty ()) {
     GST_WARNING_OBJECT (device, "Empty supported h264 profile");
-    return NULL;
+    return nullptr;
   }
 
   if (profiles.size () == 1) {
@@ -1742,27 +1844,38 @@ gst_nv_h264_encoder_create_class_data (GstObject * device, gpointer session,
   system_caps = gst_caps_from_string (sink_caps_str.c_str ());
   sink_caps = gst_caps_copy (system_caps);
 #ifdef GST_CUDA_HAS_D3D
-  if (d3d11_mode) {
+  if (device_mode == GST_NV_ENCODER_DEVICE_D3D11) {
     gst_caps_set_features (sink_caps, 0,
-        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, NULL));
-  } else
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr));
+  }
 #endif
-  {
+
+  if (device_mode == GST_NV_ENCODER_DEVICE_CUDA) {
     gst_caps_set_features (sink_caps, 0,
-        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, NULL));
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, nullptr));
   }
 
   gst_caps_append (sink_caps, system_caps);
 
-  cdata = g_new0 (GstNvH264EncoderClassData, 1);
+  cdata = gst_nv_encoder_class_data_new ();
   cdata->sink_caps = sink_caps;
   cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ());
-  cdata->dev_caps = dev_caps;
-  cdata->d3d11_mode = d3d11_mode;
-  if (d3d11_mode)
-    g_object_get (device, "adapter-luid", &cdata->adapter_luid, NULL);
-  else
-    g_object_get (device, "cuda-device-id", &cdata->cuda_device_id, NULL);
+  cdata->device_caps = dev_caps;
+  cdata->device_mode = device_mode;
+
+  /* *INDENT-OFF* */
+  for (const auto &iter: formats)
+    cdata->formats = g_list_append (cdata->formats, g_strdup (iter.c_str()));
+
+  for (const auto &iter: profiles)
+    cdata->profiles = g_list_append (cdata->profiles, g_strdup (iter.c_str()));
+  /* *INDENT-ON* */
+
+  if (device_mode == GST_NV_ENCODER_DEVICE_D3D11)
+    g_object_get (device, "adapter-luid", &cdata->adapter_luid, nullptr);
+
+  if (device_mode == GST_NV_ENCODER_DEVICE_CUDA)
+    g_object_get (device, "cuda-device-id", &cdata->cuda_device_id, nullptr);
 
   GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps,
       GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
@@ -1772,14 +1885,14 @@ gst_nv_h264_encoder_create_class_data (GstObject * device, gpointer session,
   return cdata;
 }
 
-void
+GstNvEncoderClassData *
 gst_nv_h264_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context,
     guint rank)
 {
   NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, };
   gpointer session;
   NVENCSTATUS status;
-  GstNvH264EncoderClassData *cdata;
+  GstNvEncoderClassData *cdata;
 
   GST_DEBUG_CATEGORY_INIT (gst_nv_h264_encoder_debug, "nvh264encoder", 0,
       "nvh264encoder");
@@ -1793,25 +1906,27 @@ gst_nv_h264_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context,
   status = NvEncOpenEncodeSessionEx (&session_params, &session);
   if (status != NV_ENC_SUCCESS) {
     GST_WARNING_OBJECT (context, "Failed to open session");
-    return;
+    return nullptr;
   }
 
   cdata = gst_nv_h264_encoder_create_class_data (GST_OBJECT (context), session,
-      FALSE);
+      GST_NV_ENCODER_DEVICE_CUDA);
   NvEncDestroyEncoder (session);
 
   if (!cdata)
-    return;
+    return nullptr;
+
+  gst_nv_encoder_class_data_ref (cdata);
 
   GType type;
   gchar *type_name;
   gchar *feature_name;
   GTypeInfo type_info = {
     sizeof (GstNvH264EncoderClass),
-    NULL,
-    NULL,
+    nullptr,
+    nullptr,
     (GClassInitFunc) gst_nv_h264_encoder_class_init,
-    NULL,
+    nullptr,
     cdata,
     sizeof (GstNvH264Encoder),
     0,
@@ -1841,17 +1956,19 @@ gst_nv_h264_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context,
 
   g_free (type_name);
   g_free (feature_name);
+
+  return cdata;
 }
 
 #ifdef GST_CUDA_HAS_D3D
-void
+GstNvEncoderClassData *
 gst_nv_h264_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device,
     guint rank)
 {
   NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, };
   gpointer session;
   NVENCSTATUS status;
-  GstNvH264EncoderClassData *cdata;
+  GstNvEncoderClassData *cdata;
 
   GST_DEBUG_CATEGORY_INIT (gst_nv_h264_encoder_debug, "nvh264encoder", 0,
       "nvh264encoder");
@@ -1865,25 +1982,27 @@ gst_nv_h264_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device,
   status = NvEncOpenEncodeSessionEx (&session_params, &session);
   if (status != NV_ENC_SUCCESS) {
     GST_WARNING_OBJECT (device, "Failed to open session");
-    return;
+    return nullptr;
   }
 
   cdata = gst_nv_h264_encoder_create_class_data (GST_OBJECT (device), session,
-      TRUE);
+      GST_NV_ENCODER_DEVICE_D3D11);
   NvEncDestroyEncoder (session);
 
   if (!cdata)
-    return;
+    return nullptr;
+
+  gst_nv_encoder_class_data_ref (cdata);
 
   GType type;
   gchar *type_name;
   gchar *feature_name;
   GTypeInfo type_info = {
     sizeof (GstNvH264EncoderClass),
-    NULL,
-    NULL,
+    nullptr,
+    nullptr,
     (GClassInitFunc) gst_nv_h264_encoder_class_init,
-    NULL,
+    nullptr,
     cdata,
     sizeof (GstNvH264Encoder),
     0,
@@ -1913,5 +2032,175 @@ gst_nv_h264_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device,
 
   g_free (type_name);
   g_free (feature_name);
+
+  return cdata;
 }
 #endif
+
+void
+gst_nv_h264_encoder_register_auto_select (GstPlugin * plugin,
+    GList * device_caps_list, guint rank)
+{
+  std::set < std::string > formats;
+  std::set < std::string > profiles;
+  std::string sink_caps_str;
+  std::string src_caps_str;
+  std::string format_str;
+  std::string profile_str;
+  std::string resolution_str;
+  GList *iter;
+  guint adapter_luid_size = 0;
+  gint64 adapter_luid_list[8];
+  guint cuda_device_id_size = 0;
+  guint cuda_device_id_list[8];
+  GstNvEncoderDeviceCaps dev_caps;
+  GstNvEncoderClassData *cdata;
+  GstCaps *sink_caps = nullptr;
+  GstCaps *system_caps;
+
+  GST_DEBUG_CATEGORY_INIT (gst_nv_h264_encoder_debug, "nvh264encoder", 0,
+      "nvh264encoder");
+
+  for (iter = device_caps_list; iter; iter = g_list_next (iter)) {
+    GstNvEncoderClassData *cdata = (GstNvEncoderClassData *) iter->data;
+    GList *walk;
+
+    for (walk = cdata->formats; walk; walk = g_list_next (walk))
+      formats.insert ((gchar *) walk->data);
+
+    for (walk = cdata->profiles; walk; walk = g_list_next (walk))
+      profiles.insert ((gchar *) walk->data);
+
+    if (cdata->device_mode == GST_NV_ENCODER_DEVICE_D3D11 &&
+        adapter_luid_size < G_N_ELEMENTS (adapter_luid_list) - 1) {
+      adapter_luid_list[adapter_luid_size] = cdata->adapter_luid;
+      adapter_luid_size++;
+    }
+
+    if (cdata->device_mode == GST_NV_ENCODER_DEVICE_CUDA &&
+        cuda_device_id_size < G_N_ELEMENTS (cuda_device_id_list) - 1) {
+      cuda_device_id_list[cuda_device_id_size] = cdata->cuda_device_id;
+      cuda_device_id_size++;
+    }
+
+    if (iter == device_caps_list) {
+      dev_caps = cdata->device_caps;
+    } else {
+      gst_nv_encoder_merge_device_caps (&dev_caps, &cdata->device_caps,
+          &dev_caps);
+    }
+  }
+
+  g_list_free_full (device_caps_list,
+      (GDestroyNotify) gst_nv_encoder_class_data_unref);
+  if (formats.empty () || profiles.empty ())
+    return;
+
+#define APPEND_STRING(dst,set,str) G_STMT_START { \
+  if (set.find(str) != set.end()) { \
+    if (!first) \
+      dst += ", "; \
+    dst += str; \
+    first = false; \
+  } \
+} G_STMT_END
+
+  if (formats.size () == 1) {
+    format_str = "format = (string) " + *(formats.begin ());
+  } else {
+    bool first = true;
+
+    format_str = "format = (string) { ";
+    APPEND_STRING (format_str, formats, "NV12");
+    APPEND_STRING (format_str, formats, "Y444");
+    format_str += " }";
+  }
+
+  if (profiles.size () == 1) {
+    profile_str = "profile = (string) " + *(profiles.begin ());
+  } else {
+    bool first = true;
+
+    profile_str = "profile = (string) { ";
+    APPEND_STRING (profile_str, profiles, "main");
+    APPEND_STRING (profile_str, profiles, "high");
+    APPEND_STRING (profile_str, profiles, "progressive-high");
+    APPEND_STRING (profile_str, profiles, "constrained-high");
+    APPEND_STRING (profile_str, profiles, "constrained-baseline");
+    APPEND_STRING (profile_str, profiles, "baseline");
+    APPEND_STRING (profile_str, profiles, "high-4:4:4");
+    profile_str += " }";
+  }
+#undef APPEND_STRING
+
+  resolution_str = "width = (int) [ " +
+      std::to_string (GST_ROUND_UP_16 (dev_caps.width_min))
+      + ", " + std::to_string (dev_caps.width_max) + " ]";
+  resolution_str += ", height = (int) [ " +
+      std::to_string (GST_ROUND_UP_16 (dev_caps.height_min))
+      + ", " + std::to_string (dev_caps.height_max) + " ]";
+
+  sink_caps_str = "video/x-raw, " + format_str + ", " + resolution_str;
+
+  if (dev_caps.field_encoding > 0) {
+    sink_caps_str += ", interlace-mode = (string) { interleaved, mixed }";
+  } else {
+    sink_caps_str += ", interlace-mode = (string) progressive";
+  }
+
+  src_caps_str = "video/x-h264, " + resolution_str + ", " + profile_str +
+      ", stream-format = (string) { avc, byte-stream }, alignment = (string) au";
+
+  system_caps = gst_caps_from_string (sink_caps_str.c_str ());
+  sink_caps = gst_caps_new_empty ();
+
+  if (cuda_device_id_size > 0) {
+    GstCaps *cuda_caps = gst_caps_copy (system_caps);
+    gst_caps_set_features (cuda_caps, 0,
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, nullptr));
+    gst_caps_append (sink_caps, cuda_caps);
+  }
+#ifdef GST_CUDA_HAS_D3D
+  if (adapter_luid_size > 0) {
+    GstCaps *d3d11_caps = gst_caps_copy (system_caps);
+    gst_caps_set_features (d3d11_caps, 0,
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr));
+    gst_caps_append (sink_caps, d3d11_caps);
+  }
+#endif
+
+  gst_caps_append (sink_caps, system_caps);
+
+  cdata = gst_nv_encoder_class_data_new ();
+  cdata->sink_caps = sink_caps;
+  cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ());
+  cdata->device_caps = dev_caps;
+  cdata->device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT;
+  cdata->adapter_luid = adapter_luid_list[0];
+  cdata->adapter_luid_size = adapter_luid_size;
+  memcpy (&cdata->adapter_luid_list,
+      adapter_luid_list, sizeof (adapter_luid_list));
+  cdata->cuda_device_id = cuda_device_id_list[0];
+  cdata->cuda_device_id_size = cuda_device_id_size;
+  memcpy (&cdata->cuda_device_id_list,
+      cuda_device_id_list, sizeof (cuda_device_id_list));
+
+  GType type;
+  GTypeInfo type_info = {
+    sizeof (GstNvH264EncoderClass),
+    nullptr,
+    nullptr,
+    (GClassInitFunc) gst_nv_h264_encoder_class_init,
+    nullptr,
+    cdata,
+    sizeof (GstNvH264Encoder),
+    0,
+    (GInstanceInitFunc) gst_nv_h264_encoder_init,
+  };
+
+  type = g_type_register_static (GST_TYPE_NV_ENCODER, "GstNvAutoGpuH264Enc",
+      &type_info, (GTypeFlags) 0);
+
+  if (!gst_element_register (plugin, "nvautogpuh264enc", rank, type))
+    GST_WARNING ("Failed to register plugin 'GstNvAutoGpuH264Enc'");
+}
index 77d4088..b699625 100644 (file)
 
 G_BEGIN_DECLS
 
-void gst_nv_h264_encoder_register_cuda  (GstPlugin * plugin,
-                                         GstCudaContext * context,
-                                         guint rank);
+GstNvEncoderClassData * gst_nv_h264_encoder_register_cuda (GstPlugin * plugin,
+                                                           GstCudaContext * context,
+                                                           guint rank);
 
 #ifdef GST_CUDA_HAS_D3D
-void gst_nv_h264_encoder_register_d3d11 (GstPlugin * plugin,
-                                         GstD3D11Device * device,
-                                         guint rank);
+GstNvEncoderClassData * gst_nv_h264_encoder_register_d3d11 (GstPlugin * plugin,
+                                                            GstD3D11Device * device,
+                                                            guint rank);
 #endif
 
+void gst_nv_h264_encoder_register_auto_select (GstPlugin * plugin,
+                                               GList * device_caps,
+                                               guint rank);
 
 G_END_DECLS
index 865b377..6361300 100644 (file)
@@ -33,69 +33,6 @@ GST_DEBUG_CATEGORY_STATIC (gst_nv_h265_encoder_debug);
 
 static GTypeClass *parent_class = NULL;
 
-typedef struct
-{
-  gint max_bframes;
-  gint ratecontrol_modes;
-  gint field_encoding;
-  gint monochrome;
-  gint fmo;
-  gint qpelmv;
-  gint bdirect_mode;
-  gint cabac;
-  gint adaptive_transform;
-  gint stereo_mvc;
-  gint temoral_layers;
-  gint hierarchical_pframes;
-  gint hierarchical_bframes;
-  gint level_max;
-  gint level_min;
-  gint separate_colour_plane;
-  gint width_max;
-  gint height_max;
-  gint temporal_svc;
-  gint dyn_res_change;
-  gint dyn_bitrate_change;
-  gint dyn_force_constqp;
-  gint dyn_rcmode_change;
-  gint subframe_readback;
-  gint constrained_encoding;
-  gint intra_refresh;
-  gint custom_vbv_buf_size;
-  gint dynamic_slice_mode;
-  gint ref_pic_invalidation;
-  gint preproc_support;
-  gint async_encoding_support;
-  gint mb_num_max;
-  gint mb_per_sec_max;
-  gint yuv444_encode;
-  gint lossless_encode;
-  gint sao;
-  gint meonly_mode;
-  gint lookahead;
-  gint temporal_aq;
-  gint supports_10bit_encode;
-  gint num_max_ltr_frames;
-  gint weighted_prediction;
-  gint bframe_ref_mode;
-  gint emphasis_level_map;
-  gint width_min;
-  gint height_min;
-  gint multiple_ref_frames;
-} GstNvH265EncoderDeviceCaps;
-
-typedef struct
-{
-  GstCaps *sink_caps;
-  GstCaps *src_caps;
-
-  guint cuda_device_id;
-  gint64 adapter_luid;
-  gboolean d3d11_mode;
-
-  GstNvH265EncoderDeviceCaps dev_caps;
-} GstNvH265EncoderClassData;
-
 enum
 {
   PROP_0,
@@ -187,7 +124,12 @@ typedef struct _GstNvH265Encoder
   GstNvH265EncoderStreamFormat stream_format;
   GstH265Parser *parser;
 
+  GstNvEncoderDeviceMode selected_device_mode;
+
   /* Properties */
+  guint cuda_device_id;
+  gint64 adapter_luid;
+
   GstNvEncoderPreset preset;
   gboolean weighted_pred;
 
@@ -226,11 +168,20 @@ typedef struct _GstNvH265EncoderClass
 {
   GstNvEncoderClass parent_class;
 
-  GstNvH265EncoderDeviceCaps dev_caps;
-
   guint cuda_device_id;
   gint64 adapter_luid;
-  gboolean d3d11_mode;
+
+  GstNvEncoderDeviceMode device_mode;
+
+  /* representative device caps */
+  GstNvEncoderDeviceCaps device_caps;
+
+  /* auto gpu select mode */
+  guint cuda_device_id_list[8];
+  guint cuda_device_id_size;
+
+  gint64 adapter_luid_list[8];
+  guint adapter_luid_size;
 } GstNvH265EncoderClass;
 
 #define GST_NV_H265_ENCODER(object) ((GstNvH265Encoder *) (object))
@@ -254,6 +205,9 @@ static GstBuffer *gst_nv_h265_encoder_create_output_buffer (GstNvEncoder *
 static GstNvEncoderReconfigure
 gst_nv_h265_encoder_check_reconfigure (GstNvEncoder * encoder,
     NV_ENC_CONFIG * config);
+static gboolean gst_nv_h265_encoder_select_device (GstNvEncoder * encoder,
+    const GstVideoInfo * info, GstBuffer * buffer,
+    GstNvEncoderDeviceData * data);
 
 static void
 gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
@@ -262,8 +216,8 @@ gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
   GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass);
   GstNvEncoderClass *nvenc_class = GST_NV_ENCODER_CLASS (klass);
-  GstNvH265EncoderClassData *cdata = (GstNvH265EncoderClassData *) data;
-  GstNvH265EncoderDeviceCaps *dev_caps = &cdata->dev_caps;
+  GstNvEncoderClassData *cdata = (GstNvEncoderClassData *) data;
+  GstNvEncoderDeviceCaps *dev_caps = &cdata->device_caps;
   GParamFlags param_flags = (GParamFlags) (G_PARAM_READWRITE |
       GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS);
   GParamFlags conditional_param_flags = (GParamFlags) (G_PARAM_READWRITE |
@@ -276,19 +230,41 @@ gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
   object_class->set_property = gst_nv_h265_encoder_set_property;
   object_class->get_property = gst_nv_h265_encoder_get_property;
 
-  if (cdata->d3d11_mode) {
-    g_object_class_install_property (object_class, PROP_ADAPTER_LUID,
-        g_param_spec_int64 ("adapter-luid", "Adapter LUID",
-            "DXGI Adapter LUID (Locally Unique Identifier) of associated GPU",
-            G_MININT64, G_MAXINT64, cdata->adapter_luid,
-            (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
-  } else {
-    g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID,
-        g_param_spec_uint ("cuda-device-id", "CUDA Device ID",
-            "CUDA device ID of associated GPU",
-            0, G_MAXINT, cdata->cuda_device_id,
-            (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+  switch (cdata->device_mode) {
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID,
+          g_param_spec_uint ("cuda-device-id", "CUDA Device ID",
+              "CUDA device ID of associated GPU",
+              0, G_MAXINT, cdata->cuda_device_id,
+              (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+      break;
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      g_object_class_install_property (object_class, PROP_ADAPTER_LUID,
+          g_param_spec_int64 ("adapter-luid", "Adapter LUID",
+              "DXGI Adapter LUID (Locally Unique Identifier) of associated GPU",
+              G_MININT64, G_MAXINT64, cdata->adapter_luid,
+              (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+      break;
+    case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
+      if (cdata->cuda_device_id_size > 0) {
+        g_object_class_install_property (object_class, PROP_CUDA_DEVICE_ID,
+            g_param_spec_uint ("cuda-device-id", "CUDA Device ID",
+                "CUDA device ID to use",
+                0, G_MAXINT, cdata->cuda_device_id, conditional_param_flags));
+      }
+      if (cdata->adapter_luid_size > 0) {
+        g_object_class_install_property (object_class, PROP_ADAPTER_LUID,
+            g_param_spec_int64 ("adapter-luid", "Adapter LUID",
+                "DXGI Adapter LUID (Locally Unique Identifier) to use",
+                G_MININT64, G_MAXINT64, cdata->adapter_luid,
+                conditional_param_flags));
+      }
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
   }
+
   g_object_class_install_property (object_class, PROP_PRESET,
       g_param_spec_enum ("preset", "Encoding Preset",
           "Encoding Preset", GST_TYPE_NV_ENCODER_PRESET,
@@ -418,18 +394,31 @@ gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
           "ignored if negotiated stream-format is \"hvc1\"",
           DEFAULT_REPEAT_SEQUENCE_HEADER, param_flags));
 
-  if (cdata->d3d11_mode) {
-    gst_element_class_set_metadata (element_class,
-        "NVENC H.265 Video Encoder Direct3D11 Mode",
-        "Codec/Encoder/Video/Hardware",
-        "Encode H.265 video streams using NVCODEC API Direct3D11 Mode",
-        "Seungha Yang <seungha@centricular.com>");
-  } else {
-    gst_element_class_set_metadata (element_class,
-        "NVENC H.265 Video Encoder CUDA Mode",
-        "Codec/Encoder/Video/Hardware",
-        "Encode H.265 video streams using NVCODEC API CUDA Mode",
-        "Seungha Yang <seungha@centricular.com>");
+  switch (cdata->device_mode) {
+    case GST_NV_ENCODER_DEVICE_CUDA:
+      gst_element_class_set_static_metadata (element_class,
+          "NVENC H.265 Video Encoder CUDA Mode",
+          "Codec/Encoder/Video/Hardware",
+          "Encode H.265 video streams using NVCODEC API CUDA Mode",
+          "Seungha Yang <seungha@centricular.com>");
+      break;
+    case GST_NV_ENCODER_DEVICE_D3D11:
+      gst_element_class_set_static_metadata (element_class,
+          "NVENC H.265 Video Encoder Direct3D11 Mode",
+          "Codec/Encoder/Video/Hardware",
+          "Encode H.264 video streams using NVCODEC API Direct3D11 Mode",
+          "Seungha Yang <seungha@centricular.com>");
+      break;
+    case GST_NV_ENCODER_DEVICE_AUTO_SELECT:
+      gst_element_class_set_static_metadata (element_class,
+          "NVENC H.265 Video Encoder Auto GPU select Mode",
+          "Codec/Encoder/Video/Hardware",
+          "Encode H.264 video streams using NVCODEC API auto GPU select Mode",
+          "Seungha Yang <seungha@centricular.com>");
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
   }
 
   gst_element_class_add_pad_template (element_class,
@@ -448,15 +437,21 @@ gst_nv_h265_encoder_class_init (GstNvH265EncoderClass * klass, gpointer data)
       GST_DEBUG_FUNCPTR (gst_nv_h265_encoder_create_output_buffer);
   nvenc_class->check_reconfigure =
       GST_DEBUG_FUNCPTR (gst_nv_h265_encoder_check_reconfigure);
+  nvenc_class->select_device =
+      GST_DEBUG_FUNCPTR (gst_nv_h265_encoder_select_device);
 
-  klass->dev_caps = cdata->dev_caps;
+  klass->device_caps = cdata->device_caps;
   klass->cuda_device_id = cdata->cuda_device_id;
   klass->adapter_luid = cdata->adapter_luid;
-  klass->d3d11_mode = cdata->d3d11_mode;
-
-  gst_caps_unref (cdata->sink_caps);
-  gst_caps_unref (cdata->src_caps);
-  g_free (cdata);
+  klass->device_mode = cdata->device_mode;
+  klass->cuda_device_id_size = cdata->cuda_device_id_size;
+  klass->adapter_luid_size = cdata->adapter_luid_size;
+  memcpy (klass->cuda_device_id_list, cdata->cuda_device_id_list,
+      sizeof (klass->cuda_device_id_list));
+  memcpy (klass->adapter_luid_list, cdata->adapter_luid_list,
+      sizeof (klass->adapter_luid_list));
+
+  gst_nv_encoder_class_data_unref (cdata);
 }
 
 static void
@@ -466,6 +461,9 @@ gst_nv_h265_encoder_init (GstNvH265Encoder * self)
 
   g_mutex_init (&self->prop_lock);
 
+  self->selected_device_mode = klass->device_mode;
+  self->cuda_device_id = klass->cuda_device_id;
+  self->adapter_luid = klass->adapter_luid;
   self->preset = DEFAULT_PRESET;
   self->weighted_pred = DEFAULT_WEIGHTED_PRED;
   self->gop_size = DEFAULT_GOP_SIZE;
@@ -498,13 +496,8 @@ gst_nv_h265_encoder_init (GstNvH265Encoder * self)
 
   self->parser = gst_h265_parser_new ();
 
-  if (klass->d3d11_mode) {
-    gst_nv_encoder_set_dxgi_adapter_luid (GST_NV_ENCODER (self),
-        klass->adapter_luid);
-  } else {
-    gst_nv_encoder_set_cuda_device_id (GST_NV_ENCODER (self),
-        klass->cuda_device_id);
-  }
+  gst_nv_encoder_set_device_mode (GST_NV_ENCODER (self), klass->device_mode,
+      klass->cuda_device_id, klass->adapter_luid);
 }
 
 static void
@@ -622,9 +615,43 @@ gst_nv_h265_encoder_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
 {
   GstNvH265Encoder *self = GST_NV_H265_ENCODER (object);
+  GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
 
   g_mutex_lock (&self->prop_lock);
   switch (prop_id) {
+    case PROP_ADAPTER_LUID:{
+      gint64 adapter_luid = g_value_get_int64 (value);
+      gboolean is_valid = FALSE;
+
+      for (guint i = 0; i < klass->adapter_luid_size; i++) {
+        if (klass->adapter_luid_list[i] == adapter_luid) {
+          self->adapter_luid = adapter_luid;
+          is_valid = TRUE;
+          break;
+        }
+      }
+
+      if (!is_valid)
+        g_warning ("%" G_GINT64_FORMAT " is not a valid adapter luid",
+            adapter_luid);
+      break;
+    }
+    case PROP_CUDA_DEVICE_ID:{
+      guint cuda_device_id = g_value_get_uint (value);
+      gboolean is_valid = FALSE;
+
+      for (guint i = 0; i < klass->cuda_device_id_size; i++) {
+        if (klass->cuda_device_id_list[i] == cuda_device_id) {
+          self->cuda_device_id = cuda_device_id;
+          is_valid = TRUE;
+          break;
+        }
+      }
+
+      if (!is_valid)
+        g_warning ("%d is not a valid cuda device id", cuda_device_id);
+      break;
+    }
     case PROP_PRESET:{
       GstNvEncoderPreset preset = (GstNvEncoderPreset) g_value_get_enum (value);
       if (preset != self->preset) {
@@ -737,14 +764,13 @@ gst_nv_h265_encoder_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec)
 {
   GstNvH265Encoder *self = GST_NV_H265_ENCODER (object);
-  GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
 
   switch (prop_id) {
     case PROP_ADAPTER_LUID:
-      g_value_set_int64 (value, klass->adapter_luid);
+      g_value_set_int64 (value, self->adapter_luid);
       break;
     case PROP_CUDA_DEVICE_ID:
-      g_value_set_uint (value, klass->cuda_device_id);
+      g_value_set_uint (value, self->cuda_device_id);
       break;
     case PROP_PRESET:
       g_value_set_enum (value, self->preset);
@@ -967,7 +993,7 @@ gst_nv_h265_encoder_set_format (GstNvEncoder * encoder,
 {
   GstNvH265Encoder *self = GST_NV_H265_ENCODER (encoder);
   GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
-  GstNvH265EncoderDeviceCaps *dev_caps = &klass->dev_caps;
+  GstNvEncoderDeviceCaps *dev_caps = &klass->device_caps;
   NV_ENC_RC_PARAMS *rc_params;
   GstVideoInfo *info = &state->info;
   NVENCSTATUS status;
@@ -980,6 +1006,11 @@ gst_nv_h265_encoder_set_format (GstNvEncoder * encoder,
   GUID selected_profile = NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID;
   guint chroma_format_index = 1;
   guint bitdepth_minus8 = 0;
+  gboolean bframe_aborted = FALSE;
+  gboolean weight_pred_aborted = FALSE;
+  gboolean vbv_buffer_size_aborted = FALSE;
+  gboolean lookahead_aborted = FALSE;
+  gboolean temporal_aq_aborted = FALSE;
 
   self->stream_format = GST_NV_H265_ENCODER_BYTE_STREAM;
 
@@ -1040,6 +1071,52 @@ gst_nv_h265_encoder_set_format (GstNvEncoder * encoder,
 
   g_mutex_lock (&self->prop_lock);
 
+  if (klass->device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT) {
+    GstNvEncoderDeviceCaps dev_caps;
+
+    gst_nv_encoder_get_encoder_caps (session,
+        &NV_ENC_CODEC_HEVC_GUID, &dev_caps);
+
+    if (self->bframes > 0 && !dev_caps.max_bframes) {
+      self->bframes = 0;
+      bframe_aborted = TRUE;
+
+      GST_INFO_OBJECT (self, "B-frame was enabled but not support by device");
+    }
+
+    if (self->weighted_pred && !dev_caps.weighted_prediction) {
+      self->weighted_pred = FALSE;
+      weight_pred_aborted = TRUE;
+
+      GST_INFO_OBJECT (self,
+          "Weighted prediction was enabled but not support by device");
+    }
+
+    if (self->vbv_buffer_size && !dev_caps.custom_vbv_buf_size) {
+      self->vbv_buffer_size = 0;
+      vbv_buffer_size_aborted = TRUE;
+
+      GST_INFO_OBJECT (self,
+          "VBV buffer size was specified but not supported by device");
+    }
+
+    if (self->rc_lookahead && !dev_caps.lookahead) {
+      self->rc_lookahead = 0;
+      lookahead_aborted = TRUE;
+
+      GST_INFO_OBJECT (self,
+          "VBV buffer size was specified but not supported by device");
+    }
+
+    if (self->temporal_aq && !dev_caps.temporal_aq) {
+      self->temporal_aq = FALSE;
+      temporal_aq_aborted = TRUE;
+
+      GST_INFO_OBJECT (self,
+          "temporal-aq was enabled but not supported by device");
+    }
+  }
+
   init_params->version = gst_nvenc_get_initialize_params_version ();
   init_params->encodeGUID = NV_ENC_CODEC_HEVC_GUID;
 
@@ -1219,6 +1296,17 @@ gst_nv_h265_encoder_set_format (GstNvEncoder * encoder,
 
   g_mutex_unlock (&self->prop_lock);
 
+  if (bframe_aborted)
+    g_object_notify (G_OBJECT (self), "b-frames");
+  if (weight_pred_aborted)
+    g_object_notify (G_OBJECT (self), "weighted-pred");
+  if (vbv_buffer_size_aborted)
+    g_object_notify (G_OBJECT (self), "vbv-buffer-size");
+  if (lookahead_aborted)
+    g_object_notify (G_OBJECT (self), "rc-lookahead");
+  if (temporal_aq_aborted)
+    g_object_notify (G_OBJECT (self), "temporal-aq");
+
   return TRUE;
 }
 
@@ -1514,7 +1602,7 @@ gst_nv_h265_encoder_check_reconfigure (GstNvEncoder * encoder,
 
   if (self->bitrate_updated) {
     GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
-    if (klass->dev_caps.dyn_bitrate_change > 0) {
+    if (klass->device_caps.dyn_bitrate_change > 0) {
       config->rcParams.averageBitRate = self->bitrate * 1024;
       config->rcParams.maxBitRate = self->max_bitrate * 1024;
       reconfig = GST_NV_ENCODER_RECONFIGURE_BITRATE;
@@ -1532,13 +1620,113 @@ done:
   return reconfig;
 }
 
-static GstNvH265EncoderClassData *
+static gboolean
+gst_nv_h265_encoder_select_device (GstNvEncoder * encoder,
+    const GstVideoInfo * info, GstBuffer * buffer,
+    GstNvEncoderDeviceData * data)
+{
+  GstNvH265Encoder *self = GST_NV_H265_ENCODER (encoder);
+  GstNvH265EncoderClass *klass = GST_NV_H265_ENCODER_GET_CLASS (self);
+  GstMemory *mem;
+
+  memset (data, 0, sizeof (GstNvEncoderDeviceData));
+
+  g_assert (klass->device_mode == GST_NV_ENCODER_DEVICE_AUTO_SELECT);
+
+  mem = gst_buffer_peek_memory (buffer, 0);
+  if (klass->cuda_device_id_size > 0 && gst_is_cuda_memory (mem)) {
+    GstCudaMemory *cmem = GST_CUDA_MEMORY_CAST (mem);
+    GstCudaContext *context = cmem->context;
+    guint device_id;
+    gboolean found = FALSE;
+
+    g_object_get (context, "cuda-device-id", &device_id, nullptr);
+
+    data->device_mode = GST_NV_ENCODER_DEVICE_CUDA;
+    self->selected_device_mode = GST_NV_ENCODER_DEVICE_CUDA;
+
+    for (guint i = 0; i < klass->cuda_device_id_size; i++) {
+      if (klass->cuda_device_id_list[i] == device_id) {
+        data->cuda_device_id = device_id;
+        found = TRUE;
+        break;
+      }
+    }
+
+    if (!found) {
+      GST_INFO_OBJECT (self,
+          "Upstream CUDA device is not in supported device list");
+      data->cuda_device_id = self->cuda_device_id;
+    } else {
+      data->device = (GstObject *) gst_object_ref (context);
+    }
+
+    if (data->cuda_device_id != self->cuda_device_id) {
+      self->cuda_device_id = data->cuda_device_id;
+      g_object_notify (G_OBJECT (self), "cuda-device-id");
+    }
+
+    return TRUE;
+  }
+#ifdef GST_CUDA_HAS_D3D
+  if (klass->adapter_luid_size > 0 && gst_is_d3d11_memory (mem)) {
+    GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (mem);
+    GstD3D11Device *device = dmem->device;
+    gint64 adapter_luid;
+    gboolean found = FALSE;
+
+    g_object_get (device, "adapter-luid", &adapter_luid, nullptr);
+
+    data->device_mode = GST_NV_ENCODER_DEVICE_D3D11;
+    self->selected_device_mode = GST_NV_ENCODER_DEVICE_D3D11;
+
+    for (guint i = 0; i < klass->cuda_device_id_size; i++) {
+      if (klass->adapter_luid_list[i] == adapter_luid) {
+        data->adapter_luid = adapter_luid;
+        found = TRUE;
+        break;
+      }
+    }
+
+    if (!found) {
+      GST_INFO_OBJECT (self,
+          "Upstream D3D11 device is not in supported device list");
+      data->adapter_luid = self->adapter_luid;
+    } else {
+      data->device = (GstObject *) gst_object_ref (device);
+    }
+
+    if (data->adapter_luid != self->adapter_luid) {
+      self->adapter_luid = data->adapter_luid;
+      g_object_notify (G_OBJECT (self), "adapter-luid");
+    }
+
+    return TRUE;
+  }
+#endif
+
+  if (klass->cuda_device_id_size > 0 &&
+      (self->selected_device_mode != GST_NV_ENCODER_DEVICE_D3D11)) {
+    GST_INFO_OBJECT (self, "Upstream is system memory, use CUDA mode");
+    data->device_mode = GST_NV_ENCODER_DEVICE_CUDA;
+    data->cuda_device_id = self->cuda_device_id;
+  } else {
+    GST_INFO_OBJECT (self, "Upstream is system memory, use CUDA mode");
+    data->device_mode = GST_NV_ENCODER_DEVICE_D3D11;
+    data->adapter_luid = self->adapter_luid;
+  }
+
+  self->selected_device_mode = data->device_mode;
+
+  return TRUE;
+}
+
+static GstNvEncoderClassData *
 gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
-    gboolean d3d11_mode)
+    GstNvEncoderDeviceMode device_mode)
 {
   NVENCSTATUS status;
-  GstNvH265EncoderDeviceCaps dev_caps = { 0, };
-  NV_ENC_CAPS_PARAM caps_param = { 0, };
+  GstNvEncoderDeviceCaps dev_caps = { 0, };
   GUID profile_guids[16];
   NV_ENC_BUFFER_FORMAT input_formats[16];
   guint32 profile_guid_count = 0;
@@ -1550,7 +1738,7 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
   std::set < std::string > profiles;
   std::string profile_str;
   std::string resolution_str;
-  GstNvH265EncoderClassData *cdata;
+  GstNvEncoderClassData *cdata;
   GstCaps *sink_caps;
   GstCaps *system_caps;
 
@@ -1568,93 +1756,7 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
     return NULL;
   }
 
-  caps_param.version = gst_nvenc_get_caps_param_version ();
-
-#define CHECK_CAPS(to_query,val,default_val) G_STMT_START { \
-  gint _val; \
-  caps_param.capsToQuery = to_query; \
-  status = NvEncGetEncodeCaps (session, NV_ENC_CODEC_HEVC_GUID, &caps_param, \
-      &_val); \
-  if (status != NV_ENC_SUCCESS) { \
-    GST_WARNING_OBJECT (device, "Unable to query %s, status: %" \
-        GST_NVENC_STATUS_FORMAT, G_STRINGIFY (to_query), \
-        GST_NVENC_STATUS_ARGS (status)); \
-    val = default_val; \
-  } else { \
-    GST_DEBUG_OBJECT (device, "%s: %d", G_STRINGIFY (to_query), _val); \
-    val = _val; \
-  } \
-} G_STMT_END
-
-  CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_BFRAMES, dev_caps.max_bframes, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES,
-      dev_caps.ratecontrol_modes, NV_ENC_PARAMS_RC_VBR);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FIELD_ENCODING, dev_caps.field_encoding, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MONOCHROME, dev_caps.monochrome, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_FMO, dev_caps.fmo, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_QPELMV, dev_caps.qpelmv, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BDIRECT_MODE, dev_caps.bdirect_mode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CABAC, dev_caps.cabac, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM,
-      dev_caps.adaptive_transform, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_STEREO_MVC, dev_caps.stereo_mvc, 0);
-  CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS, dev_caps.temoral_layers, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_PFRAMES,
-      dev_caps.hierarchical_pframes, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_HIERARCHICAL_BFRAMES,
-      dev_caps.hierarchical_bframes, 0);
-  CHECK_CAPS (NV_ENC_CAPS_LEVEL_MAX, dev_caps.level_max, 0);
-  CHECK_CAPS (NV_ENC_CAPS_LEVEL_MIN, dev_caps.level_min, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SEPARATE_COLOUR_PLANE,
-      dev_caps.separate_colour_plane, 0);
-  CHECK_CAPS (NV_ENC_CAPS_WIDTH_MAX, dev_caps.width_max, 4096);
-  CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MAX, dev_caps.height_max, 4096);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC, dev_caps.temporal_svc, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE, dev_caps.dyn_res_change, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE,
-      dev_caps.dyn_bitrate_change, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_FORCE_CONSTQP,
-      dev_caps.dyn_force_constqp, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYN_RCMODE_CHANGE,
-      dev_caps.dyn_rcmode_change, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SUBFRAME_READBACK,
-      dev_caps.subframe_readback, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING,
-      dev_caps.constrained_encoding, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_INTRA_REFRESH, dev_caps.intra_refresh, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE,
-      dev_caps.custom_vbv_buf_size, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_DYNAMIC_SLICE_MODE,
-      dev_caps.dynamic_slice_mode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION,
-      dev_caps.ref_pic_invalidation, 0);
-  CHECK_CAPS (NV_ENC_CAPS_PREPROC_SUPPORT, dev_caps.preproc_support, 0);
-  /* NOTE: Async is Windows only */
-#ifdef G_OS_WIN32
-  CHECK_CAPS (NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT,
-      dev_caps.async_encoding_support, 0);
-#endif
-  CHECK_CAPS (NV_ENC_CAPS_MB_NUM_MAX, dev_caps.mb_num_max, 0);
-  CHECK_CAPS (NV_ENC_CAPS_MB_PER_SEC_MAX, dev_caps.mb_per_sec_max, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, dev_caps.yuv444_encode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, dev_caps.lossless_encode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_SAO, dev_caps.sao, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MEONLY_MODE, dev_caps.meonly_mode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_LOOKAHEAD, dev_caps.lookahead, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, dev_caps.temporal_aq, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_10BIT_ENCODE,
-      dev_caps.supports_10bit_encode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_NUM_MAX_LTR_FRAMES, dev_caps.num_max_ltr_frames, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION,
-      dev_caps.weighted_prediction, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, dev_caps.bframe_ref_mode, 0);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP,
-      dev_caps.emphasis_level_map, 0);
-  CHECK_CAPS (NV_ENC_CAPS_WIDTH_MIN, dev_caps.width_min, 16);
-  CHECK_CAPS (NV_ENC_CAPS_HEIGHT_MIN, dev_caps.height_min, 16);
-  CHECK_CAPS (NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES,
-      dev_caps.multiple_ref_frames, 0);
-#undef CHECK_CAPS
+  gst_nv_encoder_get_encoder_caps (session, &NV_ENC_CODEC_HEVC_GUID, &dev_caps);
 
   for (guint32 i = 0; i < input_format_count; i++) {
     switch (input_formats[i]) {
@@ -1662,7 +1764,7 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
         formats.insert ("NV12");
         break;
       case NV_ENC_BUFFER_FORMAT_YUV444:
-        if (!d3d11_mode && dev_caps.yuv444_encode)
+        if (dev_caps.yuv444_encode)
           formats.insert ("Y444");
         break;
       case NV_ENC_BUFFER_FORMAT_YUV420_10BIT:
@@ -1753,27 +1855,38 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
   system_caps = gst_caps_from_string (sink_caps_str.c_str ());
   sink_caps = gst_caps_copy (system_caps);
 #ifdef GST_CUDA_HAS_D3D
-  if (d3d11_mode) {
+  if (device_mode == GST_NV_ENCODER_DEVICE_D3D11) {
     gst_caps_set_features (sink_caps, 0,
-        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, NULL));
-  } else
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr));
+  }
 #endif
-  {
+
+  if (device_mode == GST_NV_ENCODER_DEVICE_CUDA) {
     gst_caps_set_features (sink_caps, 0,
-        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, NULL));
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, nullptr));
   }
 
   gst_caps_append (sink_caps, system_caps);
 
-  cdata = g_new0 (GstNvH265EncoderClassData, 1);
+  cdata = gst_nv_encoder_class_data_new ();
   cdata->sink_caps = sink_caps;
   cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ());
-  cdata->dev_caps = dev_caps;
-  cdata->d3d11_mode = d3d11_mode;
-  if (d3d11_mode)
-    g_object_get (device, "adapter-luid", &cdata->adapter_luid, NULL);
-  else
-    g_object_get (device, "cuda-device-id", &cdata->cuda_device_id, NULL);
+  cdata->device_caps = dev_caps;
+  cdata->device_mode = device_mode;
+
+  /* *INDENT-OFF* */
+  for (const auto &iter: formats)
+    cdata->formats = g_list_append (cdata->formats, g_strdup (iter.c_str()));
+
+  for (const auto &iter: profiles)
+    cdata->profiles = g_list_append (cdata->profiles, g_strdup (iter.c_str()));
+  /* *INDENT-ON* */
+
+  if (device_mode == GST_NV_ENCODER_DEVICE_D3D11)
+    g_object_get (device, "adapter-luid", &cdata->adapter_luid, nullptr);
+
+  if (device_mode == GST_NV_ENCODER_DEVICE_CUDA)
+    g_object_get (device, "cuda-device-id", &cdata->cuda_device_id, nullptr);
 
   GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps,
       GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
@@ -1783,14 +1896,14 @@ gst_nv_h265_encoder_create_class_data (GstObject * device, gpointer session,
   return cdata;
 }
 
-void
+GstNvEncoderClassData *
 gst_nv_h265_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context,
     guint rank)
 {
   NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, };
   gpointer session;
   NVENCSTATUS status;
-  GstNvH265EncoderClassData *cdata;
+  GstNvEncoderClassData *cdata;
 
   GST_DEBUG_CATEGORY_INIT (gst_nv_h265_encoder_debug, "nvh265encoder", 0,
       "nvh265encoder");
@@ -1804,15 +1917,17 @@ gst_nv_h265_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context,
   status = NvEncOpenEncodeSessionEx (&session_params, &session);
   if (status != NV_ENC_SUCCESS) {
     GST_WARNING_OBJECT (context, "Failed to open session");
-    return;
+    return nullptr;
   }
 
   cdata = gst_nv_h265_encoder_create_class_data (GST_OBJECT (context), session,
-      FALSE);
+      GST_NV_ENCODER_DEVICE_CUDA);
   NvEncDestroyEncoder (session);
 
   if (!cdata)
-    return;
+    return nullptr;
+
+  gst_nv_encoder_class_data_ref (cdata);
 
   GType type;
   gchar *type_name;
@@ -1852,17 +1967,19 @@ gst_nv_h265_encoder_register_cuda (GstPlugin * plugin, GstCudaContext * context,
 
   g_free (type_name);
   g_free (feature_name);
+
+  return cdata;
 }
 
 #ifdef GST_CUDA_HAS_D3D
-void
+GstNvEncoderClassData *
 gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device,
     guint rank)
 {
   NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { 0, };
   gpointer session;
   NVENCSTATUS status;
-  GstNvH265EncoderClassData *cdata;
+  GstNvEncoderClassData *cdata;
 
   GST_DEBUG_CATEGORY_INIT (gst_nv_h265_encoder_debug, "nvh265encoder", 0,
       "nvh265encoder");
@@ -1876,15 +1993,17 @@ gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device,
   status = NvEncOpenEncodeSessionEx (&session_params, &session);
   if (status != NV_ENC_SUCCESS) {
     GST_WARNING_OBJECT (device, "Failed to open session");
-    return;
+    return nullptr;
   }
 
   cdata = gst_nv_h265_encoder_create_class_data (GST_OBJECT (device), session,
-      TRUE);
+      GST_NV_ENCODER_DEVICE_D3D11);
   NvEncDestroyEncoder (session);
 
   if (!cdata)
-    return;
+    return nullptr;
+
+  gst_nv_encoder_class_data_ref (cdata);
 
   GType type;
   gchar *type_name;
@@ -1924,5 +2043,170 @@ gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device,
 
   g_free (type_name);
   g_free (feature_name);
+
+  return cdata;
 }
 #endif
+
+void
+gst_nv_h265_encoder_register_auto_select (GstPlugin * plugin,
+    GList * device_caps_list, guint rank)
+{
+  std::set < std::string > formats;
+  std::set < std::string > profiles;
+  std::string sink_caps_str;
+  std::string src_caps_str;
+  std::string format_str;
+  std::string profile_str;
+  std::string resolution_str;
+  GList *iter;
+  guint adapter_luid_size = 0;
+  gint64 adapter_luid_list[8];
+  guint cuda_device_id_size = 0;
+  guint cuda_device_id_list[8];
+  GstNvEncoderDeviceCaps dev_caps;
+  GstNvEncoderClassData *cdata;
+  GstCaps *sink_caps = nullptr;
+  GstCaps *system_caps;
+
+  GST_DEBUG_CATEGORY_INIT (gst_nv_h265_encoder_debug, "nvh265encoder", 0,
+      "nvh265encoder");
+
+  for (iter = device_caps_list; iter; iter = g_list_next (iter)) {
+    GstNvEncoderClassData *cdata = (GstNvEncoderClassData *) iter->data;
+    GList *walk;
+
+    for (walk = cdata->formats; walk; walk = g_list_next (walk))
+      formats.insert ((gchar *) walk->data);
+
+    for (walk = cdata->profiles; walk; walk = g_list_next (walk))
+      profiles.insert ((gchar *) walk->data);
+
+    if (cdata->device_mode == GST_NV_ENCODER_DEVICE_D3D11 &&
+        adapter_luid_size < G_N_ELEMENTS (adapter_luid_list) - 1) {
+      adapter_luid_list[adapter_luid_size] = cdata->adapter_luid;
+      adapter_luid_size++;
+    }
+
+    if (cdata->device_mode == GST_NV_ENCODER_DEVICE_CUDA &&
+        cuda_device_id_size < G_N_ELEMENTS (cuda_device_id_list) - 1) {
+      cuda_device_id_list[cuda_device_id_size] = cdata->cuda_device_id;
+      cuda_device_id_size++;
+    }
+
+    if (iter == device_caps_list) {
+      dev_caps = cdata->device_caps;
+    } else {
+      gst_nv_encoder_merge_device_caps (&dev_caps, &cdata->device_caps,
+          &dev_caps);
+    }
+  }
+
+  g_list_free_full (device_caps_list,
+      (GDestroyNotify) gst_nv_encoder_class_data_unref);
+  if (formats.empty () || profiles.empty ())
+    return;
+
+#define APPEND_STRING(dst,set,str) G_STMT_START { \
+  if (set.find(str) != set.end()) { \
+    if (!first) \
+      dst += ", "; \
+    dst += str; \
+    first = false; \
+  } \
+} G_STMT_END
+
+  if (formats.size () == 1) {
+    format_str = "format = (string) " + *(formats.begin ());
+  } else {
+    bool first = true;
+
+    format_str = "format = (string) { ";
+    APPEND_STRING (format_str, formats, "NV12");
+    APPEND_STRING (format_str, formats, "P010_10LE");
+    APPEND_STRING (format_str, formats, "Y444");
+    APPEND_STRING (format_str, formats, "Y444_16LE");
+    format_str += " }";
+  }
+
+  if (profiles.size () == 1) {
+    profile_str = "profile = (string) " + *(profiles.begin ());
+  } else {
+    bool first = true;
+
+    profile_str = "profile = (string) { ";
+    APPEND_STRING (profile_str, profiles, "main");
+    APPEND_STRING (profile_str, profiles, "main-10");
+    APPEND_STRING (profile_str, profiles, "main-444");
+    APPEND_STRING (profile_str, profiles, "main-444-10");
+    profile_str += " }";
+  }
+#undef APPEND_STRING
+
+  resolution_str = "width = (int) [ " +
+      std::to_string (GST_ROUND_UP_16 (dev_caps.width_min))
+      + ", " + std::to_string (dev_caps.width_max) + " ]";
+  resolution_str += ", height = (int) [ " +
+      std::to_string (GST_ROUND_UP_16 (dev_caps.height_min))
+      + ", " + std::to_string (dev_caps.height_max) + " ]";
+
+  sink_caps_str = "video/x-raw, " + format_str + ", " + resolution_str
+      + ", interlace-mode = (string) progressive";
+
+  src_caps_str = "video/x-h265, " + resolution_str + ", " + profile_str +
+      ", stream-format = (string) { hvc1, hev1, byte-stream }" +
+      ", alignment = (string) au";
+
+  system_caps = gst_caps_from_string (sink_caps_str.c_str ());
+  sink_caps = gst_caps_new_empty ();
+
+  if (cuda_device_id_size > 0) {
+    GstCaps *cuda_caps = gst_caps_copy (system_caps);
+    gst_caps_set_features (cuda_caps, 0,
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_CUDA_MEMORY, nullptr));
+    gst_caps_append (sink_caps, cuda_caps);
+  }
+#ifdef GST_CUDA_HAS_D3D
+  if (adapter_luid_size > 0) {
+    GstCaps *d3d11_caps = gst_caps_copy (system_caps);
+    gst_caps_set_features (d3d11_caps, 0,
+        gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr));
+    gst_caps_append (sink_caps, d3d11_caps);
+  }
+#endif
+
+  gst_caps_append (sink_caps, system_caps);
+
+  cdata = gst_nv_encoder_class_data_new ();
+  cdata->sink_caps = sink_caps;
+  cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ());
+  cdata->device_caps = dev_caps;
+  cdata->device_mode = GST_NV_ENCODER_DEVICE_AUTO_SELECT;
+  cdata->adapter_luid = adapter_luid_list[0];
+  cdata->adapter_luid_size = adapter_luid_size;
+  memcpy (&cdata->adapter_luid_list,
+      adapter_luid_list, sizeof (adapter_luid_list));
+  cdata->cuda_device_id = cuda_device_id_list[0];
+  cdata->cuda_device_id_size = cuda_device_id_size;
+  memcpy (&cdata->cuda_device_id_list,
+      cuda_device_id_list, sizeof (cuda_device_id_list));
+
+  GType type;
+  GTypeInfo type_info = {
+    sizeof (GstNvH265EncoderClass),
+    nullptr,
+    nullptr,
+    (GClassInitFunc) gst_nv_h265_encoder_class_init,
+    nullptr,
+    cdata,
+    sizeof (GstNvH265Encoder),
+    0,
+    (GInstanceInitFunc) gst_nv_h265_encoder_init,
+  };
+
+  type = g_type_register_static (GST_TYPE_NV_ENCODER, "GstNvAutoGpuH265Enc",
+      &type_info, (GTypeFlags) 0);
+
+  if (!gst_element_register (plugin, "nvautogpuh265enc", rank, type))
+    GST_WARNING ("Failed to register plugin 'GstNvAutoGpuH265Enc'");
+}
index fb2771f..a317c9e 100644 (file)
 
 G_BEGIN_DECLS
 
-void gst_nv_h265_encoder_register_cuda  (GstPlugin * plugin,
-                                         GstCudaContext * context,
-                                         guint rank);
+GstNvEncoderClassData * gst_nv_h265_encoder_register_cuda  (GstPlugin * plugin,
+                                                            GstCudaContext * context,
+                                                            guint rank);
 
 #ifdef GST_CUDA_HAS_D3D
-void gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin,
-                                         GstD3D11Device * device,
-                                         guint rank);
+GstNvEncoderClassData * gst_nv_h265_encoder_register_d3d11 (GstPlugin * plugin,
+                                                            GstD3D11Device * device,
+                                                            guint rank);
 #endif
 
+void gst_nv_h265_encoder_register_auto_select (GstPlugin * plugin,
+                                               GList * device_caps_list,
+                                               guint rank);
+
+
 G_END_DECLS
index 87afd31..f106064 100644 (file)
@@ -77,6 +77,8 @@ plugin_init (GstPlugin * plugin)
   gboolean use_h265_sl_dec = FALSE;
   gboolean use_vp8_sl_dec = FALSE;
   gboolean use_vp9_sl_dec = FALSE;
+  GList *h264_enc_cdata = NULL;
+  GList *h265_enc_cdata = NULL;
 
   GST_DEBUG_CATEGORY_INIT (gst_nvcodec_debug, "nvcodec", 0, "nvcodec");
   GST_DEBUG_CATEGORY_INIT (gst_nvdec_debug, "nvdec", 0, "nvdec");
@@ -242,6 +244,8 @@ plugin_init (GstPlugin * plugin)
     }
 
     if (nvenc_available) {
+      GstNvEncoderClassData *cdata;
+
 #ifdef GST_CUDA_HAS_D3D
       if (g_win32_check_windows_version (6, 0, 0, G_WIN32_OS_ANY)) {
         gint64 adapter_luid;
@@ -253,22 +257,45 @@ plugin_init (GstPlugin * plugin)
         if (!d3d11_device) {
           GST_WARNING ("Failed to d3d11 create device");
         } else {
-          gst_nv_h264_encoder_register_d3d11 (plugin,
+          cdata = gst_nv_h264_encoder_register_d3d11 (plugin,
               d3d11_device, GST_RANK_NONE);
-          gst_nv_h265_encoder_register_d3d11 (plugin,
+          if (cdata)
+            h264_enc_cdata = g_list_append (h264_enc_cdata, cdata);
+
+          cdata = gst_nv_h265_encoder_register_d3d11 (plugin,
               d3d11_device, GST_RANK_NONE);
+          if (cdata)
+            h265_enc_cdata = g_list_append (h265_enc_cdata, cdata);
+
           gst_object_unref (d3d11_device);
         }
       }
 #endif
-      gst_nv_h264_encoder_register_cuda (plugin, context, GST_RANK_NONE);
-      gst_nv_h265_encoder_register_cuda (plugin, context, GST_RANK_NONE);
+      cdata =
+          gst_nv_h264_encoder_register_cuda (plugin, context, GST_RANK_NONE);
+      if (cdata)
+        h264_enc_cdata = g_list_append (h264_enc_cdata, cdata);
+
+      cdata =
+          gst_nv_h265_encoder_register_cuda (plugin, context, GST_RANK_NONE);
+      if (cdata)
+        h265_enc_cdata = g_list_append (h265_enc_cdata, cdata);
+
       gst_nvenc_plugin_init (plugin, i, cuda_ctx);
     }
 
     gst_object_unref (context);
   }
 
+  if (h264_enc_cdata) {
+    gst_nv_h264_encoder_register_auto_select (plugin, h264_enc_cdata,
+        GST_RANK_NONE);
+  }
+  if (h265_enc_cdata) {
+    gst_nv_h265_encoder_register_auto_select (plugin, h265_enc_cdata,
+        GST_RANK_NONE);
+  }
+
   gst_cuda_memory_copy_register (plugin, GST_RANK_NONE);
 
   gst_cuda_filter_plugin_init (plugin);