video-chroma: Add support for any combination of chroma-site flags
authorSeungha Yang <seungha@centricular.com>
Tue, 10 Nov 2020 09:01:12 +0000 (18:01 +0900)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 8 Dec 2020 07:21:28 +0000 (07:21 +0000)
We've been allowing only a few known chroma-site values such as
jpeg (not co-sited), mpeg2 (horizontally co-sited) and
dv (co-sited on alternate lines). That's insufficient for
representing all possible chroma-site values. By this commit,
we can represent any combination of chroma-site flags.
But, an exception here is that any combination with
GST_VIDEO_CHROMA_SITE_NONE will be considered as invalid value.

For any combination of chroma-site flags,
gst_video_chroma_to_string() method is deprecated in order to
return newly allocated string via a new gst_video_chroma_site_to_string()
method. And for consistent API naming, gst_video_chroma_from_string()
is also deprecated. Newly written code should use
gst_video_chroma_site_from_string() instead.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/927>

gst-libs/gst/video/video-chroma.c
gst-libs/gst/video/video-chroma.h
tests/check/libs/video.c

index f761b44..3d7f07c 100644 (file)
@@ -26,7 +26,7 @@
 
 #include "video-orc.h"
 #include "video-format.h"
-
+#include <gst/video/video-enumtypes.h>
 
 /**
  * SECTION:gstvideochroma
@@ -72,7 +72,9 @@ typedef struct
 static const ChromaSiteInfo chromasite[] = {
   {"jpeg", GST_VIDEO_CHROMA_SITE_JPEG},
   {"mpeg2", GST_VIDEO_CHROMA_SITE_MPEG2},
-  {"dv", GST_VIDEO_CHROMA_SITE_DV}
+  {"dv", GST_VIDEO_CHROMA_SITE_DV},
+  {"alt-line", GST_VIDEO_CHROMA_SITE_ALT_LINE},
+  {"cosited", GST_VIDEO_CHROMA_SITE_COSITED},
 };
 
 /**
@@ -81,18 +83,66 @@ static const ChromaSiteInfo chromasite[] = {
  *
  * Convert @s to a #GstVideoChromaSite
  *
+ * Deprecated: 1.20: Use gst_video_chroma_site_from_string() instead.
+ *
  * Returns: a #GstVideoChromaSite or %GST_VIDEO_CHROMA_SITE_UNKNOWN when @s does
  * not contain a valid chroma description.
  */
 GstVideoChromaSite
 gst_video_chroma_from_string (const gchar * s)
 {
+  return gst_video_chroma_site_from_string (s);
+}
+
+/**
+ * gst_video_chroma_site_from_string:
+ * @s: a chromasite string
+ *
+ * Convert @s to a #GstVideoChromaSite
+ *
+ * Returns: a #GstVideoChromaSite or %GST_VIDEO_CHROMA_SITE_UNKNOWN when @s does
+ * not contain a valid chroma-site description.
+ *
+ * Since: 1.20
+ */
+GstVideoChromaSite
+gst_video_chroma_site_from_string (const gchar * s)
+{
   gint i;
+  gchar **split;
+  gchar **iter;
+  GstVideoChromaSite ret = GST_VIDEO_CHROMA_SITE_UNKNOWN;
+  GFlagsClass *klass;
+
   for (i = 0; i < G_N_ELEMENTS (chromasite); i++) {
     if (g_str_equal (chromasite[i].name, s))
       return chromasite[i].site;
   }
-  return GST_VIDEO_CHROMA_SITE_UNKNOWN;
+
+  klass = (GFlagsClass *) g_type_class_ref (GST_TYPE_VIDEO_CHROMA_SITE);
+  split = g_strsplit (s, "+", 0);
+  for (iter = split; *iter; iter++) {
+    GFlagsValue *value;
+
+    value = g_flags_get_value_by_nick (klass, *iter);
+    if (!value) {
+      ret = GST_VIDEO_CHROMA_SITE_UNKNOWN;
+      goto out;
+    }
+
+    ret |= value->value;
+  }
+
+out:
+  g_type_class_unref (klass);
+  g_strfreev (split);
+
+  /* Doesn't make sense */
+  if ((ret & GST_VIDEO_CHROMA_SITE_NONE) != 0 &&
+      ret != GST_VIDEO_CHROMA_SITE_NONE)
+    return GST_VIDEO_CHROMA_SITE_UNKNOWN;
+
+  return ret;
 }
 
 /**
@@ -101,6 +151,8 @@ gst_video_chroma_from_string (const gchar * s)
  *
  * Converts @site to its string representation.
  *
+ * Deprecated: 1.20: Use gst_video_chroma_site_to_string() instead.
+ *
  * Returns: a string describing @site.
  */
 const gchar *
