v4l2videoenc: Move the profile/level negotation in the base class
authorNicolas Dufresne <nicolas.dufresne@collabora.com>
Wed, 2 Aug 2017 14:39:46 +0000 (10:39 -0400)
committerNicolas Dufresne <nicolas.dufresne@collabora.com>
Wed, 2 Aug 2017 15:28:23 +0000 (11:28 -0400)
This removes duplicated code across different codec.

sys/v4l2/gstv4l2h264enc.c
sys/v4l2/gstv4l2mpeg4enc.c
sys/v4l2/gstv4l2videoenc.c
sys/v4l2/gstv4l2videoenc.h
sys/v4l2/gstv4l2vp8enc.c
sys/v4l2/gstv4l2vp9enc.c

index 81969b2..d1c6036 100644 (file)
@@ -267,222 +267,9 @@ v4l2_level_to_string (gint v4l2_level)
   return NULL;
 }
 
-struct ProfileLevelCtx
-{
-  GstV4l2H264Enc *self;
-  const gchar *profile;
-  const gchar *level;
-};
-
-static gboolean
-get_string_list (GstStructure * s, const gchar * field, GQueue * queue)
-{
-  const GValue *value;
-
-  value = gst_structure_get_value (s, field);
-
-  if (!value)
-    return FALSE;
-
-  if (GST_VALUE_HOLDS_LIST (value)) {
-    guint i;
-
-    if (gst_value_list_get_size (value) == 0)
-      return FALSE;
-
-    for (i = 0; i < gst_value_list_get_size (value); i++) {
-      const GValue *item = gst_value_list_get_value (value, i);
-
-      if (G_VALUE_HOLDS_STRING (item))
-        g_queue_push_tail (queue, g_value_dup_string (item));
-    }
-  } else if (G_VALUE_HOLDS_STRING (value)) {
-    g_queue_push_tail (queue, g_value_dup_string (value));
-  }
-
-  return TRUE;
-}
-
-static gboolean
-negotiate_profile_and_level (GstCapsFeatures * features, GstStructure * s,
-    gpointer user_data)
-{
-  struct ProfileLevelCtx *ctx = user_data;
-  GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output;
-  GQueue profiles = G_QUEUE_INIT;
-  GQueue levels = G_QUEUE_INIT;
-  gboolean failed = FALSE;
-
-  if (get_string_list (s, "profile", &profiles)) {
-    GList *l;
-
-    for (l = profiles.head; l; l = l->next) {
-      struct v4l2_control control = { 0, };
-      gint v4l2_profile;
-      const gchar *profile = l->data;
-
-      GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile);
-
-      control.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE;
-      control.value = v4l2_profile = v4l2_profile_from_string (profile);
-
-      if (control.value < 0)
-        continue;
-
-      if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) {
-        GST_WARNING_OBJECT (ctx->self, "Failed to set H264 profile: '%s'",
-            g_strerror (errno));
-        break;
-      }
-
-      profile = v4l2_profile_to_string (control.value);
-
-      if (control.value == v4l2_profile) {
-        ctx->profile = profile;
-        break;
-      }
-
-      if (g_list_find_custom (l, profile, g_str_equal)) {
-        ctx->profile = profile;
-        break;
-      }
-    }
-
-    if (profiles.length && !ctx->profile)
-      failed = TRUE;
-
-    g_queue_foreach (&profiles, (GFunc) g_free, NULL);
-    g_queue_clear (&profiles);
-  }
-
-  if (!failed && get_string_list (s, "level", &levels)) {
-    GList *l;
-
-    for (l = levels.head; l; l = l->next) {
-      struct v4l2_control control = { 0, };
-      gint v4l2_level;
-      const gchar *level = l->data;
-
-      GST_TRACE_OBJECT (ctx->self, "Trying level %s", level);
-
-      control.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
-      control.value = v4l2_level = v4l2_level_from_string (level);
-
-      if (control.value < 0)
-        continue;
-
-      if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) {
-        GST_WARNING_OBJECT (ctx->self, "Failed to set H264 level: '%s'",
-            g_strerror (errno));
-        break;
-      }
-
-      level = v4l2_level_to_string (control.value);
-
-      if (control.value == v4l2_level) {
-        ctx->level = level;
-        break;
-      }
-
-      if (g_list_find_custom (l, level, g_str_equal)) {
-        ctx->level = level;
-        break;
-      }
-    }
-
-    if (levels.length && !ctx->level)
-      failed = TRUE;
-
-    g_queue_foreach (&levels, (GFunc) g_free, NULL);
-    g_queue_clear (&levels);
-  }
-
-  /* If it failed, we continue */
-  return failed;
-}
-
-static gboolean
-gst_v4l2_h264_enc_negotiate (GstVideoEncoder * encoder)
-{
-  GstV4l2H264Enc *self = GST_V4L2_H264_ENC (encoder);
-  GstV4l2VideoEnc *venc = GST_V4L2_VIDEO_ENC (encoder);
-  GstV4l2Object *v4l2object = venc->v4l2output;
-  GstCaps *allowed_caps;
-  struct ProfileLevelCtx ctx = { self, NULL, NULL };
-  GstVideoCodecState *state;
-  GstStructure *s;
-
-  GST_DEBUG_OBJECT (self, "Negotiating H264 profile and level.");
-
-  allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
-
-  if (allowed_caps) {
-
-    if (gst_caps_is_empty (allowed_caps))
-      goto not_negotiated;
-
-    allowed_caps = gst_caps_make_writable (allowed_caps);
-
-    /* negotiate_profile_and_level() will return TRUE on failure to keep
-     * iterating, if gst_caps_foreach() returns TRUE it means there was no
-     * compatible profile and level in any of the structure */
-    if (gst_caps_foreach (allowed_caps, negotiate_profile_and_level, &ctx)) {
-      goto no_profile_level;
-    }
-  }
-
-  if (!ctx.profile) {
-    struct v4l2_control control = { 0, };
-
-    control.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE;
-
-    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
-      goto g_ctrl_failed;
-
-    ctx.profile = v4l2_profile_to_string (control.value);
-  }
-
-  if (!ctx.level) {
-    struct v4l2_control control = { 0, };
-
-    control.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
-
-    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
-      goto g_ctrl_failed;
-
-    ctx.level = v4l2_level_to_string (control.value);
-  }
-
-  GST_DEBUG_OBJECT (self, "Selected H264 profile %s at level %s",
-      ctx.profile, ctx.level);
-
-  state = gst_video_encoder_get_output_state (encoder);
-  s = gst_caps_get_structure (state->caps, 0);
-  gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile,
-      "level", G_TYPE_STRING, ctx.level, NULL);
-
-  return GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder);
-
-g_ctrl_failed:
-  GST_WARNING_OBJECT (self, "Failed to get H264 profile and level: '%s'",
-      g_strerror (errno));
-  goto not_negotiated;
-
-no_profile_level:
-  GST_WARNING_OBJECT (self, "No compatible level and profiled in caps: %"
-      GST_PTR_FORMAT, allowed_caps);
-  goto not_negotiated;
-
-not_negotiated:
-  if (allowed_caps)
-    gst_caps_unref (allowed_caps);
-  return FALSE;
-}
-
 static void
 gst_v4l2_h264_enc_init (GstV4l2H264Enc * self)
 {
-
 }
 
 static void
