vlc | branch: master | Steve Lhomme <[email protected]> | Mon Jun 26 10:00:28 2017 +0200| [ab89edb21d4a7ed0a68a3de23cc8a76ced76df63] | committer: Jean-Baptiste Kempf
deinterlace: move some structures in common.h So they can be reused by other deinterlacing implementations Signed-off-by: Jean-Baptiste Kempf <[email protected]> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=ab89edb21d4a7ed0a68a3de23cc8a76ced76df63 --- 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 | 314 ++++++++++++++++++++++ modules/video_filter/deinterlace/common.h | 84 +++++- modules/video_filter/deinterlace/deinterlace.c | 322 +++-------------------- modules/video_filter/deinterlace/deinterlace.h | 39 +-- 8 files changed, 457 insertions(+), 353 deletions(-) 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 e320dde42b..0de608ccba 100644 --- a/modules/video_filter/deinterlace/algo_ivtc.c +++ b/modules/video_filter/deinterlace/algo_ivtc.c @@ -408,8 +408,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 ); @@ -460,7 +460,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 ); @@ -634,7 +634,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 ); @@ -862,9 +862,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 ); @@ -979,9 +979,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 ); @@ -1218,8 +1218,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 ); @@ -1478,9 +1478,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 ) @@ -1522,7 +1522,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; @@ -1571,7 +1571,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 122af599cd..d94e45dbb5 100644 --- a/modules/video_filter/deinterlace/algo_yadif.c +++ b/modules/video_filter/deinterlace/algo_yadif.c @@ -64,9 +64,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. @@ -177,7 +177,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; } @@ -192,7 +192,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..ec558d0ded --- /dev/null +++ b/modules/video_filter/deinterlace/common.c @@ -0,0 +1,314 @@ +/***************************************************************************** + * common.c : Common helper function for the VLC deinterlacer + ***************************************************************************** + * Copyright (C) 2000-2017 VLC authors and VideoLAN + * $Id$ + * + * Author: Sam Hocevar <[email protected]> + * Christophe Massiot <[email protected]> + * Laurent Aimar <[email protected]> + * Juha Jeronen <[email protected]> + * Steve Lhomme <[email protected]> + * + * 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_double_rate ) + { + if ( p_context->pf_render_single_pic( p_filter, p_dst[0], p_pic ) ) + 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_ordered( p_filter, p_dst[0], p_pic, + 0, !b_top_field_first ) ) + goto drop; + if ( p_dst[1] ) + p_context->pf_render_ordered( p_filter, p_dst[1], p_pic, + 1, b_top_field_first ); + if ( p_dst[2] ) + p_context->pf_render_ordered( 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..4f28d361e0 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 <[email protected]> + * Steve Lhomme <[email protected]> * * 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,80 @@ #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? */ + + /** + * 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]; + + union { + /** + * @param i_order Temporal field number: 0 = first, 1 = second, 2 = repeat first. + * @param i_field Keep which field? 0 = top field, 1 = bottom field. + */ + int (*pf_render_ordered)(filter_t *, picture_t *p_dst, picture_t *p_pic, + int order, int i_field); + int (*pf_render_single_pic)(filter_t *, picture_t *p_dst, picture_t *p_pic); + }; +}; + +#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_ordered() or pf_render_single_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 1f5c0f1080..210557716d 100644 --- a/modules/video_filter/deinterlace/deinterlace.c +++ b/modules/video_filter/deinterlace/deinterlace.c @@ -151,9 +151,9 @@ static void SetFilterMethod( filter_t *p_filter, const char *mode, bool pack ) if ( mode == NULL ) mode = "auto"; - p_sys->b_double_rate = false; - p_sys->b_half_height = false; - p_sys->b_use_frame_history = false; + p_sys->context.b_double_rate = false; + p_sys->context.b_half_height = false; + p_sys->context.b_use_frame_history = false; if ( !strcmp( mode, "auto" ) ) { @@ -161,27 +161,27 @@ static void SetFilterMethod( filter_t *p_filter, const char *mode, bool pack ) } else if( !strcmp( mode, "discard" ) ) { - p_sys->pf_render_single_pic = RenderDiscard; - p_sys->b_half_height = true; + p_sys->context.pf_render_single_pic = RenderDiscard; + p_sys->context.b_half_height = true; } else if( !strcmp( mode, "bob" ) || !strcmp( mode, "progressive-scan" ) ) { - p_sys->pf_render_ordered = RenderBob; - p_sys->b_double_rate = true; + p_sys->context.pf_render_ordered = RenderBob; + p_sys->context.b_double_rate = true; } else if( !strcmp( mode, "linear" ) ) { - p_sys->pf_render_ordered = RenderLinear; - p_sys->b_double_rate = true; + p_sys->context.pf_render_ordered = RenderLinear; + p_sys->context.b_double_rate = true; } else if( !strcmp( mode, "mean" ) ) { - p_sys->pf_render_single_pic = RenderMean; - p_sys->b_half_height = true; + p_sys->context.pf_render_single_pic = RenderMean; + p_sys->context.b_half_height = true; } else if( !strcmp( mode, "blend" ) ) { - p_sys->pf_render_single_pic = RenderBlend; + p_sys->context.pf_render_single_pic = RenderBlend; } else if( pack ) { @@ -191,14 +191,14 @@ static void SetFilterMethod( filter_t *p_filter, const char *mode, bool pack ) } else if( !strcmp( mode, "yadif" ) ) { - p_sys->pf_render_single_pic = RenderYadifSingle; - p_sys->b_use_frame_history = true; + p_sys->context.pf_render_ordered = RenderYadif; + p_sys->context.b_use_frame_history = true; } else if( !strcmp( mode, "yadif2x" ) ) { - p_sys->pf_render_ordered = RenderYadif; - p_sys->b_double_rate = true; - p_sys->b_use_frame_history = true; + p_sys->context.pf_render_ordered = RenderYadif; + p_sys->context.b_double_rate = true; + p_sys->context.b_use_frame_history = true; } else if( p_sys->chroma->pixel_size > 1 ) { @@ -208,18 +208,18 @@ static void SetFilterMethod( filter_t *p_filter, const char *mode, bool pack ) } else if( !strcmp( mode, "x" ) ) { - p_sys->pf_render_single_pic = RenderX; + p_sys->context.pf_render_single_pic = RenderX; } else if( !strcmp( mode, "phosphor" ) ) { - p_sys->pf_render_ordered = RenderPhosphor; - p_sys->b_double_rate = true; - p_sys->b_use_frame_history = true; + p_sys->context.pf_render_ordered = 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_single_pic = RenderIVTC; - p_sys->b_use_frame_history = true; + p_sys->context.pf_render_single_pic = RenderIVTC; + p_sys->context.b_use_frame_history = true; } else msg_Err( p_filter, "unknown deinterlace mode \"%s\"", mode ); @@ -242,261 +242,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_double_rate ) - { - if ( p_sys->pf_render_single_pic( p_filter, p_dst[0], p_pic ) ) - 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_ordered( p_filter, p_dst[0], p_pic, - 0, !b_top_field_first ) ) - goto drop; - if ( p_dst[1] ) - p_sys->pf_render_ordered( p_filter, p_dst[1], p_pic, - 1, b_top_field_first ); - if ( p_dst[2] ) - p_sys->pf_render_ordered( 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 ); } /***************************************************************************** @@ -505,22 +261,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 ); } @@ -534,7 +276,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; } @@ -593,14 +335,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 d750863d49..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,11 +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? */ - /** Merge routine: C, MMX, SSE, ALTIVEC, NEON, ... */ void (*pf_merge) ( void *, const void *, const void *, size_t ); #if defined (__i386__) || defined (__x86_64__) @@ -94,24 +76,7 @@ struct filter_sys_t void (*pf_end_merge) ( void ); #endif - union { - int (*pf_render_ordered)(filter_t *, picture_t *p_dst, picture_t *p_pic, - int order, int i_field); - int (*pf_render_single_pic)(filter_t *, picture_t *p_dst, picture_t *p_pic); - }; - - /** - * 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 { _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