@@ -114,6 +166,60 @@ gst_video_chroma_to_string (GstVideoChromaSite site)
   return NULL;
 }
 
+/**
+ * gst_video_chroma_site_to_string:
+ * @site: a #GstVideoChromaSite
+ *
+ * Converts @site to its string representation.
+ *
+ * Returns: (transfer full) (nullable): a string representation of @site
+ *          or %NULL if @site contains undefined value or
+ *          is equal to %GST_VIDEO_CHROMA_SITE_UNKNOWN
+ *
+ * Since: 1.20
+ */
+gchar *
+gst_video_chroma_site_to_string (GstVideoChromaSite site)
+{
+  gint i;
+  GString *str;
+  GFlagsValue *value;
+  GFlagsClass *klass;
+
+  /* return null string for GST_VIDEO_CHROMA_SITE_UNKNOWN */
+  if (site == 0)
+    return NULL;
+
+  for (i = 0; i < G_N_ELEMENTS (chromasite); i++) {
+    if (chromasite[i].site == site)
+      return g_strdup (chromasite[i].name);
+  }
+
+  /* Doesn't make sense */
+  if ((site & GST_VIDEO_CHROMA_SITE_NONE) != 0 &&
+      site != GST_VIDEO_CHROMA_SITE_NONE)
+    return NULL;
+
+  /* Construct new string */
+  klass = (GFlagsClass *) g_type_class_ref (GST_TYPE_VIDEO_CHROMA_SITE);
+  str = g_string_new (NULL);
+  while (site != GST_VIDEO_CHROMA_SITE_UNKNOWN &&
+      (value = g_flags_get_first_value (klass, site))) {
+    if (str->len > 0)
+      g_string_append (str, "+");
+
+    g_string_append (str, value->value_nick);
+    site &= ~value->value;
+  }
+  g_type_class_unref (klass);
+
+  /* This means given chroma-site has unknown value */
+  if (site != 0)
+    return g_string_free (str, TRUE);
+
+  return g_string_free (str, FALSE);
+}
+
 struct _GstVideoChromaResample
 {
   GstVideoChromaMethod method;
index 5240078..16720b4 100644 (file)
@@ -52,12 +52,18 @@ typedef enum {
   GST_VIDEO_CHROMA_SITE_DV        = (GST_VIDEO_CHROMA_SITE_COSITED | GST_VIDEO_CHROMA_SITE_ALT_LINE),
 } GstVideoChromaSite;
 
-GST_VIDEO_API
+GST_VIDEO_DEPRECATED_FOR(gst_video_chroma_site_from_string)
 GstVideoChromaSite    gst_video_chroma_from_string   (const gchar * s);
 
-GST_VIDEO_API
+GST_VIDEO_DEPRECATED_FOR(gst_video_chroma_site_to_string)
 const gchar *         gst_video_chroma_to_string     (GstVideoChromaSite site);
 
+GST_VIDEO_API
+GstVideoChromaSite    gst_video_chroma_site_from_string (const gchar * s);
+
+GST_VIDEO_API
+gchar *               gst_video_chroma_site_to_string   (GstVideoChromaSite site);
+
 /**
  * GstVideoChromaMethod:
  * @GST_VIDEO_CHROMA_METHOD_NEAREST: Duplicates the chroma samples when
index b9b9ec7..897d9d7 100644 (file)
@@ -2398,6 +2398,81 @@ GST_END_TEST;
 #undef HEIGHT
 #undef TIME
 
+typedef struct
+{
+  const gchar *name;
+  GstVideoChromaSite site;
+} ChromaSiteElem;
+
+GST_START_TEST (test_video_chroma_site)
+{
+  ChromaSiteElem valid_sites[] = {
+    /* pre-defined flags */
+    {"jpeg", GST_VIDEO_CHROMA_SITE_JPEG},
+    {"mpeg2", GST_VIDEO_CHROMA_SITE_MPEG2},
+    {"dv", GST_VIDEO_CHROMA_SITE_DV},
+    {"alt-line", GST_VIDEO_CHROMA_SITE_ALT_LINE},
+    {"cosited", GST_VIDEO_CHROMA_SITE_COSITED},
+    /* new values */
+    {"v-cosited", GST_VIDEO_CHROMA_SITE_V_COSITED},
+    {"v-cosited+alt-line",
+        GST_VIDEO_CHROMA_SITE_V_COSITED | GST_VIDEO_CHROMA_SITE_ALT_LINE},
+  };
+  ChromaSiteElem unknown_sites[] = {
+    {NULL, GST_VIDEO_CHROMA_SITE_UNKNOWN},
+    /* Any combination with GST_VIDEO_CHROMA_SITE_NONE doesn' make sense */
+    {NULL, GST_VIDEO_CHROMA_SITE_NONE | GST_VIDEO_CHROMA_SITE_H_COSITED},
+  };
+  gint i;
+
+  for (i = 0; i < G_N_ELEMENTS (valid_sites); i++) {
+    gchar *site = gst_video_chroma_site_to_string (valid_sites[i].site);
+
+    fail_unless (site != NULL);
+    fail_unless (g_strcmp0 (site, valid_sites[i].name) == 0);
+    fail_unless (gst_video_chroma_site_from_string (site) ==
+        valid_sites[i].site);
+    g_free (site);
+  }
+
+  for (i = 0; i < G_N_ELEMENTS (unknown_sites); i++) {
+    gchar *site = gst_video_chroma_site_to_string (unknown_sites[i].site);
+    fail_unless (site == NULL);
+  }
+
+  /* totally wrong string */
+  fail_unless (gst_video_chroma_site_from_string ("foo/bar") ==
+      GST_VIDEO_CHROMA_SITE_UNKNOWN);
+
+  /* valid ones */
+  fail_unless (gst_video_chroma_site_from_string ("jpeg") ==
+      GST_VIDEO_CHROMA_SITE_NONE);
+  fail_unless (gst_video_chroma_site_from_string ("none") ==
+      GST_VIDEO_CHROMA_SITE_NONE);
+
+  fail_unless (gst_video_chroma_site_from_string ("mpeg2") ==
+      GST_VIDEO_CHROMA_SITE_H_COSITED);
+  fail_unless (gst_video_chroma_site_from_string ("h-cosited") ==
+      GST_VIDEO_CHROMA_SITE_H_COSITED);
+
+  /* Equal to "cosited" */
+  fail_unless (gst_video_chroma_site_from_string ("v-cosited+h-cosited") ==
+      GST_VIDEO_CHROMA_SITE_COSITED);
+
+  fail_unless (gst_video_chroma_site_from_string ("v-cosited") ==
+      GST_VIDEO_CHROMA_SITE_V_COSITED);
+
+  /* none + something doesn't make sense */
+  fail_unless (gst_video_chroma_site_from_string ("none+v-cosited") ==
+      GST_VIDEO_CHROMA_SITE_UNKNOWN);
+
+  /* mix of valid and invalid strings */
+  fail_unless (gst_video_chroma_site_from_string ("mpeg2+foo/bar") ==
+      GST_VIDEO_CHROMA_SITE_UNKNOWN);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_video_scaler)
 {
   GstVideoScaler *scale;
@@ -3959,6 +4034,7 @@ video_suite (void)
   tcase_add_test (tc_chain, test_overlay_composition_global_alpha);
   tcase_add_test (tc_chain, test_video_pack_unpack2);
   tcase_add_test (tc_chain, test_video_chroma);
+  tcase_add_test (tc_chain, test_video_chroma_site);
   tcase_add_test (tc_chain, test_video_scaler);
   tcase_add_test (tc_chain, test_video_color_convert_rgb_rgb);
   tcase_add_test (tc_chain, test_video_color_convert_rgb_yuv);