[vlc-devel] [PATCH 11/11] video_filter:deinterlace: move some structures in common.h
Steve Lhomme
robux4 at videolabs.io
Tue Jun 27 10:21:47 CEST 2017
So they can be reused by other deinterlacing implementations
---
modules/video_filter/Makefile.am | 3 +-
modules/video_filter/deinterlace/algo_ivtc.c | 34 +--
modules/video_filter/deinterlace/algo_phosphor.c | 4 +-
modules/video_filter/deinterlace/algo_yadif.c | 10 +-
modules/video_filter/deinterlace/common.c | 317 +++++++++++++++++++++
modules/video_filter/deinterlace/common.h | 62 ++++-
modules/video_filter/deinterlace/deinterlace.c | 336 +++--------------------
modules/video_filter/deinterlace/deinterlace.h | 37 +--
8 files changed, 445 insertions(+), 358 deletions(-)
create mode 100644 modules/video_filter/deinterlace/common.c
diff --git a/modules/video_filter/Makefile.am b/modules/video_filter/Makefile.am
index 8fcc93c4db..a46eb3ce89 100644
--- a/modules/video_filter/Makefile.am
+++ b/modules/video_filter/Makefile.am
@@ -109,7 +109,8 @@ video_filter_LTLIBRARIES = \
libdeinterlace_plugin_la_SOURCES = \
video_filter/deinterlace/deinterlace.c video_filter/deinterlace/deinterlace.h \
- video_filter/deinterlace/mmx.h video_filter/deinterlace/common.h \
+ video_filter/deinterlace/mmx.h \
+ video_filter/deinterlace/common.c video_filter/deinterlace/common.h \
video_filter/deinterlace/merge.c video_filter/deinterlace/merge.h \
video_filter/deinterlace/helpers.c video_filter/deinterlace/helpers.h \
video_filter/deinterlace/algo_basic.c video_filter/deinterlace/algo_basic.h \
diff --git a/modules/video_filter/deinterlace/algo_ivtc.c b/modules/video_filter/deinterlace/algo_ivtc.c
index 5603828f0e..2fb378aecb 100644
--- a/modules/video_filter/deinterlace/algo_ivtc.c
+++ b/modules/video_filter/deinterlace/algo_ivtc.c
@@ -406,8 +406,8 @@ static void IVTCLowLevelDetect( filter_t *p_filter )
filter_sys_t *p_sys = p_filter->p_sys;
ivtc_sys_t *p_ivtc = &p_sys->ivtc;
- picture_t *p_curr = p_sys->pp_history[1];
- picture_t *p_next = p_sys->pp_history[2];
+ picture_t *p_curr = p_sys->context.pp_history[1];
+ picture_t *p_next = p_sys->context.pp_history[2];
assert( p_next != NULL );
assert( p_curr != NULL );
@@ -458,7 +458,7 @@ static void IVTCCadenceDetectAlgoScores( filter_t *p_filter )
filter_sys_t *p_sys = p_filter->p_sys;
ivtc_sys_t *p_ivtc = &p_sys->ivtc;
- picture_t *p_next = p_sys->pp_history[2];
+ picture_t *p_next = p_sys->context.pp_history[2];
assert( p_next != NULL );
@@ -632,7 +632,7 @@ static void IVTCCadenceDetectAlgoVektor( filter_t *p_filter )
filter_sys_t *p_sys = p_filter->p_sys;
ivtc_sys_t *p_ivtc = &p_sys->ivtc;
- picture_t *p_next = p_sys->pp_history[2];
+ picture_t *p_next = p_sys->context.pp_history[2];
assert( p_next != NULL );
@@ -860,9 +860,9 @@ static void IVTCSoftTelecineDetect( filter_t *p_filter )
filter_sys_t *p_sys = p_filter->p_sys;
ivtc_sys_t *p_ivtc = &p_sys->ivtc;
- picture_t *p_prev = p_sys->pp_history[0];
- picture_t *p_curr = p_sys->pp_history[1];
- picture_t *p_next = p_sys->pp_history[2];
+ picture_t *p_prev = p_sys->context.pp_history[0];
+ picture_t *p_curr = p_sys->context.pp_history[1];
+ picture_t *p_next = p_sys->context.pp_history[2];
assert( p_next != NULL );
assert( p_curr != NULL );
@@ -977,9 +977,9 @@ static void IVTCCadenceAnalyze( filter_t *p_filter )
filter_sys_t *p_sys = p_filter->p_sys;
ivtc_sys_t *p_ivtc = &p_sys->ivtc;
- picture_t *p_prev = p_sys->pp_history[0];
- picture_t *p_curr = p_sys->pp_history[1];
- picture_t *p_next = p_sys->pp_history[2];
+ picture_t *p_prev = p_sys->context.pp_history[0];
+ picture_t *p_curr = p_sys->context.pp_history[1];
+ picture_t *p_next = p_sys->context.pp_history[2];
assert( p_next != NULL );
assert( p_curr != NULL );
@@ -1216,8 +1216,8 @@ static bool IVTCOutputOrDropFrame( filter_t *p_filter, picture_t *p_dst )
ivtc_sys_t *p_ivtc = &p_sys->ivtc;
mtime_t t_final = VLC_TS_INVALID; /* for custom timestamp mangling */
- picture_t *p_curr = p_sys->pp_history[1];
- picture_t *p_next = p_sys->pp_history[2];
+ picture_t *p_curr = p_sys->context.pp_history[1];
+ picture_t *p_next = p_sys->context.pp_history[2];
assert( p_next != NULL );
assert( p_curr != NULL );
@@ -1479,9 +1479,9 @@ int RenderIVTC( filter_t *p_filter, picture_t *p_dst, picture_t *p_pic,
filter_sys_t *p_sys = p_filter->p_sys;
ivtc_sys_t *p_ivtc = &p_sys->ivtc;
- picture_t *p_prev = p_sys->pp_history[0];
- picture_t *p_curr = p_sys->pp_history[1];
- picture_t *p_next = p_sys->pp_history[2];
+ picture_t *p_prev = p_sys->context.pp_history[0];
+ picture_t *p_curr = p_sys->context.pp_history[1];
+ picture_t *p_next = p_sys->context.pp_history[2];
/* If the history mechanism has failed, we have nothing to do. */
if( !p_next )
@@ -1523,7 +1523,7 @@ int RenderIVTC( filter_t *p_filter, picture_t *p_dst, picture_t *p_pic,
bool b_have_output_frame = IVTCOutputOrDropFrame( p_filter, p_dst );
/* The next frame will get a custom timestamp, too. */
- p_sys->i_frame_offset = CUSTOM_PTS;
+ p_sys->context.i_frame_offset = CUSTOM_PTS;
if( b_have_output_frame )
return VLC_SUCCESS;
@@ -1572,7 +1572,7 @@ int RenderIVTC( filter_t *p_filter, picture_t *p_dst, picture_t *p_pic,
/* At the next frame, the filter starts. The next frame will get
a custom timestamp. */
- p_sys->i_frame_offset = CUSTOM_PTS;
+ p_sys->context.i_frame_offset = CUSTOM_PTS;
picture_Copy( p_dst, p_next );
return VLC_SUCCESS;
diff --git a/modules/video_filter/deinterlace/algo_phosphor.c b/modules/video_filter/deinterlace/algo_phosphor.c
index cc90ec13a6..cd954c631d 100644
--- a/modules/video_filter/deinterlace/algo_phosphor.c
+++ b/modules/video_filter/deinterlace/algo_phosphor.c
@@ -290,8 +290,8 @@ int RenderPhosphor( filter_t *p_filter,
filter_sys_t *p_sys = p_filter->p_sys;
/* Last two input frames */
- picture_t *p_in = p_sys->pp_history[HISTORY_SIZE-1];
- picture_t *p_old = p_sys->pp_history[HISTORY_SIZE-2];
+ picture_t *p_in = p_sys->context.pp_history[HISTORY_SIZE-1];
+ picture_t *p_old = p_sys->context.pp_history[HISTORY_SIZE-2];
/* Use the same input picture as "old" at the first frame after startup */
if( !p_old )
diff --git a/modules/video_filter/deinterlace/algo_yadif.c b/modules/video_filter/deinterlace/algo_yadif.c
index b326a17736..a3ccb1108b 100644
--- a/modules/video_filter/deinterlace/algo_yadif.c
+++ b/modules/video_filter/deinterlace/algo_yadif.c
@@ -59,9 +59,9 @@ int RenderYadif( filter_t *p_filter, picture_t *p_dst, picture_t *p_src,
assert( i_field == 0 || i_field == 1 );
/* As the pitches must match, use ONLY pictures coming from picture_New()! */
- picture_t *p_prev = p_sys->pp_history[0];
- picture_t *p_cur = p_sys->pp_history[1];
- picture_t *p_next = p_sys->pp_history[2];
+ picture_t *p_prev = p_sys->context.pp_history[0];
+ picture_t *p_cur = p_sys->context.pp_history[1];
+ picture_t *p_next = p_sys->context.pp_history[2];
/* Account for soft field repeat.
@@ -172,7 +172,7 @@ int RenderYadif( filter_t *p_filter, picture_t *p_dst, picture_t *p_src,
}
}
- p_sys->i_frame_offset = 1; /* p_cur will be rendered at next frame, too */
+ p_sys->context.i_frame_offset = 1; /* p_cur will be rendered at next frame, too */
return VLC_SUCCESS;
}
@@ -187,7 +187,7 @@ int RenderYadif( filter_t *p_filter, picture_t *p_dst, picture_t *p_src,
}
else
{
- p_sys->i_frame_offset = 1; /* p_cur will be rendered at next frame */
+ p_sys->context.i_frame_offset = 1; /* p_cur will be rendered at next frame */
return VLC_EGENERIC;
}
diff --git a/modules/video_filter/deinterlace/common.c b/modules/video_filter/deinterlace/common.c
new file mode 100644
index 0000000000..e36cb1d08f
--- /dev/null
+++ b/modules/video_filter/deinterlace/common.c
@@ -0,0 +1,317 @@
+/*****************************************************************************
+ * common.c : Common helper function for the VLC deinterlacer
+ *****************************************************************************
+ * Copyright (C) 2000-2017 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Author: Sam Hocevar <sam at zoy.org>
+ * Christophe Massiot <massiot at via.ecp.fr>
+ * Laurent Aimar <fenrir at videolan.org>
+ * Juha Jeronen <juha.jeronen at jyu.fi>
+ * Steve Lhomme <robux4 at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_picture.h>
+
+#include "common.h"
+
+void FlushDeinterlacing(struct deinterlace_ctx *p_context)
+{
+ p_context->meta[0].pi_date = VLC_TS_INVALID;
+ p_context->meta[0].pi_nb_fields = 2;
+ p_context->meta[0].pb_top_field_first = true;
+ for( int i = 1; i < METADATA_SIZE; i++ )
+ p_context->meta[i] = p_context->meta[i-1];
+
+ p_context->i_frame_offset = 0; /* reset to default value (first frame after
+ flush cannot have offset) */
+ for( int i = 0; i < HISTORY_SIZE; i++ )
+ {
+ if( p_context->pp_history[i] )
+ picture_Release( p_context->pp_history[i] );
+ p_context->pp_history[i] = NULL;
+ }
+}
+
+mtime_t GetFieldDuration(const struct deinterlace_ctx *p_context,
+ const video_format_t *fmt, picture_t *p_pic )
+{
+ mtime_t i_field_dur = 0;
+
+ /* Calculate one field duration. */
+ int i = 0;
+ int iend = METADATA_SIZE-1;
+ /* Find oldest valid logged date.
+ The current input frame doesn't count. */
+ for( ; i < iend; i++ )
+ if( p_context->meta[i].pi_date > VLC_TS_INVALID )
+ break;
+ if( i < iend )
+ {
+ /* Count how many fields the valid history entries
+ (except the new frame) represent. */
+ int i_fields_total = 0;
+ for( int j = i ; j < iend; j++ )
+ i_fields_total += p_context->meta[j].pi_nb_fields;
+ /* One field took this long. */
+ i_field_dur = (p_pic->date - p_context->meta[i].pi_date) / i_fields_total;
+ }
+ else if (fmt->i_frame_rate_base)
+ i_field_dur = 10000 * fmt->i_frame_rate / fmt->i_frame_rate_base;
+
+ /* Note that we default to field duration 0 if it could not be
+ determined. This behaves the same as the old code - leaving the
+ extra output frame dates the same as p_pic->date if the last cached
+ date was not valid.
+ */
+ return i_field_dur;
+}
+
+void GetDeinterlacingOutput( const struct deinterlace_ctx *p_context,
+ video_format_t *p_dst, const video_format_t *p_src )
+{
+ *p_dst = *p_src;
+
+ if( p_context->b_half_height )
+ {
+ p_dst->i_height /= 2;
+ p_dst->i_visible_height /= 2;
+ p_dst->i_y_offset /= 2;
+ p_dst->i_sar_den *= 2;
+ }
+
+ if( p_context->b_double_rate )
+ {
+ p_dst->i_frame_rate *= 2;
+ }
+}
+
+picture_t *DoDeinterlacing( filter_t *p_filter,
+ struct deinterlace_ctx *p_context, picture_t *p_pic )
+{
+ picture_t *p_dst[DEINTERLACE_DST_SIZE];
+ int i_double_rate_alloc_end;
+ /* Remember the frame offset that we should use for this frame.
+ The value in p_sys will be updated to reflect the correct value
+ for the *next* frame when we call the renderer. */
+ int i_frame_offset;
+ int i_meta_idx;
+
+ bool b_top_field_first;
+
+ /* Request output picture */
+ p_dst[0] = filter_NewPicture( p_filter );
+ if( p_dst[0] == NULL )
+ {
+ picture_Release( p_pic );
+ return NULL;
+ }
+ picture_CopyProperties( p_dst[0], p_pic );
+
+ /* Any unused p_dst pointers must be NULL, because they are used to
+ check how many output frames we have. */
+ for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i )
+ p_dst[i] = NULL;
+
+ /* Update the input frame history, if the currently active algorithm
+ needs it. */
+ if( p_context->b_use_frame_history )
+ {
+ /* Keep reference for the picture */
+ picture_t *p_dup = picture_Hold( p_pic );
+
+ /* Slide the history */
+ if( p_context->pp_history[0] )
+ picture_Release( p_context->pp_history[0] );
+ for( int i = 1; i < HISTORY_SIZE; i++ )
+ p_context->pp_history[i-1] = p_context->pp_history[i];
+ p_context->pp_history[HISTORY_SIZE-1] = p_dup;
+ }
+
+ /* Slide the metadata history. */
+ for( int i = 1; i < METADATA_SIZE; i++ )
+ p_context->meta[i-1] = p_context->meta[i];
+ /* The last element corresponds to the current input frame. */
+ p_context->meta[METADATA_SIZE-1].pi_date = p_pic->date;
+ p_context->meta[METADATA_SIZE-1].pi_nb_fields = p_pic->i_nb_fields;
+ p_context->meta[METADATA_SIZE-1].pb_top_field_first = p_pic->b_top_field_first;
+
+ /* Remember the frame offset that we should use for this frame.
+ The value in p_sys will be updated to reflect the correct value
+ for the *next* frame when we call the renderer. */
+ i_frame_offset = p_context->i_frame_offset;
+ i_meta_idx = (METADATA_SIZE-1) - i_frame_offset;
+
+ int i_nb_fields;
+
+ /* These correspond to the current *outgoing* frame. */
+ if( i_frame_offset != CUSTOM_PTS )
+ {
+ /* Pick the correct values from the history. */
+ b_top_field_first = p_context->meta[i_meta_idx].pb_top_field_first;
+ i_nb_fields = p_context->meta[i_meta_idx].pi_nb_fields;
+ }
+ else
+ {
+ /* Framerate doublers must not request CUSTOM_PTS, as they need the
+ original field timings, and need Deinterlace() to allocate the
+ correct number of output frames. */
+ assert( !p_context->b_double_rate );
+
+ /* NOTE: i_nb_fields is only used for framerate doublers, so it is
+ unused in this case. b_top_field_first is only passed to the
+ algorithm. We assume that algorithms that request CUSTOM_PTS
+ will, if necessary, extract the TFF/BFF information themselves.
+ */
+ b_top_field_first = p_pic->b_top_field_first; /* this is not guaranteed
+ to be meaningful */
+ i_nb_fields = p_pic->i_nb_fields; /* unused */
+ }
+
+ /* For framerate doublers, determine field duration and allocate
+ output frames. */
+ i_double_rate_alloc_end = 0; /* One past last for allocated output
+ frames in p_dst[]. Used only for
+ framerate doublers. Will be inited
+ below. Declared here because the
+ PTS logic needs the result. */
+ if( p_context->b_double_rate )
+ {
+ i_double_rate_alloc_end = i_nb_fields;
+ if( i_nb_fields > DEINTERLACE_DST_SIZE )
+ {
+ /* Note that the effective buffer size depends also on the constant
+ private_picture in vout_wrapper.c, since that determines the
+ maximum number of output pictures filter_NewPicture() will
+ successfully allocate for one input frame.
+ */
+ msg_Err( p_filter, "Framerate doubler: output buffer too small; "\
+ "fields = %d, buffer size = %d. Dropping the "\
+ "remaining fields.",
+ i_nb_fields, DEINTERLACE_DST_SIZE );
+ i_double_rate_alloc_end = DEINTERLACE_DST_SIZE;
+ }
+
+ /* Allocate output frames. */
+ for( int i = 1; i < i_double_rate_alloc_end ; ++i )
+ {
+ p_dst[i-1]->p_next =
+ p_dst[i] = filter_NewPicture( p_filter );
+ if( p_dst[i] )
+ {
+ picture_CopyProperties( p_dst[i], p_pic );
+ }
+ else
+ {
+ msg_Err( p_filter, "Framerate doubler: could not allocate "\
+ "output frame %d", i+1 );
+ i_double_rate_alloc_end = i; /* Inform the PTS logic about the
+ correct end position. */
+ break; /* If this happens, the rest of the allocations
+ aren't likely to work, either... */
+ }
+ }
+ /* Now we have allocated *up to* the correct number of frames;
+ normally, exactly the correct number. Upon alloc failure,
+ we may have succeeded in allocating *some* output frames,
+ but fewer than were desired. In such a case, as many will
+ be rendered as were successfully allocated.
+
+ Note that now p_dst[i] != NULL
+ for 0 <= i < i_double_rate_alloc_end. */
+ }
+ assert( p_context->b_double_rate || p_dst[1] == NULL );
+ assert( i_nb_fields > 2 || p_dst[2] == NULL );
+
+ /* Render */
+ if ( p_context->b_single_field )
+ {
+ if ( p_context->pf_render_pic( p_filter, p_dst[0], p_pic,
+ 0, 0 ) )
+ goto drop;
+ }
+ else
+ {
+ /* Note: RenderIVTC will automatically drop the duplicate frames
+ produced by IVTC. This is part of normal operation. */
+ if ( p_context->pf_render_pic( p_filter, p_dst[0], p_pic,
+ 0, !b_top_field_first ) )
+ goto drop;
+ if ( p_dst[1] )
+ p_context->pf_render_pic( p_filter, p_dst[1], p_pic,
+ 1, !b_top_field_first );
+ if ( p_dst[2] )
+ p_context->pf_render_pic( p_filter, p_dst[1], p_pic,
+ 2, !b_top_field_first );
+ }
+
+ /* Set output timestamps, if the algorithm didn't request CUSTOM_PTS
+ for this frame. */
+ assert( i_frame_offset <= METADATA_SIZE ||
+ i_frame_offset == CUSTOM_PTS );
+ if( i_frame_offset != CUSTOM_PTS )
+ {
+ mtime_t i_base_pts = p_context->meta[i_meta_idx].pi_date;
+
+ /* Note: in the usual case (i_frame_offset = 0 and
+ b_double_rate = false), this effectively does nothing.
+ This is needed to correct the timestamp
+ when i_frame_offset > 0. */
+ p_dst[0]->date = i_base_pts;
+
+ if( p_context->b_double_rate )
+ {
+ mtime_t i_field_dur = GetFieldDuration( p_context, &p_pic->format, p_pic );
+ /* Processing all actually allocated output frames. */
+ for( int i = 1; i < i_double_rate_alloc_end; ++i )
+ {
+ /* XXX it's not really good especially for the first picture, but
+ * I don't think that delaying by one frame is worth it */
+ if( i_base_pts > VLC_TS_INVALID )
+ p_dst[i]->date = i_base_pts + i * i_field_dur;
+ else
+ p_dst[i]->date = VLC_TS_INVALID;
+ }
+ }
+ }
+
+ for( int i = 0; i < DEINTERLACE_DST_SIZE; ++i )
+ {
+ if( p_dst[i] )
+ {
+ p_dst[i]->b_progressive = true;
+ p_dst[i]->i_nb_fields = 2;
+ }
+ }
+
+ picture_Release( p_pic );
+ return p_dst[0];
+
+drop:
+ picture_Release( p_dst[0] );
+ for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i )
+ {
+ if( p_dst[i] )
+ picture_Release( p_dst[i] );
+ }
+ picture_Release( p_pic );
+ return NULL;
+}
diff --git a/modules/video_filter/deinterlace/common.h b/modules/video_filter/deinterlace/common.h
index dcd7e63c73..739398cdb9 100644
--- a/modules/video_filter/deinterlace/common.h
+++ b/modules/video_filter/deinterlace/common.h
@@ -1,10 +1,11 @@
/*****************************************************************************
* common.h : Common macros for the VLC deinterlacer
*****************************************************************************
- * Copyright (C) 2000-2011 VLC authors and VideoLAN
+ * Copyright (C) 2000-2017 VLC authors and VideoLAN
* $Id$
*
* Author: Sam Hocevar <sam at zoy.org>
+ * Steve Lhomme <robux4 at gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
@@ -24,6 +25,11 @@
#ifndef VLC_DEINTERLACE_COMMON_H
#define VLC_DEINTERLACE_COMMON_H 1
+#include <vlc_common.h>
+#include <vlc_filter.h>
+
+#include <assert.h>
+
/**
* \file
* Common macros for the VLC deinterlacer.
@@ -35,4 +41,58 @@
#define FFMIN(a,b) __MIN(a,b)
#define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c)
+/**
+ * Metadata history structure, used for framerate doublers.
+ * This is used for computing field duration in Deinterlace().
+ * @see Deinterlace()
+ */
+typedef struct {
+ mtime_t pi_date;
+ int pi_nb_fields;
+ bool pb_top_field_first;
+} metadata_history_t;
+
+#define METADATA_SIZE (3)
+#define HISTORY_SIZE (3)
+#define CUSTOM_PTS -1
+
+struct deinterlace_ctx
+{
+ /* Algorithm behaviour flags */
+ bool b_double_rate; /**< Shall we double the framerate? */
+ bool b_half_height; /**< Shall be divide the height by 2 */
+ bool b_use_frame_history; /**< Use the input frame history buffer? */
+ bool b_single_field; /**< The filter doesn't handle the field info */
+
+ /**
+ * Metadata history (PTS, nb_fields, TFF). Used for framerate doublers.
+ * @see metadata_history_t
+ */
+ metadata_history_t meta[METADATA_SIZE];
+
+ /** Output frame timing / framerate doubler control
+ (see extra documentation in deinterlace.h) */
+ int i_frame_offset;
+
+ /** Input frame history buffer for algorithms with temporal filtering. */
+ picture_t *pp_history[HISTORY_SIZE];
+
+ int (*pf_render_pic)(filter_t *p_filter, picture_t *p_dst, picture_t *p_pic,
+ int order, int i_field);
+};
+
+#define DEINTERLACE_DST_SIZE 3
+
+mtime_t GetFieldDuration( const struct deinterlace_ctx *,
+ const video_format_t *fmt, picture_t *p_pic );
+
+void GetDeinterlacingOutput( const struct deinterlace_ctx *,
+ video_format_t *p_dst, const video_format_t *p_src );
+
+picture_t *DoDeinterlacing( filter_t *,
+ struct deinterlace_ctx *, picture_t * );
+
+void FlushDeinterlacing( struct deinterlace_ctx * );
+
+
#endif
diff --git a/modules/video_filter/deinterlace/deinterlace.c b/modules/video_filter/deinterlace/deinterlace.c
index 8e5e20b8b7..80698836ac 100644
--- a/modules/video_filter/deinterlace/deinterlace.c
+++ b/modules/video_filter/deinterlace/deinterlace.c
@@ -151,37 +151,37 @@ static void SetFilterMethod( filter_t *p_filter, const char *mode, bool pack )
if ( mode == NULL || !strcmp( mode, "auto" ) )
mode = "x";
- p_sys->b_double_rate = false;
- p_sys->b_half_height = false;
- p_sys->b_use_frame_history = false;
- p_sys->b_single_field = false;
+ p_sys->context.b_double_rate = false;
+ p_sys->context.b_half_height = false;
+ p_sys->context.b_use_frame_history = false;
+ p_sys->context.b_single_field = false;
if( !strcmp( mode, "discard" ) )
{
- p_sys->pf_render_pic = RenderDiscard;
- p_sys->b_single_field = true;
- p_sys->b_half_height = true;
+ p_sys->context.pf_render_pic = RenderDiscard;
+ p_sys->context.b_single_field = true;
+ p_sys->context.b_half_height = true;
}
else if( !strcmp( mode, "bob" ) || !strcmp( mode, "progressive-scan" ) )
{
- p_sys->pf_render_pic = RenderBob;
- p_sys->b_double_rate = true;
+ p_sys->context.pf_render_pic = RenderBob;
+ p_sys->context.b_double_rate = true;
}
else if( !strcmp( mode, "linear" ) )
{
- p_sys->pf_render_pic = RenderLinear;
- p_sys->b_double_rate = true;
+ p_sys->context.pf_render_pic = RenderLinear;
+ p_sys->context.b_double_rate = true;
}
else if( !strcmp( mode, "mean" ) )
{
- p_sys->pf_render_pic = RenderMean;
- p_sys->b_single_field = true;
- p_sys->b_half_height = true;
+ p_sys->context.pf_render_pic = RenderMean;
+ p_sys->context.b_single_field = true;
+ p_sys->context.b_half_height = true;
}
else if( !strcmp( mode, "blend" ) )
{
- p_sys->pf_render_pic = RenderBlend;
- p_sys->b_single_field = true;
+ p_sys->context.pf_render_pic = RenderBlend;
+ p_sys->context.b_single_field = true;
}
else if( pack )
{
@@ -191,15 +191,15 @@ static void SetFilterMethod( filter_t *p_filter, const char *mode, bool pack )
}
else if( !strcmp( mode, "yadif" ) )
{
- p_sys->pf_render_pic = RenderYadif;
- p_sys->b_single_field = true;
- p_sys->b_use_frame_history = true;
+ p_sys->context.pf_render_pic = RenderYadif;
+ p_sys->context.b_single_field = true;
+ p_sys->context.b_use_frame_history = true;
}
else if( !strcmp( mode, "yadif2x" ) )
{
- p_sys->pf_render_pic = RenderYadif;
- p_sys->b_double_rate = true;
- p_sys->b_use_frame_history = true;
+ p_sys->context.pf_render_pic = RenderYadif;
+ p_sys->context.b_double_rate = true;
+ p_sys->context.b_use_frame_history = true;
}
else if( p_sys->chroma->pixel_size > 1 )
{
@@ -209,20 +209,20 @@ static void SetFilterMethod( filter_t *p_filter, const char *mode, bool pack )
}
else if( !strcmp( mode, "x" ) )
{
- p_sys->pf_render_pic = RenderX;
- p_sys->b_single_field = true;
+ p_sys->context.pf_render_pic = RenderX;
+ p_sys->context.b_single_field = true;
}
else if( !strcmp( mode, "phosphor" ) )
{
- p_sys->pf_render_pic = RenderPhosphor;
- p_sys->b_double_rate = true;
- p_sys->b_use_frame_history = true;
+ p_sys->context.pf_render_pic = RenderPhosphor;
+ p_sys->context.b_double_rate = true;
+ p_sys->context.b_use_frame_history = true;
}
else if( !strcmp( mode, "ivtc" ) )
{
- p_sys->pf_render_pic = RenderIVTC;
- p_sys->b_single_field = true;
- p_sys->b_use_frame_history = true;
+ p_sys->context.pf_render_pic = RenderIVTC;
+ p_sys->context.b_single_field = true;
+ p_sys->context.b_use_frame_history = true;
}
else
msg_Err( p_filter, "unknown deinterlace mode \"%s\"", mode );
@@ -245,261 +245,17 @@ static void SetFilterMethod( filter_t *p_filter, const char *mode, bool pack )
static void GetOutputFormat( filter_t *p_filter,
video_format_t *p_dst, const video_format_t *p_src )
{
- filter_sys_t *p_sys = p_filter->p_sys;
- *p_dst = *p_src;
-
- if( p_sys->b_half_height )
- {
- p_dst->i_height /= 2;
- p_dst->i_visible_height /= 2;
- p_dst->i_y_offset /= 2;
- p_dst->i_sar_den *= 2;
- }
-
- if( p_sys->b_double_rate )
- {
- p_dst->i_frame_rate *= 2;
- }
+ GetDeinterlacingOutput(&p_filter->p_sys->context, p_dst, p_src);
}
/*****************************************************************************
* video filter functions
*****************************************************************************/
-#define DEINTERLACE_DST_SIZE 3
-
-static mtime_t GetFieldDuration( metadata_history_t meta[static METADATA_SIZE],
- picture_t *p_pic )
-{
- mtime_t i_field_dur = 0;
-
- /* Calculate one field duration. */
- int i = 0;
- int iend = METADATA_SIZE-1;
- /* Find oldest valid logged date.
- The current input frame doesn't count. */
- for( ; i < iend; i++ )
- if( meta[i].pi_date > VLC_TS_INVALID )
- break;
- if( i < iend )
- {
- /* Count how many fields the valid history entries
- (except the new frame) represent. */
- int i_fields_total = 0;
- for( int j = i ; j < iend; j++ )
- i_fields_total += meta[j].pi_nb_fields;
- /* One field took this long. */
- i_field_dur = (p_pic->date - meta[i].pi_date) / i_fields_total;
- }
- /* Note that we default to field duration 0 if it could not be
- determined. This behaves the same as the old code - leaving the
- extra output frame dates the same as p_pic->date if the last cached
- date was not valid.
- */
- return i_field_dur;
-}
-
/* This is the filter function. See Open(). */
picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
{
- filter_sys_t *p_sys = p_filter->p_sys;
- picture_t *p_dst[DEINTERLACE_DST_SIZE];
-
- /* Request output picture */
- p_dst[0] = filter_NewPicture( p_filter );
- if( p_dst[0] == NULL )
- {
- picture_Release( p_pic );
- return NULL;
- }
- picture_CopyProperties( p_dst[0], p_pic );
-
- /* Any unused p_dst pointers must be NULL, because they are used to
- check how many output frames we have. */
- for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i )
- p_dst[i] = NULL;
-
- /* Update the input frame history, if the currently active algorithm
- needs it. */
- if( p_sys->b_use_frame_history )
- {
- /* Keep reference for the picture */
- picture_t *p_dup = picture_Hold( p_pic );
-
- /* Slide the history */
- if( p_sys->pp_history[0] )
- picture_Release( p_sys->pp_history[0] );
- for( int i = 1; i < HISTORY_SIZE; i++ )
- p_sys->pp_history[i-1] = p_sys->pp_history[i];
- p_sys->pp_history[HISTORY_SIZE-1] = p_dup;
- }
-
- /* Slide the metadata history. */
- for( int i = 1; i < METADATA_SIZE; i++ )
- p_sys->meta[i-1] = p_sys->meta[i];
- /* The last element corresponds to the current input frame. */
- p_sys->meta[METADATA_SIZE-1].pi_date = p_pic->date;
- p_sys->meta[METADATA_SIZE-1].pi_nb_fields = p_pic->i_nb_fields;
- p_sys->meta[METADATA_SIZE-1].pb_top_field_first = p_pic->b_top_field_first;
-
- /* Remember the frame offset that we should use for this frame.
- The value in p_sys will be updated to reflect the correct value
- for the *next* frame when we call the renderer. */
- int i_frame_offset = p_sys->i_frame_offset;
- int i_meta_idx = (METADATA_SIZE-1) - i_frame_offset;
-
- /* These correspond to the current *outgoing* frame. */
- bool b_top_field_first;
- int i_nb_fields;
- if( i_frame_offset != CUSTOM_PTS )
- {
- /* Pick the correct values from the history. */
- b_top_field_first = p_sys->meta[i_meta_idx].pb_top_field_first;
- i_nb_fields = p_sys->meta[i_meta_idx].pi_nb_fields;
- }
- else
- {
- /* Framerate doublers must not request CUSTOM_PTS, as they need the
- original field timings, and need Deinterlace() to allocate the
- correct number of output frames. */
- assert( !p_sys->b_double_rate );
-
- /* NOTE: i_nb_fields is only used for framerate doublers, so it is
- unused in this case. b_top_field_first is only passed to the
- algorithm. We assume that algorithms that request CUSTOM_PTS
- will, if necessary, extract the TFF/BFF information themselves.
- */
- b_top_field_first = p_pic->b_top_field_first; /* this is not guaranteed
- to be meaningful */
- i_nb_fields = p_pic->i_nb_fields; /* unused */
- }
-
- /* For framerate doublers, determine field duration and allocate
- output frames. */
- int i_double_rate_alloc_end = 0; /* One past last for allocated output
- frames in p_dst[]. Used only for
- framerate doublers. Will be inited
- below. Declared here because the
- PTS logic needs the result. */
- if( p_sys->b_double_rate )
- {
- i_double_rate_alloc_end = i_nb_fields;
- if( i_nb_fields > DEINTERLACE_DST_SIZE )
- {
- /* Note that the effective buffer size depends also on the constant
- private_picture in vout_wrapper.c, since that determines the
- maximum number of output pictures filter_NewPicture() will
- successfully allocate for one input frame.
- */
- msg_Err( p_filter, "Framerate doubler: output buffer too small; "\
- "fields = %d, buffer size = %d. Dropping the "\
- "remaining fields.",
- i_nb_fields, DEINTERLACE_DST_SIZE );
- i_double_rate_alloc_end = DEINTERLACE_DST_SIZE;
- }
-
- /* Allocate output frames. */
- for( int i = 1; i < i_double_rate_alloc_end ; ++i )
- {
- p_dst[i-1]->p_next =
- p_dst[i] = filter_NewPicture( p_filter );
- if( p_dst[i] )
- {
- picture_CopyProperties( p_dst[i], p_pic );
- }
- else
- {
- msg_Err( p_filter, "Framerate doubler: could not allocate "\
- "output frame %d", i+1 );
- i_double_rate_alloc_end = i; /* Inform the PTS logic about the
- correct end position. */
- break; /* If this happens, the rest of the allocations
- aren't likely to work, either... */
- }
- }
- /* Now we have allocated *up to* the correct number of frames;
- normally, exactly the correct number. Upon alloc failure,
- we may have succeeded in allocating *some* output frames,
- but fewer than were desired. In such a case, as many will
- be rendered as were successfully allocated.
-
- Note that now p_dst[i] != NULL
- for 0 <= i < i_double_rate_alloc_end. */
- }
- assert( p_sys->b_double_rate || p_dst[1] == NULL );
- assert( i_nb_fields > 2 || p_dst[2] == NULL );
-
- /* Render */
- if ( p_sys->b_single_field )
- {
- if ( p_sys->pf_render_pic( p_filter, p_dst[0], p_pic, 0, 0 ) )
- goto drop;
- }
- else
- {
- /* Note: RenderIVTC will automatically drop the duplicate frames
- produced by IVTC. This is part of normal operation. */
- if ( p_sys->pf_render_pic( p_filter, p_dst[0], p_pic,
- 0, !b_top_field_first ) )
- goto drop;
- if ( p_dst[1] )
- p_sys->pf_render_pic( p_filter, p_dst[1], p_pic,
- 1, !b_top_field_first );
- if ( p_dst[2] )
- p_sys->pf_render_pic( p_filter, p_dst[1], p_pic,
- 2, !b_top_field_first );
- }
-
- /* Set output timestamps, if the algorithm didn't request CUSTOM_PTS
- for this frame. */
- assert( i_frame_offset <= METADATA_SIZE || i_frame_offset == CUSTOM_PTS );
- if( i_frame_offset != CUSTOM_PTS )
- {
- mtime_t i_base_pts = p_sys->meta[i_meta_idx].pi_date;
-
- /* Note: in the usual case (i_frame_offset = 0 and
- b_double_rate = false), this effectively does nothing.
- This is needed to correct the timestamp
- when i_frame_offset > 0. */
- p_dst[0]->date = i_base_pts;
-
- if( p_sys->b_double_rate )
- {
- mtime_t i_field_dur = GetFieldDuration( p_sys->meta, p_pic );
- /* Processing all actually allocated output frames. */
- for( int i = 1; i < i_double_rate_alloc_end; ++i )
- {
- /* XXX it's not really good especially for the first picture, but
- * I don't think that delaying by one frame is worth it */
- if( i_base_pts > VLC_TS_INVALID )
- p_dst[i]->date = i_base_pts + i * i_field_dur;
- else
- p_dst[i]->date = VLC_TS_INVALID;
- }
- }
- }
-
- for( int i = 0; i < DEINTERLACE_DST_SIZE; ++i )
- {
- if( p_dst[i] )
- {
- p_dst[i]->b_progressive = true;
- p_dst[i]->i_nb_fields = 2;
- }
- }
-
- picture_Release( p_pic );
- return p_dst[0];
-
-drop:
- picture_Release( p_dst[0] );
- for( int i = 1; i < DEINTERLACE_DST_SIZE; ++i )
- {
- if( p_dst[i] )
- picture_Release( p_dst[i] );
- }
- picture_Release( p_pic );
- return NULL;
+ return DoDeinterlacing( p_filter, &p_filter->p_sys->context, p_pic );
}
/*****************************************************************************
@@ -508,22 +264,8 @@ drop:
void Flush( filter_t *p_filter )
{
- filter_sys_t *p_sys = p_filter->p_sys;
+ FlushDeinterlacing(&p_filter->p_sys->context);
- for( int i = 0; i < METADATA_SIZE; i++ )
- {
- p_sys->meta[i].pi_date = VLC_TS_INVALID;
- p_sys->meta[i].pi_nb_fields = 2;
- p_sys->meta[i].pb_top_field_first = true;
- }
- p_sys->i_frame_offset = 0; /* reset to default value (first frame after
- flush cannot have offset) */
- for( int i = 0; i < HISTORY_SIZE; i++ )
- {
- if( p_sys->pp_history[i] )
- picture_Release( p_sys->pp_history[i] );
- p_sys->pp_history[i] = NULL;
- }
IVTCClearState( p_filter );
}
@@ -537,7 +279,7 @@ int Mouse( filter_t *p_filter,
{
VLC_UNUSED(p_old);
*p_mouse = *p_new;
- if( p_filter->p_sys->b_half_height )
+ if( p_filter->p_sys->context.b_half_height )
p_mouse->i_y *= 2;
return VLC_SUCCESS;
}
@@ -596,14 +338,14 @@ notsupp:
for( int i = 0; i < METADATA_SIZE; i++ )
{
- p_sys->meta[i].pi_date = VLC_TS_INVALID;
- p_sys->meta[i].pi_nb_fields = 2;
- p_sys->meta[i].pb_top_field_first = true;
+ p_sys->context.meta[i].pi_date = VLC_TS_INVALID;
+ p_sys->context.meta[i].pi_nb_fields = 2;
+ p_sys->context.meta[i].pb_top_field_first = true;
}
- p_sys->i_frame_offset = 0; /* start with default value (first-ever frame
+ p_sys->context.i_frame_offset = 0; /* start with default value (first-ever frame
cannot have offset) */
for( int i = 0; i < HISTORY_SIZE; i++ )
- p_sys->pp_history[i] = NULL;
+ p_sys->context.pp_history[i] = NULL;
IVTCClearState( p_filter );
diff --git a/modules/video_filter/deinterlace/deinterlace.h b/modules/video_filter/deinterlace/deinterlace.h
index e3ded7a944..ecc26e0d60 100644
--- a/modules/video_filter/deinterlace/deinterlace.h
+++ b/modules/video_filter/deinterlace/deinterlace.h
@@ -42,6 +42,7 @@ struct vlc_object_t;
#include "algo_yadif.h"
#include "algo_phosphor.h"
#include "algo_ivtc.h"
+#include "common.h"
/*****************************************************************************
* Local data
@@ -61,20 +62,6 @@ static const char *const mode_list_text[] = {
* Data structures
*****************************************************************************/
-#define METADATA_SIZE (3)
-/**
- * Metadata history structure, used for framerate doublers.
- * This is used for computing field duration in Deinterlace().
- * @see Deinterlace()
- */
-typedef struct {
- mtime_t pi_date;
- int pi_nb_fields;
- bool pb_top_field_first;
-} metadata_history_t;
-
-#define HISTORY_SIZE (3)
-#define CUSTOM_PTS -1
/**
* Top-level deinterlace subsystem state.
*/
@@ -82,12 +69,6 @@ struct filter_sys_t
{
const vlc_chroma_description_t *chroma;
- /* Algorithm behaviour flags */
- bool b_double_rate; /**< Shall we double the framerate? */
- bool b_half_height; /**< Shall be divide the height by 2 */
- bool b_use_frame_history; /**< Use the input frame history buffer? */
- bool b_single_field; /**< The filter doesn't handle the field info */
-
/** Merge routine: C, MMX, SSE, ALTIVEC, NEON, ... */
void (*pf_merge) ( void *, const void *, const void *, size_t );
#if defined (__i386__) || defined (__x86_64__)
@@ -95,21 +76,7 @@ struct filter_sys_t
void (*pf_end_merge) ( void );
#endif
- int (*pf_render_pic)(filter_t *p_filter, picture_t *p_dst, picture_t *p_pic,
- int order, int i_field);
-
- /**
- * Metadata history (PTS, nb_fields, TFF). Used for framerate doublers.
- * @see metadata_history_t
- */
- metadata_history_t meta[METADATA_SIZE];
-
- /** Output frame timing / framerate doubler control
- (see extra documentation in deinterlace.h) */
- int i_frame_offset;
-
- /** Input frame history buffer for algorithms with temporal filtering. */
- picture_t *pp_history[HISTORY_SIZE];
+ struct deinterlace_ctx context;
/* Algorithm-specific substructures */
union {
--
2.12.1
More information about the vlc-devel
mailing list