@@ -490,13 +277,13 @@ gst_v4l2_h264_enc_class_init (GstV4l2H264EncClass * klass)
 {
   GstElementClass *element_class;
   GObjectClass *gobject_class;
-  GstVideoEncoderClass *baseclass;
+  GstV4l2VideoEncClass *baseclass;
 
   parent_class = g_type_class_peek_parent (klass);
 
   element_class = (GstElementClass *) klass;
   gobject_class = (GObjectClass *) klass;
-  baseclass = GST_VIDEO_ENCODER_CLASS (klass);
+  baseclass = (GstV4l2VideoEncClass *) (klass);
 
   GST_DEBUG_CATEGORY_INIT (gst_v4l2_h264_enc_debug, "v4l2h264enc", 0,
       "V4L2 H.264 Encoder");
@@ -510,7 +297,14 @@ gst_v4l2_h264_enc_class_init (GstV4l2H264EncClass * klass)
       GST_DEBUG_FUNCPTR (gst_v4l2_h264_enc_set_property);
   gobject_class->get_property =
       GST_DEBUG_FUNCPTR (gst_v4l2_h264_enc_get_property);
-  baseclass->negotiate = GST_DEBUG_FUNCPTR (gst_v4l2_h264_enc_negotiate);
+
+  baseclass->codec_name = "H264";
+  baseclass->profile_cid = V4L2_CID_MPEG_VIDEO_H264_PROFILE;
+  baseclass->profile_to_string = v4l2_profile_to_string;
+  baseclass->profile_from_string = v4l2_profile_from_string;
+  baseclass->level_cid = V4L2_CID_MPEG_VIDEO_H264_LEVEL;
+  baseclass->level_to_string = v4l2_level_to_string;
+  baseclass->level_from_string = v4l2_level_from_string;
 }
 
 /* Probing functions */
index 45b4956..7b0bb77 100644 (file)
@@ -163,218 +163,6 @@ v4l2_level_to_string (gint v4l2_level)
   return NULL;
 }
 
