summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWim Taymans <wtaymans@redhat.com>2014-10-18 13:29:50 +0200
committerWim Taymans <wtaymans@redhat.com>2014-10-24 11:28:00 +0200
commitc71e63efc3b59c1537510c34a632cf89a0738798 (patch)
tree1697561a8c652332fcea6328cc65174bdcef895d
parent98002b39a3a64773cdfc6a23c8671e7ef6790bdf (diff)
video-scaler: handle interlaced video
Make video-converter pass the interlaced flag to the scaler. Make 2 scalers for each field in interlaced video with a 1 pixel shift and merge them together. Run the scaler on laternate lines in interlaced mode.
-rw-r--r--gst-libs/gst/video/video-converter.c8
-rw-r--r--gst-libs/gst/video/video-scaler.c217
2 files changed, 154 insertions, 71 deletions
diff --git a/gst-libs/gst/video/video-converter.c b/gst-libs/gst/video/video-converter.c
index dad7e8bc5..697c39c7f 100644
--- a/gst-libs/gst/video/video-converter.c
+++ b/gst-libs/gst/video/video-converter.c
@@ -306,12 +306,16 @@ chain_hscale (GstVideoConverter * convert, GstLineCacheNeedLineFunc need_line)
static GstLineCacheNeedLineFunc
chain_vscale (GstVideoConverter * convert, GstLineCacheNeedLineFunc need_line)
{
+ GstVideoScalerFlags flags;
+
+ flags = GST_VIDEO_INFO_IS_INTERLACED (&convert->in_info) ?
+ GST_VIDEO_SCALER_FLAG_INTERLACED : 0;
+
convert->vscale_lines = gst_line_cache_new ();
gst_line_cache_set_need_line_func (convert->vscale_lines,
need_line, convert, NULL);
convert->v_scaler =
- gst_video_scaler_new (GST_VIDEO_SCALER_METHOD_BICUBIC,
- GST_VIDEO_SCALER_FLAG_NONE, 4,
+ gst_video_scaler_new (GST_VIDEO_SCALER_METHOD_BICUBIC, flags, 4,
(convert->in_height / (gdouble) convert->out_height) / 2 - 0.5,
convert->in_height, convert->out_height);
return (GstLineCacheNeedLineFunc) do_vscale_lines;
diff --git a/gst-libs/gst/video/video-scaler.c b/gst-libs/gst/video/video-scaler.c
index 9ffaf0c90..3cf895440 100644
--- a/gst-libs/gst/video/video-scaler.c
+++ b/gst-libs/gst/video/video-scaler.c
@@ -34,31 +34,32 @@ typedef void (*GstVideoScalerHFunc) (GstVideoScaler * scale,
gpointer src, gpointer dest, guint dest_offset, guint width);
typedef void (*GstVideoScalerVFunc) (GstVideoScaler * scale,
gpointer srcs[], gpointer dest, guint dest_offset, guint width);
+typedef struct _ScalerParams ScalerParams;
-struct _GstVideoScaler
+struct _ScalerParams
{
- GstVideoScalerMethod method;
- GstVideoScalerFlags flags;
- guint n_taps;
- gdouble shift;
gint in_size;
gint out_size;
+ gint n_taps;
+ gdouble shift;
+ gdouble *taps;
+ guint *offsets;
+ gdouble (*get_tap) (ScalerParams * scale, gint l, gint xi, gdouble x);
+ /* used by lanczos */
+ gdouble ex, fx, dx;
/* extra params */
gdouble envelope;
gdouble sharpness;
gdouble sharpen;
+};
- /* an array with one entry for each output. It contains the offset
- * of the first input pixel to use */
- guint *offsets;
- /* scale coefficients. It contains (out_size * n_taps) elements */
- gdouble *taps;
-
- /* used by lanczos */
- gdouble ex, fx, dx;
+struct _GstVideoScaler
+{
+ GstVideoScalerMethod method;
+ GstVideoScalerFlags flags;
- gdouble (*get_tap) (GstVideoScaler * scale, gint l, gint xi, gdouble x);
+ ScalerParams params;
/* cached integer coefficients */
gint16 *taps_s16;
@@ -82,13 +83,13 @@ envelope (double x)
}
static gdouble
-get_nearest_tap (GstVideoScaler * scale, gint l, gint xi, gdouble x)
+get_nearest_tap (ScalerParams * scale, gint l, gint xi, gdouble x)
{
return 1.0;
}
static gdouble
-get_linear_tap (GstVideoScaler * scale, gint l, gint xi, gdouble x)
+get_linear_tap (ScalerParams * scale, gint l, gint xi, gdouble x)
{
gdouble n_taps;
gdouble res, a;
@@ -120,7 +121,7 @@ bicubic (gdouble s)
}
static gdouble
-get_bicubic_tap (GstVideoScaler * scale, gint l, gint xi, gdouble x)
+get_bicubic_tap (ScalerParams * scale, gint l, gint xi, gdouble x)
{
gdouble a, res;
@@ -139,14 +140,14 @@ get_bicubic_tap (GstVideoScaler * scale, gint l, gint xi, gdouble x)
}
static gdouble
-get_sinc_tap (GstVideoScaler * scale, gint l, gint xi, gdouble x)
+get_sinc_tap (ScalerParams * scale, gint l, gint xi, gdouble x)
{
gint xl = xi + l;
return sinc (x - xl);
}
static gdouble
-get_lanczos_tap (GstVideoScaler * scale, gint l, gint xi, gdouble x)
+get_lanczos_tap (ScalerParams * scale, gint l, gint xi, gdouble x)
{
gint xl = xi + l;
gdouble env = envelope ((x - xl) * scale->ex);
@@ -154,7 +155,14 @@ get_lanczos_tap (GstVideoScaler * scale, gint l, gint xi, gdouble x)
}
static void
-scaler_calculate_taps (GstVideoScaler * scale)
+scaler_clear (ScalerParams * params)
+{
+ g_free (params->offsets);
+ g_free (params->taps);
+}
+
+static void
+scaler_calculate_taps (ScalerParams * scale)
{
gint j;
guint *offsets;
@@ -162,26 +170,16 @@ scaler_calculate_taps (GstVideoScaler * scale)
gint n_taps;
gint in_size, out_size;
gdouble offset;
- gdouble scale_inc;
gdouble corr;
in_size = scale->in_size;
out_size = scale->out_size;
- scale_inc = in_size / (gdouble) out_size;
n_taps = scale->n_taps;
tap_offs = (n_taps - 1) / 2;
corr = (n_taps == 1 ? 0.5 : 0.0);
- offset = scale_inc / 2 - 0.5;
-#if 0
- if (scale->shift == -0.5)
- offset = 0.0;
- else if (scale->shift == 0.0)
- offset = scale_inc / 2 - 0.5;
- else
-#endif
- offset = scale->shift;
+ offset = scale->shift;
scale->taps = g_malloc (sizeof (gdouble) * n_taps * out_size);
offsets = scale->offsets = g_malloc (sizeof (guint) * out_size);
@@ -242,6 +240,35 @@ scaler_calculate_taps (GstVideoScaler * scale)
}
}
+static void
+scaler_zip (ScalerParams * scale, const ScalerParams * p1,
+ const ScalerParams * p2)
+{
+ guint i, out_size, n_taps;
+ gdouble *taps;
+ guint *offsets;
+
+ g_return_if_fail (p1->n_taps == p2->n_taps);
+
+ out_size = p1->out_size + p2->out_size;
+ n_taps = p1->n_taps;
+
+ taps = scale->taps = g_malloc (sizeof (gdouble) * n_taps * out_size);
+ offsets = scale->offsets = g_malloc (sizeof (guint) * out_size);
+
+ for (i = 0; i < out_size; i++) {
+ guint idx = i / 2;
+ const ScalerParams *p;
+
+ p = (i & 1) ? p2 : p1;
+
+ offsets[i] = p->offsets[idx] * 2 + (i & 1);
+
+ memcpy (taps + i * n_taps, p->taps + idx * n_taps,
+ n_taps * sizeof (gdouble));
+ }
+}
+
/**
* gst_video_scaler_new:
* @format: a #GstVideoFormat
@@ -275,21 +302,21 @@ gst_video_scaler_new (GstVideoScalerMethod method, GstVideoScalerFlags flags,
switch (method) {
case GST_VIDEO_SCALER_METHOD_NEAREST:
- scale->get_tap = get_nearest_tap;
+ scale->params.get_tap = get_nearest_tap;
if (n_taps == 0)
n_taps = 1;
break;
case GST_VIDEO_SCALER_METHOD_LINEAR:
- scale->get_tap = get_linear_tap;
+ scale->params.get_tap = get_linear_tap;
if (n_taps == 0)
n_taps = 2;
break;
case GST_VIDEO_SCALER_METHOD_BICUBIC:
n_taps = 4;
- scale->get_tap = get_bicubic_tap;
+ scale->params.get_tap = get_bicubic_tap;
break;
case GST_VIDEO_SCALER_METHOD_SINC:
- scale->get_tap = get_sinc_tap;
+ scale->params.get_tap = get_sinc_tap;
if (n_taps == 0)
n_taps = 4;
break;
@@ -297,21 +324,21 @@ gst_video_scaler_new (GstVideoScalerMethod method, GstVideoScalerFlags flags,
{
gdouble scale_inc = in_size / (gdouble) out_size;
- scale->envelope = 2.0;
- scale->sharpness = 1.0;
- scale->sharpen = 0.0;
+ scale->params.envelope = 2.0;
+ scale->params.sharpness = 1.0;
+ scale->params.sharpen = 0.0;
if (scale_inc > 1.0) {
- scale->fx = (1.0 / scale_inc) * scale->sharpness;
+ scale->params.fx = (1.0 / scale_inc) * scale->params.sharpness;
} else {
- scale->fx = (1.0) * scale->sharpness;
+ scale->params.fx = (1.0) * scale->params.sharpness;
}
- scale->ex = scale->fx / scale->envelope;
- scale->dx = ceil (scale->envelope / scale->fx);
+ scale->params.ex = scale->params.fx / scale->params.envelope;
+ scale->params.dx = ceil (scale->params.envelope / scale->params.fx);
if (n_taps == 0)
- n_taps = 2 * scale->dx;
- scale->get_tap = get_lanczos_tap;
+ n_taps = 2 * scale->params.dx;
+ scale->params.get_tap = get_lanczos_tap;
break;
}
default:
@@ -320,12 +347,58 @@ gst_video_scaler_new (GstVideoScalerMethod method, GstVideoScalerFlags flags,
scale->method = method;
scale->flags = flags;
- scale->n_taps = n_taps;
- scale->shift = shift;
- scale->in_size = in_size;
- scale->out_size = out_size;
- scaler_calculate_taps (scale);
+ scale->params.in_size = in_size;
+ scale->params.out_size = out_size;
+ scale->params.n_taps = n_taps;
+ scale->params.shift = shift;
+
+ if (flags & GST_VIDEO_SCALER_FLAG_INTERLACED) {
+ ScalerParams tparams, bparams;
+
+ tparams = bparams = scale->params;
+
+ tparams.in_size = (in_size + 1) / 2;
+ tparams.out_size = (out_size + 1) / 2;
+ tparams.n_taps = n_taps;
+ tparams.shift = shift;
+ scaler_calculate_taps (&tparams);
+
+ bparams.in_size = in_size - tparams.in_size;
+ bparams.out_size = out_size - tparams.out_size;
+ bparams.n_taps = n_taps;
+ bparams.shift = shift - 1.0;
+ scaler_calculate_taps (&bparams);
+
+ scaler_zip (&scale->params, &tparams, &bparams);
+ scaler_clear (&tparams);
+ scaler_clear (&bparams);
+ } else {
+ scaler_calculate_taps (&scale->params);
+ }
+
+#if 0
+ {
+ gint i;
+
+ for (i = 0; i < out_size; i++) {
+ gint j, o;
+ gdouble sum;
+
+ o = scale->params.offsets[i];
+
+ printf ("%u: \t%d ", i, o);
+ sum = 0;
+ for (j = 0; j < scale->params.n_taps; j++) {
+ gdouble tap;
+ tap = scale->params.taps[i * scale->params.n_taps + j];
+ printf ("\t%f ", tap);
+ sum += tap;
+ }
+ printf ("\t: sum %f\n", sum);
+ }
+ }
+#endif
return scale;
}
@@ -341,8 +414,7 @@ gst_video_scaler_free (GstVideoScaler * scale)
{
g_return_if_fail (scale != NULL);
- g_free (scale->offsets);
- g_free (scale->taps);
+ scaler_clear (&scale->params);
g_free (scale->taps_s16);
g_slice_free (GstVideoScaler, scale);
}
@@ -366,16 +438,18 @@ gst_video_scaler_get_coeff (GstVideoScaler * scale,
guint offset;
g_return_val_if_fail (scale != NULL, NULL);
- g_return_val_if_fail (out_offset < scale->out_size, NULL);
+ g_return_val_if_fail (out_offset < scale->params.out_size, NULL);
- offset = scale->offsets[out_offset];
+ offset = scale->params.offsets[out_offset];
if (in_offset)
*in_offset = offset;
- if (n_taps)
- *n_taps = scale->n_taps;
-
- return scale->taps + out_offset * scale->n_taps;
+ if (n_taps) {
+ *n_taps = scale->params.n_taps;
+ if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED)
+ *n_taps *= 2;
+ }
+ return scale->params.taps + out_offset * scale->params.n_taps;
}
/**
@@ -459,10 +533,10 @@ make_s16_taps (GstVideoScaler * scale)
gint16 *taps_s16;
gdouble *taps;
- out_size = scale->out_size;
- n_taps = scale->n_taps;
+ out_size = scale->params.out_size;
+ n_taps = scale->params.n_taps;
- taps = scale->taps;
+ taps = scale->params.taps;
taps_s16 = scale->taps_s16 = g_malloc (sizeof (gint16) * out_size * n_taps);
for (i = 0; i < out_size; i++) {
@@ -481,7 +555,7 @@ video_scale_h_near_8888 (GstVideoScaler * scale,
guint32 *s, *d;
guint *offsets;
- offsets = scale->offsets + dest_offset;
+ offsets = scale->params.offsets + dest_offset;
d = (guint32 *) dest + dest_offset;
s = (guint32 *) src;
@@ -509,8 +583,8 @@ video_scale_h_ntap_8888 (GstVideoScaler * scale,
if (scale->taps_s16 == NULL)
make_s16_taps (scale);
- n_taps = scale->n_taps;
- offsets = scale->offsets + dest_offset;
+ n_taps = scale->params.n_taps;
+ offsets = scale->params.offsets + dest_offset;
taps = scale->taps_s16 + (dest_offset * n_taps);
d = (guint8 *) dest + 4 * dest_offset;
@@ -542,20 +616,25 @@ video_scale_v_ntap_8888 (GstVideoScaler * scale,
gpointer srcs[], gpointer dest, guint dest_offset, guint width)
{
gint16 *t;
- gint i, j, n_taps, sum0, sum1, sum2, sum3;
+ gint i, j, k, n_taps, sum0, sum1, sum2, sum3, src_inc;
guint8 *s, *d;
if (scale->taps_s16 == NULL)
make_s16_taps (scale);
- n_taps = scale->n_taps;
+ n_taps = scale->params.n_taps;
t = scale->taps_s16 + (dest_offset * n_taps);
d = (guint8 *) dest;
+ if (scale->flags & GST_VIDEO_SCALER_FLAG_INTERLACED)
+ src_inc = 2;
+ else
+ src_inc = 1;
+
for (i = 0; i < width; i++) {
sum0 = sum1 = sum2 = sum3 = 0;
- for (j = 0; j < n_taps; j++) {
- s = (guint8 *) (srcs[j]);
+ for (j = 0, k = 0; j < n_taps; j++, k += src_inc) {
+ s = (guint8 *) (srcs[k]);
sum0 += t[j] * s[4 * i + 0];
sum1 += t[j] * s[4 * i + 1];
@@ -596,7 +675,7 @@ gst_video_scaler_horizontal (GstVideoScaler * scale, GstVideoFormat format,
g_return_if_fail (scale != NULL);
g_return_if_fail (src != NULL);
g_return_if_fail (dest != NULL);
- g_return_if_fail (dest_offset + width <= scale->out_size);
+ g_return_if_fail (dest_offset + width <= scale->params.out_size);
switch (scale->method) {
case GST_VIDEO_SCALER_METHOD_NEAREST:
@@ -639,7 +718,7 @@ gst_video_scaler_vertical (GstVideoScaler * scale, GstVideoFormat format,
g_return_if_fail (scale != NULL);
g_return_if_fail (srcs != NULL);
g_return_if_fail (dest != NULL);
- g_return_if_fail (dest_offset <= scale->out_size);
+ g_return_if_fail (dest_offset <= scale->params.out_size);
switch (scale->method) {
case GST_VIDEO_SCALER_METHOD_NEAREST: