/* returns static descriptions and dynamic ones (such as video/x-raw),
 * or NULL if caps aren't known at all */
static gchar *
format_info_get_desc (const FormatInfo * info, const GstCaps * caps)
{
  const GstStructure *s;

  g_assert (info != NULL);

  if (info->desc != NULL)
    return g_strdup (_(info->desc));

  s = gst_caps_get_structure (caps, 0);

  if (strcmp (info->type, "video/x-raw") == 0) {
    gchar *ret = NULL;
    const gchar *str = 0;
    GstVideoFormat format;
    const GstVideoFormatInfo *finfo;

    str = gst_structure_get_string (s, "format");
    if (str == NULL)
      return g_strdup (_("Uncompressed video"));
    format = gst_video_format_from_string (str);
    if (format == GST_VIDEO_FORMAT_UNKNOWN)
      return g_strdup (_("Uncompressed video"));

    finfo = gst_video_format_get_info (format);

    if (GST_VIDEO_FORMAT_INFO_IS_GRAY (finfo)) {
      ret = g_strdup (_("Uncompressed gray"));
    } else if (GST_VIDEO_FORMAT_INFO_IS_YUV (finfo)) {
      const gchar *subs;
      gint w_sub, h_sub, n_semi;

      w_sub = GST_VIDEO_FORMAT_INFO_W_SUB (finfo, 1);
      h_sub = GST_VIDEO_FORMAT_INFO_H_SUB (finfo, 1);

      if (w_sub == 1 && h_sub == 1) {
        subs = "4:4:4";
      } else if (w_sub == 2 && h_sub == 1) {
        subs = "4:2:2";
      } else if (w_sub == 2 && h_sub == 2) {
        subs = "4:2:0";
      } else if (w_sub == 4 && h_sub == 1) {
        subs = "4:1:1";
      } else {
        subs = "";
      }

      n_semi = GST_VIDEO_FORMAT_INFO_HAS_ALPHA (finfo) ? 3 : 2;

      if (GST_VIDEO_FORMAT_INFO_N_PLANES (finfo) == 1) {
        ret = g_strdup_printf (_("Uncompressed packed YUV %s"), subs);
      } else if (GST_VIDEO_FORMAT_INFO_N_PLANES (finfo) == n_semi) {
        ret = g_strdup_printf (_("Uncompressed semi-planar YUV %s"), subs);
      } else {
        ret = g_strdup_printf (_("Uncompressed planar YUV %s"), subs);
      }
    } else if (GST_VIDEO_FORMAT_INFO_IS_RGB (finfo)) {
      gboolean alpha, palette;
      gint bits;

      alpha = GST_VIDEO_FORMAT_INFO_HAS_ALPHA (finfo);
      palette = GST_VIDEO_FORMAT_INFO_HAS_PALETTE (finfo);
      bits = GST_VIDEO_FORMAT_INFO_BITS (finfo);

      if (palette) {
        ret = g_strdup_printf (_("Uncompressed palettized %d-bit %s"),
            bits, alpha ? "RGBA" : "RGB");
      } else {
        ret = g_strdup_printf (_("Uncompressed %d-bit %s"),
            bits, alpha ? "RGBA" : "RGB");
      }
    } else {
      ret = g_strdup (_("Uncompressed video"));
    }
    return ret;
  } else if (strcmp (info->type, "video/x-h263") == 0) {
    const gchar *variant, *ret;

    variant = gst_structure_get_string (s, "variant");
    if (variant == NULL)
      ret = "H.263";
    else if (strcmp (variant, "itu") == 0)
      ret = "ITU H.26n";        /* why not ITU H.263? (tpm) */
    else if (strcmp (variant, "lead") == 0)
      ret = "Lead H.263";
    else if (strcmp (variant, "microsoft") == 0)
      ret = "Microsoft H.263";
    else if (strcmp (variant, "vdolive") == 0)
      ret = "VDOLive";
    else if (strcmp (variant, "vivo") == 0)
      ret = "Vivo H.263";
    else if (strcmp (variant, "xirlink") == 0)
      ret = "Xirlink H.263";
    else {
      GST_WARNING ("Unknown H263 variant '%s'", variant);
      ret = "H.263";
    }
    return g_strdup (ret);
  } else if (strcmp (info->type, "video/x-h264") == 0) {
    const gchar *variant, *ret;
    const gchar *profile;

    variant = gst_structure_get_string (s, "variant");
    if (variant == NULL)
      ret = "H.264";
    else if (strcmp (variant, "itu") == 0)
      ret = "ITU H.264";
    else if (strcmp (variant, "videosoft") == 0)
      ret = "Videosoft H.264";
    else if (strcmp (variant, "lead") == 0)
      ret = "Lead H.264";
    else {
      GST_WARNING ("Unknown H264 variant '%s'", variant);
      ret = "H.264";
    }
    /* profile */
    profile = gst_structure_get_string (s, "profile");
    if (profile != NULL)
      profile = pbutils_desc_get_h264_profile_name_from_nick (profile);
    if (profile == NULL)
      return g_strdup (ret);
    return g_strdup_printf ("%s (%s Profile)", ret, profile);
  } else if (strcmp (info->type, "video/x-h265") == 0) {
    const gchar *profile = gst_structure_get_string (s, "profile");

    if (profile != NULL)
      profile = pbutils_desc_get_h265_profile_name_from_nick (profile);
    if (profile != NULL)
      return g_strdup_printf ("H.265 (%s Profile)", profile);

    return g_strdup ("H.265");
  } else if (strcmp (info->type, "video/x-dirac") == 0) {
    const gchar *profile = gst_structure_get_string (s, "profile");
    if (profile == NULL)
      return g_strdup ("Dirac");
    if (strcmp (profile, "vc2-low-delay") == 0)
      return g_strdup_printf ("Dirac (%s)", "VC-2 Low Delay Profile");
    else if (strcmp (profile, "vc2-simple") == 0)
      return g_strdup_printf ("Dirac (%s)", "VC-2 Simple Profile");
    else if (strcmp (profile, "vc2-main") == 0)
      return g_strdup_printf ("Dirac (%s)", "VC-2 Main Profile");
    else
      return g_strdup ("Dirac");
  } else if (strcmp (info->type, "video/x-divx") == 0) {
    gint ver = 0;

    if (!gst_structure_get_int (s, "divxversion", &ver) || ver <= 2) {
      GST_WARNING ("Unexpected DivX version in %" GST_PTR_FORMAT, caps);
      return g_strdup ("DivX MPEG-4");
    }
    return g_strdup_printf (_("DivX MPEG-4 Version %d"), ver);
  } else if (strcmp (info->type, "video/x-msmpeg") == 0) {
    gint ver = 0;

    if (!gst_structure_get_int (s, "msmpegversion", &ver) ||
        ver < 40 || ver > 49) {
      GST_WARNING ("Unexpected msmpegversion in %" GST_PTR_FORMAT, caps);
      return g_strdup ("Microsoft MPEG-4 4.x");
    }
    return g_strdup_printf ("Microsoft MPEG-4 4.%d", ver % 10);
  } else if (strcmp (info->type, "video/x-truemotion") == 0) {
    gint ver = 0;

    gst_structure_get_int (s, "trueversion", &ver);
    switch (ver) {
      case 1:
        return g_strdup_printf ("Duck TrueMotion 1");
      case 2:
        return g_strdup_printf ("TrueMotion 2.0");
      default:
        GST_WARNING ("Unexpected trueversion in %" GST_PTR_FORMAT, caps);
        break;
    }
    return g_strdup_printf ("TrueMotion");
  } else if (strcmp (info->type, "video/x-xan") == 0) {
    gint ver = 0;

    if (!gst_structure_get_int (s, "wcversion", &ver) || ver < 1) {
      GST_WARNING ("Unexpected wcversion in %" GST_PTR_FORMAT, caps);
      return g_strdup ("Xan Wing Commander");
    }
    return g_strdup_printf ("Xan Wing Commander %u", ver);
  } else if (strcmp (info->type, "video/x-indeo") == 0) {
    gint ver = 0;

    if (!gst_structure_get_int (s, "indeoversion", &ver) || ver < 2) {
      GST_WARNING ("Unexpected indeoversion in %" GST_PTR_FORMAT, caps);
      return g_strdup ("Intel Indeo");
    }
    return g_strdup_printf ("Intel Indeo %u", ver);
  } else if (strcmp (info->type, "audio/x-wma") == 0) {
    gint ver = 0;

    gst_structure_get_int (s, "wmaversion", &ver);
    switch (ver) {
      case 1:
      case 2:
      case 3:
        return g_strdup_printf ("Windows Media Audio %d", ver + 6);
      default:
        break;
    }
    GST_WARNING ("Unexpected wmaversion in %" GST_PTR_FORMAT, caps);
    return g_strdup ("Windows Media Audio");
  } else if (strcmp (info->type, "video/x-wmv") == 0) {
    gint ver = 0;
    const gchar *str;

    gst_structure_get_int (s, "wmvversion", &ver);
    str = gst_structure_get_string (s, "format");

    switch (ver) {
      case 1:
      case 2:
      case 3:
        if (str && strncmp (str, "MSS", 3)) {
          return g_strdup_printf ("Windows Media Video %d Screen", ver + 6);
        } else {
          return g_strdup_printf ("Windows Media Video %d", ver + 6);
        }
      default:
        break;
    }
    GST_WARNING ("Unexpected wmvversion in %" GST_PTR_FORMAT, caps);
    return g_strdup ("Windows Media Video");
  } else if (strcmp (info->type, "audio/x-mace") == 0) {
    gint ver = 0;

    gst_structure_get_int (s, "maceversion", &ver);
    if (ver == 3 || ver == 6) {
      return g_strdup_printf ("MACE-%d", ver);
    } else {
      GST_WARNING ("Unexpected maceversion in %" GST_PTR_FORMAT, caps);
      return g_strdup ("MACE");
    }
  } else if (strcmp (info->type, "video/x-svq") == 0) {
    gint ver = 0;

    gst_structure_get_int (s, "svqversion", &ver);
    if (ver == 1 || ver == 3) {
      return g_strdup_printf ("Sorensen Video %d", ver);
    } else {
      GST_WARNING ("Unexpected svqversion in %" GST_PTR_FORMAT, caps);
      return g_strdup ("Sorensen Video");
    }
  } else if (strcmp (info->type, "video/x-asus") == 0) {
    gint ver = 0;

    gst_structure_get_int (s, "asusversion", &ver);
    if (ver == 1 || ver == 2) {
      return g_strdup_printf ("Asus Video %d", ver);
    } else {
      GST_WARNING ("Unexpected asusversion in %" GST_PTR_FORMAT, caps);
      return g_strdup ("Asus Video");
    }
  } else if (strcmp (info->type, "video/x-ati-vcr") == 0) {
    gint ver = 0;

    gst_structure_get_int (s, "vcrversion", &ver);
    if (ver == 1 || ver == 2) {
      return g_strdup_printf ("ATI VCR %d", ver);
    } else {
      GST_WARNING ("Unexpected acrversion in %" GST_PTR_FORMAT, caps);
      return g_strdup ("ATI VCR");
    }
  } else if (strcmp (info->type, "audio/x-adpcm") == 0) {
    const GValue *layout_val;

    layout_val = gst_structure_get_value (s, "layout");
    if (layout_val != NULL && G_VALUE_HOLDS_STRING (layout_val)) {
      const gchar *layout;

      if ((layout = g_value_get_string (layout_val))) {
        gchar *layout_upper, *ret;

        if (strcmp (layout, "swf") == 0)
          return g_strdup ("Shockwave ADPCM");
        if (strcmp (layout, "microsoft") == 0)
          return g_strdup ("Microsoft ADPCM");
        if (strcmp (layout, "quicktime") == 0)
          return g_strdup ("Quicktime ADPCM");
        if (strcmp (layout, "westwood") == 0)
          return g_strdup ("Westwood ADPCM");
        if (strcmp (layout, "yamaha") == 0)
          return g_strdup ("Yamaha ADPCM");
        /* FIXME: other layouts: sbpro2, sbpro3, sbpro4, ct, g726, ea,
         * adx, xa, 4xm, smjpeg, dk4, dk3, dvi */
        layout_upper = g_ascii_strup (layout, -1);
        ret = g_strdup_printf ("%s ADPCM", layout_upper);
        g_free (layout_upper);
        return ret;
      }
    }
    return g_strdup ("ADPCM");
  } else if (strcmp (info->type, "audio/mpeg") == 0) {
    gint ver = 0, layer = 0;

    gst_structure_get_int (s, "mpegversion", &ver);

    switch (ver) {
      case 1:
        gst_structure_get_int (s, "layer", &layer);
        switch (layer) {
          case 1:
          case 2:
          case 3:
            return g_strdup_printf ("MPEG-1 Layer %d (MP%d)", layer, layer);
          default:
            break;
        }
        GST_WARNING ("Unexpected MPEG-1 layer in %" GST_PTR_FORMAT, caps);
        return g_strdup ("MPEG-1 Audio");
      case 2:
        return g_strdup ("MPEG-2 AAC");
      case 4:
        return g_strdup ("MPEG-4 AAC");
      default:
        break;
    }
    GST_WARNING ("Unexpected audio mpegversion in %" GST_PTR_FORMAT, caps);
    return g_strdup ("MPEG Audio");
  } else if (strcmp (info->type, "audio/x-pn-realaudio") == 0) {
    gint ver = 0;

    gst_structure_get_int (s, "raversion", &ver);
    switch (ver) {
      case 1:
        return g_strdup ("RealAudio 14k4bps");
      case 2:
        return g_strdup ("RealAudio 28k8bps");
      case 8:
        return g_strdup ("RealAudio G2 (Cook)");
      default:
        break;
    }
    GST_WARNING ("Unexpected raversion in %" GST_PTR_FORMAT, caps);
    return g_strdup ("RealAudio");
  } else if (strcmp (info->type, "video/x-pn-realvideo") == 0) {
    gint ver = 0;

    gst_structure_get_int (s, "rmversion", &ver);
    switch (ver) {
      case 1:
        return g_strdup ("RealVideo 1.0");
      case 2:
        return g_strdup ("RealVideo 2.0");
      case 3:
        return g_strdup ("RealVideo 3.0");
      case 4:
        return g_strdup ("RealVideo 4.0");
      default:
        break;
    }
    GST_WARNING ("Unexpected rmversion in %" GST_PTR_FORMAT, caps);
    return g_strdup ("RealVideo");
  } else if (strcmp (info->type, "video/mpeg") == 0) {
    gboolean sysstream;
    gint ver = 0;

    if (!gst_structure_get_boolean (s, "systemstream", &sysstream)) {
      GST_WARNING ("Missing systemstream field in mpeg video caps "
          "%" GST_PTR_FORMAT, caps);
      sysstream = FALSE;
    }

    if (gst_structure_get_int (s, "mpegversion", &ver) && ver > 0 && ver <= 4) {
      if (sysstream) {
        return g_strdup_printf ("MPEG-%d System Stream", ver);
      } else {
        const gchar *profile = gst_structure_get_string (s, "profile");
        if (profile != NULL) {
          if (ver == 4)
            profile = pbutils_desc_get_mpeg4v_profile_name_from_nick (profile);
          else if (ver == 2)
            profile = pbutils_desc_get_mpeg2v_profile_name_from_nick (profile);
          else
            profile = NULL;
        }
        if (profile != NULL)
          return g_strdup_printf ("MPEG-%d Video (%s Profile)", ver, profile);
        else
          return g_strdup_printf ("MPEG-%d Video", ver);
      }
    }
    GST_WARNING ("Missing mpegversion field in mpeg video caps "
        "%" GST_PTR_FORMAT, caps);
    return g_strdup ("MPEG Video");
  } else if (strcmp (info->type, "audio/x-raw") == 0) {
    gint depth = 0;
    gboolean is_float;
    const gchar *str;
    GstAudioFormat format;
    const GstAudioFormatInfo *finfo;

    str = gst_structure_get_string (s, "format");
    format = gst_audio_format_from_string (str);
    if (format == GST_AUDIO_FORMAT_UNKNOWN)
      return g_strdup (_("Uncompressed audio"));

    finfo = gst_audio_format_get_info (format);
    depth = GST_AUDIO_FORMAT_INFO_DEPTH (finfo);
    is_float = GST_AUDIO_FORMAT_INFO_IS_FLOAT (finfo);

    return g_strdup_printf (_("Raw %d-bit %s audio"), depth,
        is_float ? "floating-point" : "PCM");
  } else if (strcmp (info->type, "video/x-tscc") == 0) {
    gint version;
    gst_structure_get_int (s, "tsccversion", &version);
    switch (version) {
      case 1:
        return g_strdup ("TechSmith Screen Capture 1");
      case 2:
        return g_strdup ("TechSmith Screen Capture 2");
      default:
        break;
    }
    GST_WARNING ("Unexpected version in %" GST_PTR_FORMAT, caps);
    return g_strdup ("TechSmith Screen Capture");
  }
  return NULL;
}
static void
check_1x1_buffer (GstBuffer * buf, GstCaps * caps)
{
  GstVideoInfo info;
  GstVideoFrame frame;
  /* the exact values we check for come from videotestsrc */
  static const guint yuv_values[] = { 81, 90, 240, 255 };
  static const guint rgb_values[] = { 0xff, 0, 0, 255 };
  static const guint gray8_values[] = { 0x51 };
  static const guint gray16_values[] = { 0x5151 };
  const guint *values;
  guint i;
  const GstVideoFormatInfo *finfo;

  fail_unless (buf != NULL);
  fail_unless (caps != NULL);

  fail_unless (gst_video_info_from_caps (&info, caps));
  fail_unless (gst_video_frame_map (&frame, &info, buf, GST_MAP_READ));

  finfo = info.finfo;

  if (GST_VIDEO_INFO_IS_YUV (&info))
    values = yuv_values;
  else if (GST_VIDEO_INFO_IS_GRAY (&info))
    if (GST_VIDEO_FORMAT_INFO_BITS (finfo) == 8)
      values = gray8_values;
    else
      values = gray16_values;
  else
    values = rgb_values;

  GST_MEMDUMP ("buffer", GST_VIDEO_FRAME_PLANE_DATA (&frame, 0), 8);

  for (i = 0; i < GST_VIDEO_FRAME_N_COMPONENTS (&frame); i++) {
    guint8 *data = GST_VIDEO_FRAME_COMP_DATA (&frame, i);

    GST_DEBUG ("W: %d", GST_VIDEO_FORMAT_INFO_W_SUB (finfo, i));
    GST_DEBUG ("H: %d", GST_VIDEO_FORMAT_INFO_H_SUB (finfo, i));

    if (GST_VIDEO_FORMAT_INFO_W_SUB (finfo,
            i) >= GST_VIDEO_FRAME_WIDTH (&frame))
      continue;
    if (GST_VIDEO_FORMAT_INFO_H_SUB (finfo,
            i) >= GST_VIDEO_FRAME_HEIGHT (&frame))
      continue;

    if (GST_VIDEO_FORMAT_INFO_BITS (finfo) == 8) {
      fail_unless_equals_int (data[0], values[i]);
    } else if (GST_VIDEO_FORMAT_INFO_BITS (finfo) == 16) {
      guint16 pixels, val;
      gint depth;

      if (GST_VIDEO_FORMAT_INFO_IS_LE (finfo))
        pixels = GST_READ_UINT16_LE (data);
      else
        pixels = GST_READ_UINT16_BE (data);

      depth = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, i);
      val = pixels >> GST_VIDEO_FORMAT_INFO_SHIFT (finfo, i);
      val = val & ((1 << depth) - 1);

      GST_DEBUG ("val %08x %d : %d", pixels, i, val);
      if (depth <= 8) {
        fail_unless_equals_int (val, values[i] >> (8 - depth));
      } else {