-struct ProfileLevelCtx
-{
-  GstV4l2Mpeg4Enc *self;
-  const gchar *profile;
-  const gchar *level;
-};
-
-static gboolean
-get_string_list (GstStructure * s, const gchar * field, GQueue * queue)
-{
-  const GValue *value;
-
-  value = gst_structure_get_value (s, field);
-
-  if (!value)
-    return FALSE;
-
-  if (GST_VALUE_HOLDS_LIST (value)) {
-    guint i;
-
-    if (gst_value_list_get_size (value) == 0)
-      return FALSE;
-
-    for (i = 0; i < gst_value_list_get_size (value); i++) {
-      const GValue *item = gst_value_list_get_value (value, i);
-
-      if (G_VALUE_HOLDS_STRING (item))
-        g_queue_push_tail (queue, g_value_dup_string (item));
-    }
-  } else if (G_VALUE_HOLDS_STRING (value)) {
-    g_queue_push_tail (queue, g_value_dup_string (value));
-  }
-
-  return TRUE;
-}
-
-static gboolean
-negotiate_profile_and_level (GstCapsFeatures * features, GstStructure * s,
-    gpointer user_data)
-{
-  struct ProfileLevelCtx *ctx = user_data;
-  GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output;
-  GQueue profiles = G_QUEUE_INIT;
-  GQueue levels = G_QUEUE_INIT;
-  gboolean failed = FALSE;
-
-  if (get_string_list (s, "profile", &profiles)) {
-    GList *l;
-
-    for (l = profiles.head; l; l = l->next) {
-      struct v4l2_control control = { 0, };
-      gint v4l2_profile;
-      const gchar *profile = l->data;
-
-      GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile);
-
-      control.id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE;
-      control.value = v4l2_profile = v4l2_profile_from_string (profile);
-
-      if (control.value < 0)
-        continue;
-
-      if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) {
-        GST_WARNING_OBJECT (ctx->self, "Failed to set MPEG4 profile: '%s'",
-            g_strerror (errno));
-        break;
-      }
-
-      profile = v4l2_profile_to_string (control.value);
-
-      if (control.value == v4l2_profile) {
-        ctx->profile = profile;
-        break;
-      }
-
-      if (g_list_find_custom (l, profile, g_str_equal)) {
-        ctx->profile = profile;
-        break;
-      }
-    }
-
-    if (profiles.length && !ctx->profile)
-      failed = TRUE;
-
-    g_queue_foreach (&profiles, (GFunc) g_free, NULL);
-    g_queue_clear (&profiles);
-  }
-
-  if (!failed && get_string_list (s, "level", &levels)) {
-    GList *l;
-
-    for (l = levels.head; l; l = l->next) {
-      struct v4l2_control control = { 0, };
-      gint v4l2_level;
-      const gchar *level = l->data;
-
-      GST_TRACE_OBJECT (ctx->self, "Trying level %s", level);
-
-      control.id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL;
-      control.value = v4l2_level = v4l2_level_from_string (level);
-
-      if (control.value < 0)
-        continue;
-
-      if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) {
-        GST_WARNING_OBJECT (ctx->self, "Failed to set MPEG4 level: '%s'",
-            g_strerror (errno));
-        break;
-      }
-
-      level = v4l2_level_to_string (control.value);
-
-      if (control.value == v4l2_level) {
-        ctx->level = level;
-        break;
-      }
-
-      if (g_list_find_custom (l, level, g_str_equal)) {
-        ctx->level = level;
-        break;
-      }
-    }
-
-    if (levels.length && !ctx->level)
-      failed = TRUE;
-
-    g_queue_foreach (&levels, (GFunc) g_free, NULL);
-    g_queue_clear (&levels);
-  }
-
-  /* If it failed, we continue */
-  return failed;
-}
-
-static gboolean
-gst_v4l2_mpeg4_enc_negotiate (GstVideoEncoder * encoder)
-{
-  GstV4l2Mpeg4Enc *self = GST_V4L2_MPEG4_ENC (encoder);
-  GstV4l2VideoEnc *venc = GST_V4L2_VIDEO_ENC (encoder);
-  GstV4l2Object *v4l2object = venc->v4l2output;
-  GstCaps *allowed_caps;
-  struct ProfileLevelCtx ctx = { self, NULL, NULL };
-  GstVideoCodecState *state;
-  GstStructure *s;
-
-  GST_DEBUG_OBJECT (self, "Negotiating MPEG4 profile and level.");
-
-  allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
-
-  if (allowed_caps) {
-
-    if (gst_caps_is_empty (allowed_caps))
-      goto not_negotiated;
-
-    allowed_caps = gst_caps_make_writable (allowed_caps);
-
-    /* negotiate_profile_and_level() will return TRUE on failure to keep
-     * iterating, if gst_caps_foreach() returns TRUE it means there was no
-     * compatible profile and level in any of the structure */
-    if (gst_caps_foreach (allowed_caps, negotiate_profile_and_level, &ctx)) {
-      goto no_profile_level;
-    }
-  }
-
-  if (!ctx.profile) {
-    struct v4l2_control control = { 0, };
-
-    control.id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE;
-
-    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
-      goto g_ctrl_failed;
-
-    ctx.profile = v4l2_profile_to_string (control.value);
-  }
-
-  if (!ctx.level) {
-    struct v4l2_control control = { 0, };
-
-    control.id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL;
-
-    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
-      goto g_ctrl_failed;
-
-    ctx.level = v4l2_level_to_string (control.value);
-  }
-
-  GST_DEBUG_OBJECT (self, "Selected MPEG4 profile %s at level %s",
-      ctx.profile, ctx.level);
-
-  state = gst_video_encoder_get_output_state (encoder);
-  s = gst_caps_get_structure (state->caps, 0);
-  gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile,
-      "level", G_TYPE_STRING, ctx.level, NULL);
-
-  return GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder);
-
-g_ctrl_failed:
-  GST_WARNING_OBJECT (self, "Failed to get MPEG4 profile and level: '%s'",
-      g_strerror (errno));
-  goto not_negotiated;
-
-no_profile_level:
-  GST_WARNING_OBJECT (self, "No compatible level and profiled in caps: %"
-      GST_PTR_FORMAT, allowed_caps);
-  goto not_negotiated;
-
-not_negotiated:
-  if (allowed_caps)
-    gst_caps_unref (allowed_caps);
-  return FALSE;
-}
-
 static void
 gst_v4l2_mpeg4_enc_init (GstV4l2Mpeg4Enc * self)
 {
@@ -385,13 +173,13 @@ gst_v4l2_mpeg4_enc_class_init (GstV4l2Mpeg4EncClass * klass)
 {
   GstElementClass *element_class;
   GObjectClass *gobject_class;
-  GstVideoEncoderClass *baseclass;
+  GstV4l2VideoEncClass *baseclass;
 
   parent_class = g_type_class_peek_parent (klass);
 
   element_class = (GstElementClass *) klass;
   gobject_class = (GObjectClass *) klass;
-  baseclass = GST_VIDEO_ENCODER_CLASS (klass);
+  baseclass = (GstV4l2VideoEncClass *) (klass);
 
   GST_DEBUG_CATEGORY_INIT (gst_v4l2_mpeg4_enc_debug, "v4l2mpeg4enc", 0,
       "V4L2 MPEG4 Encoder");
@@ -406,7 +194,14 @@ gst_v4l2_mpeg4_enc_class_init (GstV4l2Mpeg4EncClass * klass)
       GST_DEBUG_FUNCPTR (gst_v4l2_mpeg4_enc_set_property);
   gobject_class->get_property =
       GST_DEBUG_FUNCPTR (gst_v4l2_mpeg4_enc_get_property);
-  baseclass->negotiate = GST_DEBUG_FUNCPTR (gst_v4l2_mpeg4_enc_negotiate);
+
+  baseclass->codec_name = "MPEG4";
+  baseclass->profile_cid = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE;
+  baseclass->profile_to_string = v4l2_profile_to_string;
+  baseclass->profile_from_string = v4l2_profile_from_string;
+  baseclass->level_cid = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL;
+  baseclass->level_to_string = v4l2_level_to_string;
+  baseclass->level_from_string = v4l2_level_from_string;
 }
 
 /* Probing functions */
index 9b14963..ee89cec 100644 (file)
@@ -373,10 +373,205 @@ gst_v4l2_video_enc_flush (GstVideoEncoder * encoder)
   return TRUE;
 }
 
