Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
V
vlc
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
videolan
vlc
Commits
3586bd91
Commit
3586bd91
authored
Feb 19, 2011
by
Juha Jeronen
Committed by
Laurent Aimar
Feb 20, 2011
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Soft field repeat support for deinterlacer
Signed-off-by:
Laurent Aimar
<
fenrir@videolan.org
>
parent
278f8ed4
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
291 additions
and
32 deletions
+291
-32
modules/video_filter/deinterlace.c
modules/video_filter/deinterlace.c
+291
-32
No files found.
modules/video_filter/deinterlace.c
View file @
3586bd91
...
...
@@ -126,7 +126,16 @@ static const char *const ppsz_filter_options[] = {
"mode"
,
NULL
};
/* Used for framerate doublers */
#define METADATA_SIZE (3)
typedef
struct
{
mtime_t
pi_date
[
METADATA_SIZE
];
int
pi_nb_fields
[
METADATA_SIZE
];
bool
pb_top_field_first
[
METADATA_SIZE
];
}
metadata_history_t
;
#define HISTORY_SIZE (3)
#define CUSTOM_PTS -1
struct
filter_sys_t
{
int
i_mode
;
/* Deinterlace mode */
...
...
@@ -136,12 +145,78 @@ struct filter_sys_t
void
(
*
pf_merge
)
(
void
*
,
const
void
*
,
const
void
*
,
size_t
);
void
(
*
pf_end_merge
)
(
void
);
mtime_t
i_last_date
;
/* Metadata history (PTS, nb_fields, TFF). Used for framerate doublers. */
metadata_history_t
meta
;
/* Output frame timing / framerate doubler control (see below) */
int
i_frame_offset
;
/* Yadif */
picture_t
*
pp_history
[
HISTORY_SIZE
];
};
/* NOTE on i_frame_offset:
This value indicates the offset between input and output frames in the currently active deinterlace algorithm.
See the rationale below for why this is needed and how it is used.
Valid range: 0 <= i_frame_offset < METADATA_SIZE, or i_frame_offset = CUSTOM_PTS.
The special value CUSTOM_PTS is only allowed if b_double_rate is false.
If CUSTOM_PTS is used, the algorithm must compute the outgoing PTSs itself,
and additionally, read the TFF/BFF information itself (if it needs it)
from the incoming frames.
Meaning of values:
0 = output frame corresponds to the current input frame
(no frame offset; default if not set),
1 = output frame corresponds to the previous input frame
(e.g. Yadif and Yadif2x work like this),
...
If necessary, i_frame_offset should be updated by the active deinterlace algorithm
to indicate the correct delay for the *next* input frame. It does not matter at which i_order
the algorithm updates this information, but the new value will only take effect upon the
next call to Deinterlace() (i.e. at the next incoming frame).
The first-ever frame that arrives to the filter after Open() is always handled as having
i_frame_offset = 0. For the second and all subsequent frames, each algorithm is responsible
for setting the offset correctly. (The default is 0, so if that is correct, there's no need
to do anything.)
This solution guarantees that i_frame_offset:
1) is up to date at the start of each frame,
2) does not change (as far as Deinterlace() is concerned) during a frame, and
3) does not need a special API for setting the value at the start of each input frame,
before the algorithm starts rendering the (first) output frame for that input frame.
The deinterlace algorithm is allowed to behave differently for different input frames.
This is especially important for startup, when full history (as defined by each algorithm)
is not yet available. During the first-ever input frame, it is clear that it is the
only possible source for information, so i_frame_offset = 0 is necessarily correct.
After that, what to do is up to each algorithm.
Having the correct offset at the start of each input frame is critically important in order to:
1) Allocate the correct number of output frames for framerate doublers, and to
2) Pass correct TFF/BFF information to the algorithm.
These points are important for proper soft field repeat support. This feature is used in some
streams originating from film. In soft NTSC telecine, the number of fields alternates as 3,2,3,2,...
and the video field dominance flips every two frames (after every "3"). Also, some streams
request an occasional field repeat (nb_fields = 3), after which the video field dominance flips.
To render such streams correctly, the nb_fields and TFF/BFF information must be taken from
the specific input frame that the algorithm intends to render.
Additionally, the output PTS is automatically computed by Deinterlace() from i_frame_offset and i_order.
It is possible to use the special value CUSTOM_PTS to indicate that the algorithm computes
the output PTSs itself. In this case, Deinterlace() will pass them through. This special value
is not valid for framerate doublers, as by definition they are field renderers, so they need to
use the original field timings to work correctly. Basically, this special value is only intended
for algorithms that need to perform nontrivial framerate conversions (such as IVTC).
*/
/*****************************************************************************
* SetFilterMethod: setup the deinterlace method to use.
*****************************************************************************/
...
...
@@ -209,6 +284,8 @@ static void SetFilterMethod( filter_t *p_filter, const char *psz_method, vlc_fou
p_sys
->
b_half_height
=
false
;
}
p_sys
->
i_frame_offset
=
0
;
/* reset to default when method changes */
msg_Dbg
(
p_filter
,
"using %s deinterlace method"
,
psz_method
);
}
...
...
@@ -1440,7 +1517,7 @@ static int RenderYadif( filter_t *p_filter, picture_t *p_dst, picture_t *p_src,
filter_sys_t
*
p_sys
=
p_filter
->
p_sys
;
/* */
assert
(
i_order
==
0
||
i_order
==
1
);
assert
(
i_order
>=
0
&&
i_order
<=
2
);
/* 2 = soft field repeat */
assert
(
i_field
==
0
||
i_field
==
1
);
if
(
i_order
==
0
)
...
...
@@ -1465,6 +1542,41 @@ static int RenderYadif( filter_t *p_filter, picture_t *p_dst, picture_t *p_src,
picture_t
*
p_cur
=
p_sys
->
pp_history
[
1
];
picture_t
*
p_next
=
p_sys
->
pp_history
[
2
];
/* Account for soft field repeat.
The "parity" parameter affects the algorithm like this (from yadif.h):
uint8_t *prev2= parity ? prev : cur ;
uint8_t *next2= parity ? cur : next;
The original parity expression that was used here is:
(i_field ^ (i_order == i_field)) & 1
Truth table:
i_field = 0, i_order = 0 => 1
i_field = 1, i_order = 1 => 0
i_field = 1, i_order = 0 => 1
i_field = 0, i_order = 1 => 0
=> equivalent with e.g. (1 - i_order) or (i_order + 1) % 2
Thus, in a normal two-field frame,
parity 1 = first field (i_order == 0)
parity 0 = second field (i_order == 1)
Now, with three fields, where the third is a copy of the first,
i_order = 0 => parity 1 (as usual)
i_order = 1 => due to the repeat, prev = cur, but also next = cur.
Because in such a case there is no motion (otherwise field repeat makes no sense),
we don't actually need to invoke Yadif's filter(). Thus, set "parity" to 2,
and use this to bypass the filter.
i_order = 2 => parity 0 (as usual)
*/
int
yadif_parity
;
if
(
p_cur
&&
p_cur
->
i_nb_fields
>
2
)
yadif_parity
=
(
i_order
+
1
)
%
3
;
/* 1, *2*, 0; where 2 is a special value meaning "bypass filter". */
else
yadif_parity
=
(
i_order
+
1
)
%
2
;
/* 1, 0 */
/* Filter if we have all the pictures we need */
if
(
p_prev
&&
p_cur
&&
p_next
)
{
...
...
@@ -1486,7 +1598,7 @@ static int RenderYadif( filter_t *p_filter, picture_t *p_dst, picture_t *p_src,
for
(
int
y
=
1
;
y
<
dstp
->
i_visible_lines
-
1
;
y
++
)
{
if
(
(
y
%
2
)
==
i_field
)
if
(
(
y
%
2
)
==
i_field
||
yadif_parity
==
2
)
{
vlc_memcpy
(
&
dstp
->
p_pixels
[
y
*
dstp
->
i_pitch
],
&
curp
->
p_pixels
[
y
*
curp
->
i_pitch
],
dstp
->
i_visible_pitch
);
...
...
@@ -1505,7 +1617,7 @@ static int RenderYadif( filter_t *p_filter, picture_t *p_dst, picture_t *p_src,
&
nextp
->
p_pixels
[
y
*
nextp
->
i_pitch
],
dstp
->
i_visible_pitch
,
curp
->
i_pitch
,
(
i_field
^
(
i_order
==
i_field
))
&
1
);
yadif_parity
);
}
/* We duplicate the first and last lines */
...
...
@@ -1516,18 +1628,23 @@ static int RenderYadif( filter_t *p_filter, picture_t *p_dst, picture_t *p_src,
}
}
/*
*/
p_dst
->
date
=
(
p_next
->
date
-
p_cur
->
date
)
*
i_order
/
2
+
p_cur
->
date
;
p_sys
->
i_frame_offset
=
1
;
/* p_curr will be rendered at next frame, too
*/
return
VLC_SUCCESS
;
}
else
if
(
!
p_prev
&&
!
p_cur
&&
p_next
)
{
/* NOTE: For the first frame, we use the default frame offset
as set by Open() or SetFilterMethod(). It is always 0. */
/* FIXME not good as it does not use i_order/i_field */
RenderX
(
p_dst
,
p_next
);
return
VLC_SUCCESS
;
}
else
{
p_sys
->
i_frame_offset
=
1
;
/* p_curr will be rendered at next frame */
return
VLC_EGENERIC
;
}
}
...
...
@@ -1535,10 +1652,11 @@ static int RenderYadif( filter_t *p_filter, picture_t *p_dst, picture_t *p_src,
/*****************************************************************************
* video filter2 functions
*****************************************************************************/
#define DEINTERLACE_DST_SIZE 3
static
picture_t
*
Deinterlace
(
filter_t
*
p_filter
,
picture_t
*
p_pic
)
{
filter_sys_t
*
p_sys
=
p_filter
->
p_sys
;
picture_t
*
p_dst
[
2
];
picture_t
*
p_dst
[
DEINTERLACE_DST_SIZE
];
/* Request output picture */
p_dst
[
0
]
=
filter_NewPicture
(
p_filter
);
...
...
@@ -1549,25 +1667,116 @@ static picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
}
picture_CopyProperties
(
p_dst
[
0
],
p_pic
);
if
(
p_sys
->
b_double_rate
)
/* 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
;
/* Slide the metadata history. */
for
(
int
i
=
1
;
i
<
METADATA_SIZE
;
i
++
)
{
p_dst
[
0
]
->
p_next
=
p_dst
[
1
]
=
filter_NewPicture
(
p_filter
);
if
(
p_dst
[
1
]
)
{
picture_CopyProperties
(
p_dst
[
1
],
p_pic
);
/* 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
(
p_sys
->
i_last_date
>
VLC_TS_INVALID
&&
p_pic
->
date
>
VLC_TS_INVALID
)
p_dst
[
1
]
->
date
=
p_pic
->
date
+
(
p_pic
->
date
-
p_sys
->
i_last_date
)
/
2
;
}
p_sys
->
i_last_date
=
p_pic
->
date
;
p_sys
->
meta
.
pi_date
[
i
-
1
]
=
p_sys
->
meta
.
pi_date
[
i
];
p_sys
->
meta
.
pi_nb_fields
[
i
-
1
]
=
p_sys
->
meta
.
pi_nb_fields
[
i
];
p_sys
->
meta
.
pb_top_field_first
[
i
-
1
]
=
p_sys
->
meta
.
pb_top_field_first
[
i
];
}
/* The last element corresponds to the current input frame. */
p_sys
->
meta
.
pi_date
[
METADATA_SIZE
-
1
]
=
p_pic
->
date
;
p_sys
->
meta
.
pi_nb_fields
[
METADATA_SIZE
-
1
]
=
p_pic
->
i_nb_fields
;
p_sys
->
meta
.
pb_top_field_first
[
METADATA_SIZE
-
1
]
=
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
.
pb_top_field_first
[
i_meta_idx
];
i_nb_fields
=
p_sys
->
meta
.
pi_nb_fields
[
i_meta_idx
];
}
else
{
p_dst
[
1
]
=
NULL
;
/* 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. */
mtime_t
i_field_dur
=
0
;
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
)
{
/* Calculate one field duration. */
int
i
=
0
;
int
iend
=
METADATA_SIZE
-
1
;
/* Find oldest valid logged date. Note: the current input frame doesn't count. */
for
(
;
i
<
iend
;
i
++
)
if
(
p_sys
->
meta
.
pi_date
[
i
]
>
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_sys
->
meta
.
pi_nb_fields
[
j
];
/* One field took this long. */
i_field_dur
=
(
p_pic
->
date
-
p_sys
->
meta
.
pi_date
[
i
])
/
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.
*/
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
==
true
||
p_dst
[
1
]
==
NULL
);
assert
(
i_nb_fields
>
2
||
p_dst
[
2
]
==
NULL
);
/* Render */
switch
(
p_sys
->
i_mode
)
{
case
DEINTERLACE_DISCARD
:
...
...
@@ -1575,15 +1784,19 @@ static picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
break
;
case
DEINTERLACE_BOB
:
RenderBob
(
p_filter
,
p_dst
[
0
],
p_pic
,
!
p_pic
->
b_top_field_first
);
RenderBob
(
p_filter
,
p_dst
[
0
],
p_pic
,
!
b_top_field_first
);
if
(
p_dst
[
1
]
)
RenderBob
(
p_filter
,
p_dst
[
1
],
p_pic
,
p_pic
->
b_top_field_first
);
RenderBob
(
p_filter
,
p_dst
[
1
],
p_pic
,
b_top_field_first
);
if
(
p_dst
[
2
]
)
RenderBob
(
p_filter
,
p_dst
[
2
],
p_pic
,
!
b_top_field_first
);
break
;;
case
DEINTERLACE_LINEAR
:
RenderLinear
(
p_filter
,
p_dst
[
0
],
p_pic
,
!
p_pic
->
b_top_field_first
);
RenderLinear
(
p_filter
,
p_dst
[
0
],
p_pic
,
!
b_top_field_first
);
if
(
p_dst
[
1
]
)
RenderLinear
(
p_filter
,
p_dst
[
1
],
p_pic
,
p_pic
->
b_top_field_first
);
RenderLinear
(
p_filter
,
p_dst
[
1
],
p_pic
,
b_top_field_first
);
if
(
p_dst
[
2
]
)
RenderLinear
(
p_filter
,
p_dst
[
2
],
p_pic
,
!
b_top_field_first
);
break
;
case
DEINTERLACE_MEAN
:
...
...
@@ -1604,24 +1817,58 @@ static picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
break
;
case
DEINTERLACE_YADIF2X
:
if
(
RenderYadif
(
p_filter
,
p_dst
[
0
],
p_pic
,
0
,
!
p_pic
->
b_top_field_first
)
)
if
(
RenderYadif
(
p_filter
,
p_dst
[
0
],
p_pic
,
0
,
!
b_top_field_first
)
)
goto
drop
;
if
(
p_dst
[
1
]
)
RenderYadif
(
p_filter
,
p_dst
[
1
],
p_pic
,
1
,
p_pic
->
b_top_field_first
);
RenderYadif
(
p_filter
,
p_dst
[
1
],
p_pic
,
1
,
b_top_field_first
);
if
(
p_dst
[
2
]
)
RenderYadif
(
p_filter
,
p_dst
[
2
],
p_pic
,
2
,
!
b_top_field_first
);
break
;
}
/* 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
.
pi_date
[
i_meta_idx
];
/* 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
)
{
/* 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
;
}
}
}
p_dst
[
0
]
->
b_progressive
=
true
;
if
(
p_dst
[
1
]
)
p_dst
[
1
]
->
b_progressive
=
true
;
for
(
int
i
=
1
;
i
<
DEINTERLACE_DST_SIZE
;
++
i
)
{
if
(
p_dst
[
i
]
)
p_dst
[
i
]
->
b_progressive
=
true
;
}
picture_Release
(
p_pic
);
return
p_dst
[
0
];
drop:
picture_Release
(
p_dst
[
0
]
);
if
(
p_dst
[
1
]
)
picture_Release
(
p_dst
[
1
]
);
for
(
int
i
=
1
;
i
<
DEINTERLACE_DST_SIZE
;
++
i
)
{
if
(
p_dst
[
i
]
)
picture_Release
(
p_dst
[
i
]
);
}
picture_Release
(
p_pic
);
return
NULL
;
}
...
...
@@ -1630,7 +1877,13 @@ static void Flush( filter_t *p_filter )
{
filter_sys_t
*
p_sys
=
p_filter
->
p_sys
;
p_sys
->
i_last_date
=
VLC_TS_INVALID
;
for
(
int
i
=
0
;
i
<
METADATA_SIZE
;
i
++
)
{
p_sys
->
meta
.
pi_date
[
i
]
=
VLC_TS_INVALID
;
p_sys
->
meta
.
pi_nb_fields
[
i
]
=
2
;
p_sys
->
meta
.
pb_top_field_first
[
i
]
=
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
]
)
...
...
@@ -1669,7 +1922,13 @@ static int Open( vlc_object_t *p_this )
p_sys
->
i_mode
=
DEINTERLACE_BLEND
;
p_sys
->
b_double_rate
=
false
;
p_sys
->
b_half_height
=
true
;
p_sys
->
i_last_date
=
VLC_TS_INVALID
;
for
(
int
i
=
0
;
i
<
METADATA_SIZE
;
i
++
)
{
p_sys
->
meta
.
pi_date
[
i
]
=
VLC_TS_INVALID
;
p_sys
->
meta
.
pi_nb_fields
[
i
]
=
2
;
p_sys
->
meta
.
pb_top_field_first
[
i
]
=
true
;
}
p_sys
->
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
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment