[vlc-devel] [PATCH 08/19] deinterlace: implement draining of extra pictures

Steve Lhomme robux4 at ycbcr.xyz
Tue Oct 13 15:51:51 CEST 2020


Each filter using the common deinterlacing code now calls DoDrain (similar to
DoDeinterlacing) to do its draining.

We no longer return pictures chained using vlc_picture_chain_AppendChain().

DoDraining calls DoDeinterlacing internally with a NULL input picture. To
create secondary pictures, the state to create the same pictures that were
appended before is kept in the filter p_sys. The prev_pic is the original
picture used to create the primary and secondary pictures. The picture is
released when there is no more pictures to produce from this picture or during
a Flush.

Now DoDeinterlacing() only outputs one picture at a time. By default it outputs
the current_field == 0 picture, then extra pictures up to max_fields.
---
 modules/hw/d3d11/d3d11_deinterlace.c          |  12 ++
 modules/hw/d3d9/dxva2_deinterlace.c           |   7 +
 modules/video_filter/deinterlace/common.c     | 152 +++++++++---------
 modules/video_filter/deinterlace/common.h     |   5 +
 .../video_filter/deinterlace/deinterlace.c    |   7 +
 5 files changed, 107 insertions(+), 76 deletions(-)

diff --git a/modules/hw/d3d11/d3d11_deinterlace.c b/modules/hw/d3d11/d3d11_deinterlace.c
index a61160a9c05..c15e01d9164 100644
--- a/modules/hw/d3d11/d3d11_deinterlace.c
+++ b/modules/hw/d3d11/d3d11_deinterlace.c
@@ -174,6 +174,17 @@ static picture_t *Deinterlace(filter_t *p_filter, picture_t *p_pic)
     return res;
 }
 
+static picture_t *Drain(filter_t *p_filter)
+{
+    filter_sys_t *p_sys = p_filter->p_sys;
+
+    d3d11_device_lock( p_sys->d3d_dev );
+    picture_t *res = DoDraining( p_filter, &p_sys->context );
+    d3d11_device_unlock( p_sys->d3d_dev );
+
+    return res;
+}
+
 static const struct filter_mode_t *GetFilterMode(const char *mode)
 {
     if ( mode == NULL || !strcmp( mode, "auto" ) )
@@ -244,6 +255,7 @@ static void D3D11CloseDeinterlace(filter_t *filter)
 
 static const struct vlc_filter_operations filter_ops = {
     .filter_video = Deinterlace, .flush = Flush, .close = D3D11CloseDeinterlace,
+    .drain_video = Drain,
 };
 
 int D3D11OpenDeinterlace(filter_t *filter)
diff --git a/modules/hw/d3d9/dxva2_deinterlace.c b/modules/hw/d3d9/dxva2_deinterlace.c
index 65cacdad59b..ba8a0ebfed4 100644
--- a/modules/hw/d3d9/dxva2_deinterlace.c
+++ b/modules/hw/d3d9/dxva2_deinterlace.c
@@ -268,6 +268,12 @@ static picture_t *Deinterlace(filter_t *p_filter, picture_t *p_pic)
     return DoDeinterlacing( p_filter, &p_sys->context, p_pic );
 }
 
+static picture_t *Drain(filter_t *p_filter)
+{
+    filter_sys_t *p_sys = p_filter->p_sys;
+    return DoDraining( p_filter, &p_sys->context );
+}
+
 static const struct filter_mode_t *GetFilterMode(const char *mode)
 {
     if ( mode == NULL || !strcmp( mode, "auto" ) )
@@ -337,6 +343,7 @@ static void D3D9CloseDeinterlace(filter_t *filter)
 
 static const struct vlc_filter_operations filter_ops = {
     .filter_video = Deinterlace, .flush = Flush, .close = D3D9CloseDeinterlace,
+    .drain_video = Drain,
 };
 
 int D3D9OpenDeinterlace(filter_t *filter)
diff --git a/modules/video_filter/deinterlace/common.c b/modules/video_filter/deinterlace/common.c
index 94a6280e37e..1f55b6c4d3b 100644
--- a/modules/video_filter/deinterlace/common.c
+++ b/modules/video_filter/deinterlace/common.c
@@ -49,6 +49,10 @@ void InitDeinterlacingContext( struct deinterlace_ctx *p_context )
                                   cannot have offset) */
     for( int i = 0; i < HISTORY_SIZE; i++ )
         p_context->pp_history[i] = NULL;