+struct ProfileLevelCtx
+{
+  GstV4l2VideoEnc *self;
+  const gchar *profile;
+  const gchar *level;
+};
+
+static gboolean
+get_string_list (GstStructure * s, const gchar * field, GQueue * queue)
+{
+  const GValue *value;
+
+  value = gst_structure_get_value (s, field);
+
+  if (!value)
+    return FALSE;
+
+  if (GST_VALUE_HOLDS_LIST (value)) {
+    guint i;
+
+    if (gst_value_list_get_size (value) == 0)
+      return FALSE;
+
+    for (i = 0; i < gst_value_list_get_size (value); i++) {
+      const GValue *item = gst_value_list_get_value (value, i);
+
+      if (G_VALUE_HOLDS_STRING (item))
+        g_queue_push_tail (queue, g_value_dup_string (item));
+    }
+  } else if (G_VALUE_HOLDS_STRING (value)) {
+    g_queue_push_tail (queue, g_value_dup_string (value));
+  }
+
+  return TRUE;
+}
+
+static gboolean
+negotiate_profile_and_level (GstCapsFeatures * features, GstStructure * s,
+    gpointer user_data)
+{
+  struct ProfileLevelCtx *ctx = user_data;
+  GstV4l2VideoEncClass *klass = GST_V4L2_VIDEO_ENC_GET_CLASS (ctx->self);
+  GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output;
+  GQueue profiles = G_QUEUE_INIT;
+  GQueue levels = G_QUEUE_INIT;
+  gboolean failed = FALSE;
+
+  if (klass->profile_cid && get_string_list (s, "profile", &profiles)) {
+    GList *l;
+
+    for (l = profiles.head; l; l = l->next) {
+      struct v4l2_control control = { 0, };
+      gint v4l2_profile;
+      const gchar *profile = l->data;
+
+      GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile);
+
+      control.id = klass->profile_cid;
+      control.value = v4l2_profile = klass->profile_from_string (profile);
+
+      if (control.value < 0)
+        continue;
+
+      if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) {
+        GST_WARNING_OBJECT (ctx->self, "Failed to set %s profile: '%s'",
+            klass->codec_name, g_strerror (errno));
+        break;
+      }
+
+      profile = klass->profile_to_string (control.value);
+
+      if (control.value == v4l2_profile) {
+        ctx->profile = profile;
+        break;
+      }
+
+      if (g_list_find_custom (l, profile, g_str_equal)) {
+        ctx->profile = profile;
+        break;
+      }
+    }
+
+    if (profiles.length && !ctx->profile)
+      failed = TRUE;
+
+    g_queue_foreach (&profiles, (GFunc) g_free, NULL);
+    g_queue_clear (&profiles);
+  }
+
+  if (!failed && klass->level_cid && get_string_list (s, "level", &levels)) {
+    GList *l;
+
+    for (l = levels.head; l; l = l->next) {
+      struct v4l2_control control = { 0, };
+      gint v4l2_level;
+      const gchar *level = l->data;
+
+      GST_TRACE_OBJECT (ctx->self, "Trying level %s", level);
+
+      control.id = klass->level_cid;
+      control.value = v4l2_level = klass->level_from_string (level);
+
+      if (control.value < 0)
+        continue;
+
+      if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) {
+        GST_WARNING_OBJECT (ctx->self, "Failed to set %s level: '%s'",
+            klass->codec_name, g_strerror (errno));
+        break;
+      }
+
+      level = klass->level_to_string (control.value);
+
+      if (control.value == v4l2_level) {
+        ctx->level = level;
+        break;
+      }
+
+      if (g_list_find_custom (l, level, g_str_equal)) {
+        ctx->level = level;
+        break;
+      }
+    }
+
+    if (levels.length && !ctx->level)
+      failed = TRUE;
+
+    g_queue_foreach (&levels, (GFunc) g_free, NULL);
+    g_queue_clear (&levels);
+  }
+
+  /* If it failed, we continue */
+  return failed;
+}
+
 static gboolean
 gst_v4l2_video_enc_negotiate (GstVideoEncoder * encoder)
 {
+  GstV4l2VideoEncClass *klass = GST_V4L2_VIDEO_ENC_GET_CLASS (encoder);
   GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder);
