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