+
+    p_context->prev_pic = NULL;
+    p_context->current_field = 0;
+    p_context->max_fields = 0;
 }
 
 void FlushDeinterlacing(struct deinterlace_ctx *p_context)
@@ -67,6 +71,11 @@ void FlushDeinterlacing(struct deinterlace_ctx *p_context)
             picture_Release( p_context->pp_history[i] );
         p_context->pp_history[i] = NULL;
     }
+    if ( p_context->prev_pic )
+    {
+        picture_Release( p_context->prev_pic );
+        p_context->prev_pic = NULL;
+    }
 }
 
 vlc_tick_t GetFieldDuration(const struct deinterlace_ctx *p_context,
@@ -127,7 +136,6 @@ void GetDeinterlacingOutput( const struct deinterlace_ctx *p_context,
 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
@@ -137,20 +145,32 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
 
     bool b_top_field_first;
 
-    /* Request output picture */
-    p_dst[0] = AllocPicture( p_filter );
-    if( p_dst[0] == NULL )
+    if ( p_context->current_field == p_context->max_fields )
     {
-        picture_Release( p_pic );
-        return NULL;
+        if( p_pic == NULL )
+            return NULL; // there's nothing else to drain
+    }
+    if ( p_context->current_field != 0 )
+    {
+        assert(p_pic == NULL); // we are draining the output
+        p_pic = p_context->prev_pic;
     }
-    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;
+    /* Request output picture */
+    picture_t *p_dst = AllocPicture( p_filter );
+    if( p_dst == NULL )
+    {
+        if ( p_context->current_field != 0 )
+            msg_Err( p_filter, "Framerate doubler: could not allocate "\
+                                "output frame %d", p_context->current_field+1 );
+        goto exit;
+    }
+    picture_CopyProperties( p_dst, p_pic );
+    p_dst->b_progressive = true;
+    p_dst->i_nb_fields = 2;
 
+    if ( p_context->current_field == 0 )
+    {
     /* Update the input frame history, if the currently active algorithm
        needs it. */
     if( p_context->settings.b_use_frame_history )
@@ -173,6 +193,7 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
     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
@@ -206,6 +227,8 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
         i_nb_fields       = p_pic->i_nb_fields;       /* unused */
     }
 
+    if ( p_context->current_field == 0 )
+    {
     /* For framerate doublers, determine field duration and allocate
        output frames. */
     i_double_rate_alloc_end = 0; /* One past last for allocated output
@@ -229,57 +252,39 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
                                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]           = AllocPicture( p_filter );
-            if( p_dst[i] )
-            {
-                vlc_picture_chain_AppendChain( p_dst[i-1], 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->settings.b_double_rate  ||  p_dst[1] == NULL );
-    assert( i_nb_fields > 2  ||  p_dst[2] == NULL );
+    assert( p_context->settings.b_double_rate  ||  i_double_rate_alloc_end == 0 );
+    assert( i_nb_fields > 2  ||  i_double_rate_alloc_end <= 2 );
+    p_context->max_fields = i_double_rate_alloc_end;
+    }
 
     /* Render */
     if ( !p_context->settings.b_double_rate )
     {
-        if ( p_context->pf_render_single_pic( p_filter, p_dst[0], p_pic ) )
+        if ( p_context->pf_render_single_pic( p_filter, p_dst, 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[2], p_pic,
-                                          2, !b_top_field_first );
+        if ( p_context->current_field == 0 )
+        {
+            assert(p_context->prev_pic == NULL);
+            p_context->prev_pic = p_pic;
+            if ( p_context->pf_render_ordered( p_filter, p_dst, p_pic,
+                                               0, !b_top_field_first ) )
+                goto drop;
+        }
+        else
+        {
+            if ( p_context->current_field == 1 )
+                p_context->pf_render_ordered( p_filter, p_dst, p_pic,
+                                            1, b_top_field_first );
+            else
+                p_context->pf_render_ordered( p_filter, p_dst, p_pic,
+                                            2, !b_top_field_first );
+        }
     }
 
     if ( p_context->settings.b_custom_pts )
@@ -313,43 +318,33 @@ picture_t *DoDeinterlacing( filter_t *p_filter,
                  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;
+        p_dst->date = i_base_pts;
 
-        if( p_context->settings.b_double_rate )
+        if( p_context->current_field != 0 )
         {
-            vlc_tick_t i_field_dur = GetFieldDuration( p_context, &p_filter->fmt_out.video, 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_TICK_INVALID )
             {
-                /* 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_TICK_INVALID )
-                    p_dst[i]->date = i_base_pts + i * i_field_dur;
-                else
-                    p_dst[i]->date = VLC_TICK_INVALID;
+                vlc_tick_t i_field_dur = GetFieldDuration( p_context, &p_filter->fmt_out.video, p_pic );
+                p_dst->date += p_context->current_field * i_field_dur;
             }
         }
     }
 
-    for( int i = 0; i < DEINTERLACE_DST_SIZE; ++i )
+    if ( ++p_context->current_field >= p_context->max_fields )
     {
-        if( p_dst[i] )
-        {
-            p_dst[i]->b_progressive = true;
-            p_dst[i]->i_nb_fields = 2;
-        }
+        picture_Release( p_pic );
+        p_context->prev_pic = NULL;
+        p_context->current_field = 0;
+        p_context->max_fields = 0;
     }
 
-    picture_Release( p_pic );
-    return p_dst[0];
+    return p_dst;
 
 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_dst );
+exit:
 #ifndef NDEBUG
     picture_Release( p_pic );
     return NULL;
@@ -357,3 +352,8 @@ drop:
     return p_pic;
 #endif
 }
+
+picture_t *DoDraining( filter_t *p_filter, struct deinterlace_ctx *p_context )
+{
+    return DoDeinterlacing( p_filter, p_context, NULL );
+}
diff --git a/modules/video_filter/deinterlace/common.h b/modules/video_filter/deinterlace/common.h
index 144523647c2..c4966530e36 100644
--- a/modules/video_filter/deinterlace/common.h
+++ b/modules/video_filter/deinterlace/common.h
@@ -88,6 +88,10 @@ struct deinterlace_ctx
                                  int order, int i_field);
         int (*pf_render_single_pic)(filter_t *, picture_t *p_dst, picture_t *p_pic);
     };
+
+    int        current_field;
+    int        max_fields;
+    picture_t  *prev_pic;
 };
 
 #define DEINTERLACE_DST_SIZE 3
@@ -116,6 +120,7 @@ void GetDeinterlacingOutput( const struct deinterlace_ctx *,
  * @return The deinterlaced picture or NULL if it failed
  */
 picture_t *DoDeinterlacing( filter_t *, struct deinterlace_ctx *, picture_t * );
+picture_t *DoDraining( filter_t *, struct deinterlace_ctx * );
 
 /**
  * @brief Flush the deinterlacer context
diff --git a/modules/video_filter/deinterlace/deinterlace.c b/modules/video_filter/deinterlace/deinterlace.c
index 62bd077c0a2..e0517ee0f46 100644
--- a/modules/video_filter/deinterlace/deinterlace.c
+++ b/modules/video_filter/deinterlace/deinterlace.c
@@ -444,6 +444,12 @@ picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
     return DoDeinterlacing( p_filter, &p_sys->context, p_pic );
 }
 
+static picture_t *Drain(filter_t *p_filter )
+{
+    filter_sys_t *p_sys = p_filter->p_sys;
+    return DoDraining( p_filter, &p_sys->context );
+}
+
 /*****************************************************************************
  * Flush
  *****************************************************************************/
@@ -486,6 +492,7 @@ static void Close( filter_t *p_filter )
 
 static const struct vlc_filter_operations filter_ops = {
     .filter_video = Deinterlace,
+    .drain_video = Drain,
     .flush = Flush,
     .video_mouse = Mouse,
     .close = Close,
-- 
2.26.2



More information about the vlc-devel mailing list