+  GstV4l2Object *v4l2object = self->v4l2output;
+  GstCaps *allowed_caps;
+  struct ProfileLevelCtx ctx = { self, NULL, NULL };
+  GstVideoCodecState *state;
+  GstStructure *s;
+
+  GST_DEBUG_OBJECT (self, "Negotiating %s profile and level.",
+      klass->codec_name);
+
+  allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
+
+  if (allowed_caps) {
+
+    if (gst_caps_is_empty (allowed_caps))
+      goto not_negotiated;
+
+    allowed_caps = gst_caps_make_writable (allowed_caps);
+
+    /* negotiate_profile_and_level() will return TRUE on failure to keep
+     * iterating, if gst_caps_foreach() returns TRUE it means there was no
+     * compatible profile and level in any of the structure */
+    if (gst_caps_foreach (allowed_caps, negotiate_profile_and_level, &ctx)) {
+      goto no_profile_level;
+    }
+  }
+
+  if (klass->profile_cid && !ctx.profile) {
+    struct v4l2_control control = { 0, };
+
+    control.id = klass->profile_cid;
+
+    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
+      goto g_ctrl_failed;
+
+    ctx.profile = klass->profile_to_string (control.value);
+  }
+
+  if (klass->level_cid && !ctx.level) {
+    struct v4l2_control control = { 0, };
+
+    control.id = klass->level_cid;
+
+    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
+      goto g_ctrl_failed;
+
+    ctx.level = klass->level_to_string (control.value);
+  }
+
+  GST_DEBUG_OBJECT (self, "Selected %s profile %s at level %s",
+      klass->codec_name, ctx.profile, ctx.level);
+
+  state = gst_video_encoder_get_output_state (encoder);
+  s = gst_caps_get_structure (state->caps, 0);
+
+  if (klass->profile_cid)
+    gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile, NULL);
+
+  if (klass->level_cid)
+    gst_structure_set (s, "level", G_TYPE_STRING, ctx.level, NULL);
 
   if (!GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder))
     return FALSE;
@@ -388,6 +583,21 @@ gst_v4l2_video_enc_negotiate (GstVideoEncoder * encoder)
   }
 
   return TRUE;
+
+g_ctrl_failed:
+  GST_WARNING_OBJECT (self, "Failed to get %s profile and level: '%s'",
+      klass->codec_name, g_strerror (errno));
+  goto not_negotiated;
+
+no_profile_level:
+  GST_WARNING_OBJECT (self, "No compatible level and profile in caps: %"
+      GST_PTR_FORMAT, allowed_caps);
+  goto not_negotiated;
+
+not_negotiated:
+  if (allowed_caps)
+    gst_caps_unref (allowed_caps);
+  return FALSE;
 }
 
 static GstVideoCodecFrame *
