[vlc-devel] [PATCH 11/12] video_filter:deinterlace: move some structures in common.h

Steve Lhomme robux4 at videolabs.io
Tue Jun 27 16:11:13 CEST 2017


So they can be reused by other deinterlacing implementations

--
replaces https://patches.videolan.org/patch/17112/
- fix field swapping when using history
- move the duration computation from frame rate in a separate patch
- add documentation
---
 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        | 315 +++++++++++++++++++++
 modules/video_filter/deinterlace/common.h        |  78 +++++-
 modules/video_filter/deinterlace/deinterlace.c   | 336 +++--------------------
 modules/video_filter/deinterlace/deinterlace.h   |  37 +--
 8 files changed, 459 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..a50be692c8
--- /dev/null
+++ b/modules/video_filter/deinterlace/common.c
@@ -0,0 +1,315 @@
+/*****************************************************************************
+ * 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 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;
+    }
+
+    /* 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 );
+            /* 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..2b0ce069da 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,74 @@
 #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
+
+/**
+ * @brief Get the field duration based on the previous fields
+ * @param p_pic the picture which field we want the duration
+ * @return the duration of the field or 0 if there's no known framerate
+ */
+mtime_t GetFieldDuration( const struct deinterlace_ctx *,
+                          const picture_t *p_pic );
+
+/**
+ * @brief Get the output video_format_t configured for the deinterlacer
+ * @param p_dst video_format_t to fill
+ * @param p_src source video_format_t
+ */
+void GetDeinterlacingOutput( const struct deinterlace_ctx *,
+                             video_format_t *p_dst, const video_format_t *p_src );
+
+/**
+ * @brief Do the deinterlacing of the picture using pf_render_pic() calls.
+ * @return The deinterlaced picture or NULL if it failed
+ */
+picture_t *DoDeinterlacing( filter_t *, struct deinterlace_ctx *, picture_t * );
+
+/**
+ * @brief Flush the deinterlacer context
+ */
+void FlushDeinterlacing( struct deinterlace_ctx * );
+
+
 #endif
diff --git a/modules/video_filter/deinterlace/deinterlace.c b/modules/video_filter/deinterlace/deinterlace.c
index 93efabb8b5..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