mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
videoconvert: be smarter when selecting a conversion
Try to select the conversion that would result in the minimal amount of quality loss. Quality loss is calculated rather arbitrarily but it avoids doing something really stupid in most cases.
This commit is contained in:
parent
3532323817
commit
33a54ba4f3
1 changed files with 142 additions and 1 deletions
|
@ -136,13 +136,149 @@ gst_video_convert_caps_remove_format_info (GstCaps * caps)
|
|||
return res;
|
||||
}
|
||||
|
||||
#define SCORE_PALETTE_LOSS 1
|
||||
#define SCORE_COLOR_LOSS 2
|
||||
#define SCORE_ALPHA_LOSS 4
|
||||
#define SCORE_CHROMA_W_LOSS 8
|
||||
#define SCORE_CHROMA_H_LOSS 16
|
||||
#define SCORE_DEPTH_LOSS 32
|
||||
|
||||
#define COLOR_MASK (GST_VIDEO_FORMAT_FLAG_YUV | \
|
||||
GST_VIDEO_FORMAT_FLAG_RGB | GST_VIDEO_FORMAT_FLAG_GRAY)
|
||||
#define ALPHA_MASK (GST_VIDEO_FORMAT_FLAG_ALPHA)
|
||||
#define PALETTE_MASK (GST_VIDEO_FORMAT_FLAG_PALETTE)
|
||||
|
||||
/* calculate how much loss a conversion would be */
|
||||
static void
|
||||
score_value (GstBaseTransform * base, const GstVideoFormatInfo * in_info,
|
||||
const GValue * val, gint * min_loss, const GstVideoFormatInfo ** out_info)
|
||||
{
|
||||
const gchar *fname;
|
||||
const GstVideoFormatInfo *t_info;
|
||||
GstVideoFormatFlags in_flags, t_flags;
|
||||
gint loss;
|
||||
|
||||
fname = g_value_get_string (val);
|
||||
t_info = gst_video_format_get_info (gst_video_format_from_string (fname));
|
||||
if (!t_info)
|
||||
return;
|
||||
|
||||
/* accept input format immediately without loss */
|
||||
if (in_info == t_info) {
|
||||
*min_loss = 0;
|
||||
*out_info = t_info;
|
||||
return;
|
||||
}
|
||||
|
||||
loss = 1;
|
||||
|
||||
in_flags = GST_VIDEO_FORMAT_INFO_FLAGS (in_info);
|
||||
in_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
|
||||
in_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
|
||||
in_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
|
||||
|
||||
t_flags = GST_VIDEO_FORMAT_INFO_FLAGS (t_info);
|
||||
t_flags &= ~GST_VIDEO_FORMAT_FLAG_LE;
|
||||
t_flags &= ~GST_VIDEO_FORMAT_FLAG_COMPLEX;
|
||||
t_flags &= ~GST_VIDEO_FORMAT_FLAG_UNPACK;
|
||||
|
||||
if ((t_flags & PALETTE_MASK) != (in_flags & PALETTE_MASK))
|
||||
loss += SCORE_PALETTE_LOSS;
|
||||
|
||||
if ((t_flags & COLOR_MASK) != (in_flags & COLOR_MASK))
|
||||
loss += SCORE_COLOR_LOSS;
|
||||
|
||||
if ((t_flags & ALPHA_MASK) != (in_flags & ALPHA_MASK))
|
||||
loss += SCORE_ALPHA_LOSS;
|
||||
|
||||
if ((in_info->h_sub[1]) < (t_info->h_sub[1]))
|
||||
loss += SCORE_CHROMA_H_LOSS;
|
||||
if ((in_info->w_sub[1]) < (t_info->w_sub[1]))
|
||||
loss += SCORE_CHROMA_W_LOSS;
|
||||
|
||||
if ((in_info->bits) > (t_info->bits))
|
||||
loss += SCORE_DEPTH_LOSS;
|
||||
|
||||
GST_DEBUG_OBJECT (base, "score %s -> %s = %d",
|
||||
GST_VIDEO_FORMAT_INFO_NAME (in_info),
|
||||
GST_VIDEO_FORMAT_INFO_NAME (t_info), loss);
|
||||
|
||||
if (loss < *min_loss) {
|
||||
GST_DEBUG_OBJECT (base, "found new best %d", loss);
|
||||
*out_info = t_info;
|
||||
*min_loss = loss;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_video_convert_fixate_format (GstBaseTransform * base, GstCaps * caps,
|
||||
GstCaps * result)
|
||||
{
|
||||
GstStructure *ins, *outs;
|
||||
const gchar *in_format;
|
||||
const GstVideoFormatInfo *in_info, *out_info = NULL;
|
||||
gint min_loss = G_MAXINT;
|
||||
guint i, capslen;
|
||||
|
||||
ins = gst_caps_get_structure (caps, 0);
|
||||
in_format = gst_structure_get_string (ins, "format");
|
||||
if (!in_format)
|
||||
return;
|
||||
|
||||
GST_DEBUG_OBJECT (base, "source format %s", in_format);
|
||||
|
||||
in_info =
|
||||
gst_video_format_get_info (gst_video_format_from_string (in_format));
|
||||
if (!in_info)
|
||||
return;
|
||||
|
||||
outs = gst_caps_get_structure (result, 0);
|
||||
|
||||
capslen = gst_caps_get_size (result);
|
||||
GST_DEBUG_OBJECT (base, "iterate %d structures", capslen);
|
||||
for (i = 0; i < capslen; i++) {
|
||||
GstStructure *tests;
|
||||
const GValue *format;
|
||||
|
||||
tests = gst_caps_get_structure (result, i);
|
||||
format = gst_structure_get_value (tests, "format");
|
||||
/* should not happen */
|
||||
if (format == NULL)
|
||||
continue;
|
||||
|
||||
if (GST_VALUE_HOLDS_LIST (format)) {
|
||||
gint j, len;
|
||||
|
||||
len = gst_value_list_get_size (format);
|
||||
GST_DEBUG_OBJECT (base, "have %d formats", len);
|
||||
for (j = 0; j < len; j++) {
|
||||
const GValue *val;
|
||||
|
||||
val = gst_value_list_get_value (format, j);
|
||||
if (G_VALUE_HOLDS_STRING (val)) {
|
||||
score_value (base, in_info, val, &min_loss, &out_info);
|
||||
if (min_loss == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (G_VALUE_HOLDS_STRING (format)) {
|
||||
score_value (base, in_info, format, &min_loss, &out_info);
|
||||
}
|
||||
}
|
||||
if (out_info)
|
||||
gst_structure_set (outs, "format", G_TYPE_STRING,
|
||||
GST_VIDEO_FORMAT_INFO_NAME (out_info), NULL);
|
||||
}
|
||||
|
||||
|
||||
static GstCaps *
|
||||
gst_video_convert_fixate_caps (GstBaseTransform * trans,
|
||||
GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
|
||||
{
|
||||
GstCaps *result;
|
||||
|
||||
GST_DEBUG_OBJECT (trans, "fixating caps %" GST_PTR_FORMAT, othercaps);
|
||||
GST_DEBUG_OBJECT (trans, "trying to fixate othercaps %" GST_PTR_FORMAT
|
||||
" based on caps %" GST_PTR_FORMAT, othercaps, caps);
|
||||
|
||||
result = gst_caps_intersect (othercaps, caps);
|
||||
if (gst_caps_is_empty (result)) {
|
||||
|
@ -152,6 +288,11 @@ gst_video_convert_fixate_caps (GstBaseTransform * trans,
|
|||
gst_caps_unref (othercaps);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (trans, "now fixating %" GST_PTR_FORMAT, result);
|
||||
|
||||
result = gst_caps_make_writable (result);
|
||||
gst_video_convert_fixate_format (trans, caps, result);
|
||||
|
||||
/* fixate remaining fields */
|
||||
result = gst_caps_fixate (result);
|
||||
|
||||
|
|
Loading…
Reference in a new issue