@@ -783,6 +993,7 @@ gst_v4l2_video_enc_change_state (GstElement * element,
   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
 }
 
+
 static void
 gst_v4l2_video_enc_dispose (GObject * object)
 {
@@ -805,6 +1016,7 @@ gst_v4l2_video_enc_finalize (GObject * object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+
 static void
 gst_v4l2_video_enc_init (GstV4l2VideoEnc * self)
 {
index daf1892..1d5d58b 100644 (file)
@@ -43,6 +43,9 @@ G_BEGIN_DECLS
   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_VIDEO_ENC))
 #define GST_IS_V4L2_VIDEO_ENC_CLASS(obj) \
   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_VIDEO_ENC))
+#define GST_V4L2_VIDEO_ENC_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2_VIDEO_ENC, GstV4l2VideoEncClass))
+
 typedef struct _GstV4l2VideoEnc GstV4l2VideoEnc;
 typedef struct _GstV4l2VideoEncClass GstV4l2VideoEncClass;
 
@@ -71,9 +74,15 @@ struct _GstV4l2VideoEncClass
   GstVideoEncoderClass parent_class;
 
   gchar *default_device;
+  const char *codec_name;
+
+  guint32 profile_cid;
+  const gchar * (*profile_to_string) (gint v4l2_profile);
+  gint (*profile_from_string) (const gchar * profile);
 
-  GstFlowReturn (*get_output_caps) (GstVideoEncoder * encoder,
-      GstCaps ** outcaps);
+  guint32 level_cid;
+  const gchar * (*level_to_string) (gint v4l2_level);
+  gint (*level_from_string) (const gchar * level);
 };
 
 GType gst_v4l2_video_enc_get_type (void);
index 75f3586..cf3eb06 100644 (file)
@@ -104,161 +104,6 @@ v4l2_profile_to_string (gint v4l2_profile)
   return NULL;
 }
 
