diff options
author | Wim Taymans <wtaymans@redhat.com> | 2014-10-18 13:29:50 +0200 |
---|---|---|
committer | Wim Taymans <wtaymans@redhat.com> | 2014-10-24 11:28:00 +0200 |
commit | c71e63efc3b59c1537510c34a632cf89a0738798 (patch) | |
tree | 1697561a8c652332fcea6328cc65174bdcef895d | |
parent | 98002b39a3a64773cdfc6a23c8671e7ef6790bdf (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.c | 8 | ||||
-rw-r--r-- | gst-libs/gst/video/video-scaler.c | 217 |
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: |