-struct ProfileCtx
-{
-  GstV4l2Vp8Enc *self;
-  const gchar *profile;
-};
-
-static gboolean
-get_string_list (GstStructure * s, const gchar * field, GQueue * queue)
-{
-  const GValue *value;
-
-  value = gst_structure_get_value (s, field);
-
-  if (!value)
-    return FALSE;
-
-  if (GST_VALUE_HOLDS_LIST (value)) {
-    guint i;
-
-    if (gst_value_list_get_size (value) == 0)
-      return FALSE;
-
-    for (i = 0; i < gst_value_list_get_size (value); i++) {
-      const GValue *item = gst_value_list_get_value (value, i);
-
-      if (G_VALUE_HOLDS_STRING (item))
-        g_queue_push_tail (queue, g_value_dup_string (item));
-    }
-  } else if (G_VALUE_HOLDS_STRING (value)) {
-    g_queue_push_tail (queue, g_value_dup_string (value));
-  }
-
-  return TRUE;
-}
-
-static gboolean
-negotiate_profile (GstCapsFeatures * features, GstStructure * s,
-    gpointer user_data)
-{
-  struct ProfileCtx *ctx = user_data;
-  GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output;
-  GQueue profiles = G_QUEUE_INIT;
-  gboolean failed = FALSE;
-
-  if (get_string_list (s, "profile", &profiles)) {
-    GList *l;
-
-    for (l = profiles.head; l; l = l->next) {
-      struct v4l2_control control = { 0, };
-      gint v4l2_profile;
-      const gchar *profile = l->data;
-
-      GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile);
-
-      control.id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE;
-      control.value = v4l2_profile = v4l2_profile_from_string (profile);
-
-      if (control.value < 0)
-        continue;
-
-      if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) {
-        GST_WARNING_OBJECT (ctx->self, "Failed to set VP8 profile: '%s'",
-            g_strerror (errno));
-        break;
-      }
-
-      profile = v4l2_profile_to_string (control.value);
-
-      if (control.value == v4l2_profile) {
-        ctx->profile = profile;
-        break;
-      }
-
-      if (g_list_find_custom (l, profile, g_str_equal)) {
-        ctx->profile = profile;
-        break;
-      }
-    }
-
-    if (profiles.length && !ctx->profile)
-      failed = TRUE;
-
-    g_queue_foreach (&profiles, (GFunc) g_free, NULL);
-    g_queue_clear (&profiles);
-  }
-
-  /* If it failed, we continue */
-  return failed;
-}
-
-static gboolean
-gst_v4l2_vp8_enc_negotiate (GstVideoEncoder * encoder)
-{
-  GstV4l2Vp8Enc *self = GST_V4L2_VP8_ENC (encoder);
-  GstV4l2VideoEnc *venc = GST_V4L2_VIDEO_ENC (encoder);
-  GstV4l2Object *v4l2object = venc->v4l2output;
-  GstCaps *allowed_caps;
-  struct ProfileCtx ctx = { self, NULL };
-  GstVideoCodecState *state;
-  GstStructure *s;
-
-  GST_DEBUG_OBJECT (self, "Negotiating VP8 profile.");
-
-  allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
-
-  if (allowed_caps) {
-
-    if (gst_caps_is_empty (allowed_caps))
-      goto not_negotiated;
-
-    allowed_caps = gst_caps_make_writable (allowed_caps);
-
-    /* negotiate_profile() will return TRUE on failure to keep
-     * iterating, if gst_caps_foreach() returns TRUE it means there was no
-     * compatible profile in any of the structure */
-    if (gst_caps_foreach (allowed_caps, negotiate_profile, &ctx)) {
-      goto no_profile;
-    }
-  }
-
-  if (!ctx.profile) {
-    struct v4l2_control control = { 0, };
-
-    control.id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE;
-
-    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
-      goto g_ctrl_failed;
-
-    ctx.profile = v4l2_profile_to_string (control.value);
-  }
-
-  GST_DEBUG_OBJECT (self, "Selected VP8 profile %s", ctx.profile);
-
-  state = gst_video_encoder_get_output_state (encoder);
-  s = gst_caps_get_structure (state->caps, 0);
-  gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile, NULL);
-
-  return GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder);
-
-g_ctrl_failed:
-  GST_WARNING_OBJECT (self, "Failed to get VP8 profile: '%s'",
-      g_strerror (errno));
-  goto not_negotiated;
-
-no_profile:
-  GST_WARNING_OBJECT (self, "No compatible profile in caps: %" GST_PTR_FORMAT,
-      allowed_caps);
-  goto not_negotiated;
-
-not_negotiated:
-  if (allowed_caps)
-    gst_caps_unref (allowed_caps);
-  return FALSE;
-}
-
 static void
 gst_v4l2_vp8_enc_init (GstV4l2Vp8Enc * self)
 {
@@ -269,13 +114,14 @@ gst_v4l2_vp8_enc_class_init (GstV4l2Vp8EncClass * klass)
 {
   GstElementClass *element_class;
   GObjectClass *gobject_class;
-  GstVideoEncoderClass *baseclass;
+  GstV4l2VideoEncClass *baseclass;
 
   parent_class = g_type_class_peek_parent (klass);
 
   element_class = (GstElementClass *) klass;
   gobject_class = (GObjectClass *) klass;
-  baseclass = GST_VIDEO_ENCODER_CLASS (klass);
+  baseclass = (GstV4l2VideoEncClass *) (klass);
+
 
   GST_DEBUG_CATEGORY_INIT (gst_v4l2_vp8_enc_debug, "v4l2vp8enc", 0,
       "V4L2 VP8 Encoder");
@@ -290,7 +136,11 @@ gst_v4l2_vp8_enc_class_init (GstV4l2Vp8EncClass * klass)
       GST_DEBUG_FUNCPTR (gst_v4l2_vp8_enc_set_property);
   gobject_class->get_property =
       GST_DEBUG_FUNCPTR (gst_v4l2_vp8_enc_get_property);
-  baseclass->negotiate = GST_DEBUG_FUNCPTR (gst_v4l2_vp8_enc_negotiate);
+
+  baseclass->codec_name = "VP8";
+  baseclass->profile_cid = V4L2_CID_MPEG_VIDEO_VPX_PROFILE;
+  baseclass->profile_to_string = v4l2_profile_to_string;
+  baseclass->profile_from_string = v4l2_profile_from_string;
 }
 
 /* Probing functions */
index 139525a..e938d78 100644 (file)
@@ -104,161 +104,6 @@ v4l2_profile_to_string (gint v4l2_profile)
   return NULL;
 }
 
-struct ProfileCtx
-{
-  GstV4l2Vp9Enc *self;
-  const gchar *profile;
-};
-
-static gboolean
-get_string_list (GstStructure * s, const gchar * field, GQueue * queue)
-{
-  const GValue *value;
-
-  value = gst_structure_get_value (s, field);
-
-  if (!value)
-    return FALSE;
-
-  if (GST_VALUE_HOLDS_LIST (value)) {
-    guint i;
-
-    if (gst_value_list_get_size (value) == 0)
-      return FALSE;
-
-    for (i = 0; i < gst_value_list_get_size (value); i++) {
-      const GValue *item = gst_value_list_get_value (value, i);
-
-      if (G_VALUE_HOLDS_STRING (item))
-        g_queue_push_tail (queue, g_value_dup_string (item));
-    }
-  } else if (G_VALUE_HOLDS_STRING (value)) {
-    g_queue_push_tail (queue, g_value_dup_string (value));
-  }
-
-  return TRUE;
-}
-
-static gboolean
-negotiate_profile (GstCapsFeatures * features, GstStructure * s,
-    gpointer user_data)
-{
-  struct ProfileCtx *ctx = user_data;
-  GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output;
-  GQueue profiles = G_QUEUE_INIT;
-  gboolean failed = FALSE;
-
-  if (get_string_list (s, "profile", &profiles)) {
-    GList *l;
-
-    for (l = profiles.head; l; l = l->next) {
-      struct v4l2_control control = { 0, };
-      gint v4l2_profile;
-      const gchar *profile = l->data;
-
-      GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile);
-
-      control.id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE;
-      control.value = v4l2_profile = v4l2_profile_from_string (profile);
-
-      if (control.value < 0)
-        continue;
-
-      if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) {
-        GST_WARNING_OBJECT (ctx->self, "Failed to set VP9 profile: '%s'",
-            g_strerror (errno));
-        break;
-      }
-
-      profile = v4l2_profile_to_string (control.value);
-
-      if (control.value == v4l2_profile) {
-        ctx->profile = profile;
-        break;
-      }
-
-      if (g_list_find_custom (l, profile, g_str_equal)) {
-        ctx->profile = profile;
-        break;
-      }
-    }
-
-    if (profiles.length && !ctx->profile)
-      failed = TRUE;
-
-    g_queue_foreach (&profiles, (GFunc) g_free, NULL);
-    g_queue_clear (&profiles);
-  }
-
-  /* If it failed, we continue */
-  return failed;
-}
-
-static gboolean
-gst_v4l2_vp9_enc_negotiate (GstVideoEncoder * encoder)
-{
-  GstV4l2Vp9Enc *self = GST_V4L2_VP9_ENC (encoder);
-  GstV4l2VideoEnc *venc = GST_V4L2_VIDEO_ENC (encoder);
-  GstV4l2Object *v4l2object = venc->v4l2output;
-  GstCaps *allowed_caps;
-  struct ProfileCtx ctx = { self, NULL };
-  GstVideoCodecState *state;
-  GstStructure *s;
-
-  GST_DEBUG_OBJECT (self, "Negotiating VP8 profile.");
-
-  allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
-
-  if (allowed_caps) {
-
-    if (gst_caps_is_empty (allowed_caps))
-      goto not_negotiated;
-
-    allowed_caps = gst_caps_make_writable (allowed_caps);
-
-    /* negotiate_profile() will return TRUE on failure to keep
-     * iterating, if gst_caps_foreach() returns TRUE it means there was no
-     * compatible profile in any of the structure */
-    if (gst_caps_foreach (allowed_caps, negotiate_profile, &ctx)) {
-      goto no_profile;
-    }
-  }
-
-  if (!ctx.profile) {
-    struct v4l2_control control = { 0, };
-
-    control.id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE;
-
-    if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0)
-      goto g_ctrl_failed;
-
-    ctx.profile = v4l2_profile_to_string (control.value);
-  }
-
-  GST_DEBUG_OBJECT (self, "Selected VP9 profile %s", ctx.profile);
-
-  state = gst_video_encoder_get_output_state (encoder);
-  s = gst_caps_get_structure (state->caps, 0);
-  gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile, NULL);
-
-  return GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder);
-
-g_ctrl_failed:
-  GST_WARNING_OBJECT (self, "Failed to get VP9 profile: '%s'",
-      g_strerror (errno));
-  goto not_negotiated;
-
-no_profile:
-  GST_WARNING_OBJECT (self, "No compatible profile in caps: %" GST_PTR_FORMAT,
-      allowed_caps);
-  goto not_negotiated;
-
-not_negotiated:
-  if (allowed_caps)
-    gst_caps_unref (allowed_caps);
-  return FALSE;
-}
-
 static void
 gst_v4l2_vp9_enc_init (GstV4l2Vp9Enc * self)
 {
@@ -269,13 +114,13 @@ gst_v4l2_vp9_enc_class_init (GstV4l2Vp9EncClass * klass)
 {
   GstElementClass *element_class;
   GObjectClass *gobject_class;
-  GstVideoEncoderClass *baseclass;
+  GstV4l2VideoEncClass *baseclass;
 
   parent_class = g_type_class_peek_parent (klass);
 
   element_class = (GstElementClass *) klass;
   gobject_class = (GObjectClass *) klass;
-  baseclass = GST_VIDEO_ENCODER_CLASS (klass);
+  baseclass = (GstV4l2VideoEncClass *) (klass);
 
   GST_DEBUG_CATEGORY_INIT (gst_v4l2_vp9_enc_debug, "v4l2vp9enc", 0,
       "V4L2 VP9 Encoder");
@@ -290,7 +135,11 @@ gst_v4l2_vp9_enc_class_init (GstV4l2Vp9EncClass * klass)
       GST_DEBUG_FUNCPTR (gst_v4l2_vp9_enc_set_property);
   gobject_class->get_property =
       GST_DEBUG_FUNCPTR (gst_v4l2_vp9_enc_get_property);
-  baseclass->negotiate = GST_DEBUG_FUNCPTR (gst_v4l2_vp9_enc_negotiate);
+
+  baseclass->codec_name = "VP9";
+  baseclass->profile_cid = V4L2_CID_MPEG_VIDEO_VPX_PROFILE;
+  baseclass->profile_to_string = v4l2_profile_to_string;
+  baseclass->profile_from_string = v4l2_profile_from_string;
 }
 
 /* Probing functions */