diff mbox series

[FFmpeg-devel] Complete rewrite of the fps video filter section. More accurate.

Message ID 20200427061747.48843-1-list+ffmpeg-dev@jdlh.com
State New
Headers show
Series [FFmpeg-devel] Complete rewrite of the fps video filter section. More accurate.
Related show

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

Jim DeLaHunt April 27, 2020, 6:17 a.m. UTC
From: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>

This is a complete rewrite of the documentation for the "fps" video
filter. It describes the filter's behaviour more clearly and accurately.
I based the rewrite on reading the source code in vf_fps.c closely.

No code, or other documentation files, are touched by this change.

Signed-off-by: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
---
 doc/filters.texi | 167 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 149 insertions(+), 18 deletions(-)

Comments

Marton Balint April 27, 2020, 8:04 a.m. UTC | #1
On Sun, 26 Apr 2020, list+ffmpeg-dev@jdlh.com wrote:

> From: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
>
> This is a complete rewrite of the documentation for the "fps" video
> filter. It describes the filter's behaviour more clearly and accurately.
> I based the rewrite on reading the source code in vf_fps.c closely.

IMHO it is way too verbose now.

Regards,
Marton

>
> No code, or other documentation files, are touched by this change.
>
> Signed-off-by: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
> ---
> doc/filters.texi | 167 ++++++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 149 insertions(+), 18 deletions(-)
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 71a6787289..bd4a1ad2a9 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -11139,27 +11139,34 @@ format=pix_fmts=yuv420p|yuv444p|yuv410p
> @anchor{fps}
> @section fps
> 
> -Convert the video to specified constant frame rate by duplicating or dropping
> -frames as necessary.
> +Generate a video, having the specified constant frame rate, from the frames of 
> +the input video, by copying or duplicating or dropping input frames based on 
> +their input presentation time stamps (PTSs). The output video has new PTSs. You 
> +can choose the method for rounding from input PTS to output PTS.
> 
> It accepts the following parameters:
> @table @option
> 
> @item fps
> -The desired output frame rate. The default is @code{25}.
> +The output frame rate, as a number of frames per second. This value can be an 
> +integer, real, or rational number, or an abbreviation. The default is @code{25}.
> 
> @item start_time
> -Assume the first PTS should be the given value, in seconds. This allows for
> -padding/trimming at the start of stream. By default, no assumption is made
> -about the first frame's expected PTS, so no padding or trimming is done.
> -For example, this could be set to 0 to pad the beginning with duplicates of
> -the first frame if a video stream starts after the audio stream or to trim any
> -frames with a negative PTS.
> +The time, in seconds from the start of the input stream, which is converted to 
> +an input starting PTS value and an output starting PTS value. 
> +If set, @var{fps} drops input frames
> +which have PTS values less than the input starting PTS. If not set, the input 
> +and output starting PTS values are zero, but @var{fps} drops no input frames based 
> +on PTS. 
> +(See details below.)
> 
> @item round
> -Timestamp (PTS) rounding method.
> +Rounding method to use when calculating output Presentation Timestamp 
> +(PTS) integer values from input PTS values. If the calculated output PTS value
> +is not exactly an integer, then the method determines which of the two 
> +neighbouring integer values to choose.
> 
> -Possible values are:
> +Possible method names are:
> @table @option
> @item zero
> round towards 0
> @@ -11170,43 +11177,167 @@ round towards -infinity
> @item up
> round towards +infinity
> @item near
> -round to nearest
> +round to nearest (and if exactly at midpoint, away from 0)
> @end table
> The default is @code{near}.
> 
> @item eof_action
> -Action performed when reading the last frame.
> +Action which @var{fps} takes with the final input frame. The input video passes
> +in an ending input PTS, which @var{fps} converts to an ending output PTS. 
> +@var{fps} drops any input frames with a PTS at or after this ending PTS.
> 
> Possible values are:
> @table @option
> @item round
> -Use same timestamp rounding method as used for other frames.
> +Use same rounding method as for other frames, to convert the ending input PTS
> +to output PTS.
> +
> @item pass
> -Pass through last frame if input duration has not been reached yet.
> +Round the ending input PTS using @code{up}. This can have the effect of passing
> +through one last input frame. 
> @end table
> +
> The default is @code{round}.
> 
> @end table
> 
> -Alternatively, the options can be specified as a flat string:
> +Alternatively, the options may be specified as a flat string:
> @var{fps}[:@var{start_time}[:@var{round}]].
> 
> +@var{fps} generates an output video with integer Presentation Time Stamp (PTS) 
> +values which increment by one each output frame, and with a time base set to 
> +the inverse of the given frame rate. @var{fps} copies, duplicates, or drops 
> +input frames, in sequence, to the output video. It does so according to their 
> +input PTS values, as converted to seconds (via the input time base), then 
> +rounded to output PTS values. 
> +
> +@var{fps} sets output PTS values in terms of a time line which starts at
> +zero. The integer PTS value multipled by the output time base gives a point 
> +in seconds of that output frame on that timeline. If the @var{start_time} 
> +parameter is not set, or is zero, the first output frame's PTS value is zero. 
> +Otherwise, the first PTS value is the output starting PTS value calculated
> +from the @var{start_time} parameter. 
> +
> +@var{fps} interprets input PTS values in terms of the same time line. It 
> +multiplies the input PTS value by the input time base time, to get a frame 
> +position in seconds on the time line. It rounds that position to an integer 
> +output PTS value. For example, if the input video has a frame rate
> +of 30 fps, a time base of 1/30 seconds, and its first frame has a 
> +PTS of 300, then @var{fps} treats that frame as occurring 10 seconds (300 * 1/30 
> +seconds) after the start of the video, even though it is the first frame.
> +
> +Setting a @code{start_time} value allows for padding/trimming at the 
> +start of stream. For example, @code{start_time} could be set to 0 to pad the 
> +beginning with duplicates of the first frame if a video stream starts after 
> +the audio stream or to trim any frames with a negative PTS. When 
> +@code{start_time} is not set, the @var{fps} filter makes no assumption about 
> +the first frame's expected PTS, and does not pad or trim input frames which 
> +have a PTS set.
> +
> See also the @ref{setpts} filter.
> 
> +@subsection Details
> +
> +@var{fps} emits exactly one frame for each output PTS value. If there is 
> +exactly one input frame with an input PTS which converts to the current output 
> +PTS value, @var{fps} emits that frame. If there are multiple frames which 
> +convert to the same output PTS value, @var{fps} emits the final frame of that 
> +group, and drops the previous frames. If no input frame PTS converts to a given 
> +output PTS value, @var{fps} emits another copy of the previously emitted frame. 
> +When the first input frame converts to an output PTS after the first frame, then 
> +@var{fps} emits copies of that first frame until the output PTS reaches the 
> +converted value of that first frame's input PTS. 
> +
> +@var{fps} always drops input frames which have no PTS value set, regardless
> +of the @var{start_time} parameter. 
> +
> +The @var{frame rate} value may be provided in a variety of forms. Each form is
> +converted into a rational number, with an integer numerator and denominator. 
> +Each value must be zero or greater.
> +
> +@itemize
> +
> +@item
> +An integer number, e.g. @code{25}. This converts to the rational number 
> +@code{25/1}.
> +
> +@item
> +A real number, e.g. @code{3.14145926}. This converts to a rational number, 
> +e.g. @code{954708/303893}
> +
> +@item
> +A rational number. The numerator and denominator may be either integers or real
> +numbers. e.g. @code{30/1.001} or @code{30000/1001}, which both convert to 
> +@code{30000/1001}.
> +
> +@item 
> +An abbreviation. e.g @code{ntsc} as @code{30000/1001}, 
> +@code{ntsc-film} as @code{24000/1001}. See the complete list at 
> +@ref{Video rate,,the "Video rate" section in the ffmpeg-utils(1) manual,ffmpeg-utils}.
> +
> +@end itemize
> +
> +@var{fps} defines a sync point, where one input PTS value is treated as occuring
> +at the same moment as one output PTS value. IT calcluates other PTS values
> +as positive or negative time offsets from this sync point. 
> +This affects the details of how rounding works. If @var{start_time} is set, 
> +then the input and output PTS values which @var{fps} calculates from this 
> +become the sync point. Otherwise, input and output PTS values of zero are 
> +the sync point.
> +
> +Note that @var{fps} does not assume that input frames are separated by exactly 
> +1/frame_rate seconds. It takes the input PTS values literally. If the increment 
> +of PTS between frames varies along the video, fps will treat those frames as
> +happening at varying time intervals. 
> +
> +As a consequence, if you have a video which is supposed to be at a certain frame 
> +rate, but in reality the frames were not always captured at the exactly right 
> +moment, and if the input PTS values reflect that variation, then you can pass 
> +this video through an @var{fps} filter set to the same frame rate. A @var{round} 
> +method of @code{near} will pass through each input frame exactly once, and 
> +the output video will have new PTS values which reflect the exact time interval 
> +(1/frame rate) between frames.
> +
> +Because @var{fps} treats input PTS values at face value when converting them to
> +time on the time line, and because that time line starts at zero, an input 
> +video with PTS values that do not start at zero might yield unexpected results.
> +Suppose the input PTS values start with the value 300, which converts to 10
> +seconds after the sync point. Then @var{fps} will repeat the first frame of the
> +input video to fill the first 10 seconds of the output video. (However, 
> +@command{ffmpeg} may suppress those repeated frames, depending on the 
> +@option{-vsync} setting.) If you set @var{start_time} to 10 seconds, then 
> +@var{fps} sets the sync point to the PTS values converted from the 10 second 
> +moment on the time line. It no longer repeats the first frame. And, it starts 
> +the output PTS at the value corresponding to 10 seconds, instead of zero.
> +
> @subsection Examples
> 
> @itemize
> @item
> -A typical usage in order to set the fps to 25:
> +A typical usage in order to emit a video with a frame rate of 25 frames per second:
> @example
> fps=fps=25
> @end example
> 
> @item
> -Sets the fps to 24, using abbreviation and rounding method to round to nearest:
> +Emit a video with a frame rate of 24 frames per second, using an abbreviation, 
> +and rounding method to round to nearest:
> @example
> fps=fps=film:round=near
> @end example
> +
> +@item
> +If an input video is supposed to have a frame rate of 29.97 frames per second
> +(NTSC standard), but the time base is 3003/90000, and the PTS values increment 
> +variably at slightly more and less than that rate, this emits a video with the 
> +same frames, but with a new time base and PTS values that increment at exactly 
> +the NTSC rate. If some frames were dropped by the recorder, but the PTS values 
> +still reflect when the remaining frames were captured, this will also repeat 
> +frames to fill the gaps from the dropped frames.
> +@example
> +fps=fps=30/1.001:round=near
> +@end example
> +
> @end itemize
> 
> @section framepack
> -- 
> 2.26.2
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Jim DeLaHunt April 27, 2020, 8:37 a.m. UTC | #2
On 2020-04-27 01:04, Marton Balint wrote:

>
> On Sun, 26 Apr 2020, list+ffmpeg-dev@jdlh.com wrote:
>
>> From: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
>>
>> This is a complete rewrite of the documentation for the "fps" video
>> filter. It describes the filter's behaviour more clearly and accurately.
>> I based the rewrite on reading the source code in vf_fps.c closely.
>
> IMHO it is way too verbose now.
>
> Regards,
> Marton
>
I agree, there are a lot more words. On the other hand it now describes 
what the filter actually does, without forcing a user to read the code. 
(And the code isn't straightforward, there is an event-driven style to 
video filter code which takes a bit of learning.)

However, I'm confident it could be improved.

It would be useful for reviewers to comment on:

 1. Is there information which should be kept, but expressed with fewer
    words?
 2. Is there information which is true description of the filter, but
    which the documentation should not bother to disclose?

I'm of the opinion that FFmpeg users are impeded a lot more by necessary 
facts left out of documentation than by too many words left in.
>>
>> No code, or other documentation files, are touched by this change.
>>
>> Signed-off-by: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
Cheers,

       —Jim DeLaHunt, software engineer, Vancouver, Canada
Josh de Kock April 28, 2020, 2:33 p.m. UTC | #3
On 27/04/2020 07:17, list+ffmpeg-dev@jdlh.com wrote:
> From: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
> 
> This is a complete rewrite of the documentation for the "fps" video
> filter. It describes the filter's behaviour more clearly and accurately.
> I based the rewrite on reading the source code in vf_fps.c closely.
> 
> No code, or other documentation files, are touched by this change.
> 
> Signed-off-by: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
> ---
>   doc/filters.texi | 167 ++++++++++++++++++++++++++++++++++++++++++-----
>   1 file changed, 149 insertions(+), 18 deletions(-)
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 71a6787289..bd4a1ad2a9 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -11139,27 +11139,34 @@ format=pix_fmts=yuv420p|yuv444p|yuv410p
>   @anchor{fps}
>   @section fps
>   
> -Convert the video to specified constant frame rate by duplicating or dropping
> -frames as necessary.
> +Generate a video, having the specified constant frame rate, from the frames of
> +the input video, by copying or duplicating or dropping input frames based on
> +their input presentation time stamps (PTSs). The output video has new PTSs. You

Should just be `PTS'.

> +can choose the method for rounding from input PTS to output PTS.
>   
>   It accepts the following parameters:
>   @table @option
>   
>   @item fps
> -The desired output frame rate. The default is @code{25}.
> +The output frame rate, as a number of frames per second. This value can be an
> +integer, real, or rational number, or an abbreviation. The default is @code{25}.
>   

I don't think this change adds any value here.

>   @item start_time
> -Assume the first PTS should be the given value, in seconds. This allows for
> -padding/trimming at the start of stream. By default, no assumption is made
> -about the first frame's expected PTS, so no padding or trimming is done.
> -For example, this could be set to 0 to pad the beginning with duplicates of
> -the first frame if a video stream starts after the audio stream or to trim any
> -frames with a negative PTS.
> +The time, in seconds from the start of the input stream, which is converted to
> +an input starting PTS value and an output starting PTS value.
> +If set, @var{fps} drops input frames
> +which have PTS values less than the input starting PTS. If not set, the input
> +and output starting PTS values are zero, but @var{fps} drops no input frames based
> +on PTS.
> +(See details below.)
>   
>   @item round
> -Timestamp (PTS) rounding method.
> +Rounding method to use when calculating output Presentation Timestamp
> +(PTS) integer values from input PTS values. If the calculated output PTS value
> +is not exactly an integer, then the method determines which of the two
> +neighbouring integer values to choose.
>   
> -Possible values are:
> +Possible method names are:

They are still just values for the option.

>   @table @option
>   @item zero
>   round towards 0
> @@ -11170,43 +11177,167 @@ round towards -infinity
>   @item up
>   round towards +infinity
>   @item near
> -round to nearest
> +round to nearest (and if exactly at midpoint, away from 0)
>   @end table
>   The default is @code{near}.
>   
>   @item eof_action
> -Action performed when reading the last frame.
> +Action which @var{fps} takes with the final input frame. The input video passes
> +in an ending input PTS, which @var{fps} converts to an ending output PTS.
> +@var{fps} drops any input frames with a PTS at or after this ending PTS.
>   
>   Possible values are:
>   @table @option
>   @item round
> -Use same timestamp rounding method as used for other frames.
> +Use same rounding method as for other frames, to convert the ending input PTS
> +to output PTS.
> +
>   @item pass
> -Pass through last frame if input duration has not been reached yet.
> +Round the ending input PTS using @code{up}. This can have the effect of passing
> +through one last input frame.
>   @end table
> +
>   The default is @code{round}.
>   
>   @end table
>   
> -Alternatively, the options can be specified as a flat string:
> +Alternatively, the options may be specified as a flat string:
>   @var{fps}[:@var{start_time}[:@var{round}]].
>   
> +@var{fps} generates an output video with integer Presentation Time Stamp (PTS)

You already specify what PTS is above.

> +values which increment by one each output frame, and with a time base set to
> +the inverse of the given frame rate. @var{fps} copies, duplicates, or drops
> +input frames, in sequence, to the output video. It does so according to their
> +input PTS values, as converted to seconds (via the input time base), then
> +rounded to output PTS values.
> +
> +@var{fps} sets output PTS values in terms of a time line which starts at
> +zero. The integer PTS value multipled by the output time base gives a point

multiplied

> +in seconds of that output frame on that timeline. If the @var{start_time}
> +parameter is not set, or is zero, the first output frame's PTS value is zero.
> +Otherwise, the first PTS value is the output starting PTS value calculated
> +from the @var{start_time} parameter.
> +
> +@var{fps} interprets input PTS values in terms of the same time line. It
> +multiplies the input PTS value by the input time base time, to get a frame
> +position in seconds on the time line. It rounds that position to an integer
> +output PTS value. For example, if the input video has a frame rate
> +of 30 fps, a time base of 1/30 seconds, and its first frame has a
> +PTS of 300, then @var{fps} treats that frame as occurring 10 seconds (300 * 1/30
> +seconds) after the start of the video, even though it is the first frame.
> +
> +Setting a @code{start_time} value allows for padding/trimming at the
> +start of stream. For example, @code{start_time} could be set to 0 to pad the
> +beginning with duplicates of the first frame if a video stream starts after
> +the audio stream or to trim any frames with a negative PTS. When
> +@code{start_time} is not set, the @var{fps} filter makes no assumption about
> +the first frame's expected PTS, and does not pad or trim input frames which
> +have a PTS set.
> +
>   See also the @ref{setpts} filter.
>   
> +@subsection Details
> +
> +@var{fps} emits exactly one frame for each output PTS value. If there is
> +exactly one input frame with an input PTS which converts to the current output
> +PTS value, @var{fps} emits that frame. If there are multiple frames which
> +convert to the same output PTS value, @var{fps} emits the final frame of that
> +group, and drops the previous frames. If no input frame PTS converts to a given
> +output PTS value, @var{fps} emits another copy of the previously emitted frame.
> +When the first input frame converts to an output PTS after the first frame, then
> +@var{fps} emits copies of that first frame until the output PTS reaches the
> +converted value of that first frame's input PTS.
> +
> +@var{fps} always drops input frames which have no PTS value set, regardless
> +of the @var{start_time} parameter.
> +
> +The @var{frame rate} value may be provided in a variety of forms. Each form is
> +converted into a rational number, with an integer numerator and denominator.
> +Each value must be zero or greater.
> +
> +@itemize
> +
> +@item
> +An integer number, e.g. @code{25}. This converts to the rational number
> +@code{25/1}.
> +
> +@item
> +A real number, e.g. @code{3.14145926}. This converts to a rational number,
> +e.g. @code{954708/303893}
> +
> +@item
> +A rational number. The numerator and denominator may be either integers or real
> +numbers. e.g. @code{30/1.001} or @code{30000/1001}, which both convert to
> +@code{30000/1001}.
> +
> +@item
> +An abbreviation. e.g @code{ntsc} as @code{30000/1001},
> +@code{ntsc-film} as @code{24000/1001}. See the complete list at
> +@ref{Video rate,,the "Video rate" section in the ffmpeg-utils(1) manual,ffmpeg-utils}.
> +
> +@end itemize
> +
> +@var{fps} defines a sync point, where one input PTS value is treated as occuring

occurring

> +at the same moment as one output PTS value. IT calcluates other PTS values

calculates

> +as positive or negative time offsets from this sync point.
> +This affects the details of how rounding works. If @var{start_time} is set,
> +then the input and output PTS values which @var{fps} calculates from this
> +become the sync point. Otherwise, input and output PTS values of zero are
> +the sync point.
> +
> +Note that @var{fps} does not assume that input frames are separated by exactly
> +1/frame_rate seconds. It takes the input PTS values literally. If the increment
> +of PTS between frames varies along the video, fps will treat those frames as
> +happening at varying time intervals.
> +
> +As a consequence, if you have a video which is supposed to be at a certain frame
> +rate, but in reality the frames were not always captured at the exactly right
> +moment, and if the input PTS values reflect that variation, then you can pass
> +this video through an @var{fps} filter set to the same frame rate. A @var{round}
> +method of @code{near} will pass through each input frame exactly once, and
> +the output video will have new PTS values which reflect the exact time interval
> +(1/frame rate) between frames.
> +
> +Because @var{fps} treats input PTS values at face value when converting them to
> +time on the time line, and because that time line starts at zero, an input
> +video with PTS values that do not start at zero might yield unexpected results.
> +Suppose the input PTS values start with the value 300, which converts to 10
> +seconds after the sync point. Then @var{fps} will repeat the first frame of the
> +input video to fill the first 10 seconds of the output video. (However,
> +@command{ffmpeg} may suppress those repeated frames, depending on the
> +@option{-vsync} setting.) If you set @var{start_time} to 10 seconds, then
> +@var{fps} sets the sync point to the PTS values converted from the 10 second
> +moment on the time line. It no longer repeats the first frame. And, it starts
> +the output PTS at the value corresponding to 10 seconds, instead of zero.
> +
>   @subsection Examples
>   
>   @itemize
>   @item
> -A typical usage in order to set the fps to 25:
> +A typical usage in order to emit a video with a frame rate of 25 frames per second: >
>   @example
>   fps=fps=25
>   @end example
>   
>   @item
> -Sets the fps to 24, using abbreviation and rounding method to round to nearest:
> +Emit a video with a frame rate of 24 frames per second, using an abbreviation,
> +and rounding method to round to nearest:
>   @example
>   fps=fps=film:round=near
>   @end example
> +
> +@item
> +If an input video is supposed to have a frame rate of 29.97 frames per second
> +(NTSC standard), but the time base is 3003/90000, and the PTS values increment
> +variably at slightly more and less than that rate, this emits a video with the
> +same frames, but with a new time base and PTS values that increment at exactly
> +the NTSC rate. If some frames were dropped by the recorder, but the PTS values
> +still reflect when the remaining frames were captured, this will also repeat
> +frames to fill the gaps from the dropped frames.
> +@example
> +fps=fps=30/1.001:round=near
> +@end example
> +
>   @end itemize
>   
>   @section framepack
> 

Just a few nitpicks.

Also your usage of `emit' may be confusing to non-native English 
speakers and I don't think your writing has those people in mind 
particularly. I would just s/emit/output/ for the most part (check first 
this works).

Like Marton said, it's probably a bit on the verbose side, just have a 
read over and see if you can say the same thing with less words. I don't 
think being verbose is necessarily bad either, as you said there's less 
chance that a user would have to end up looking into code to know how to 
use it.
Carl Eugen Hoyos April 28, 2020, 3:50 p.m. UTC | #4
Am Mo., 27. Apr. 2020 um 08:18 Uhr schrieb <list+ffmpeg-dev@jdlh.com>:

The suggested patch is currently not ok.

> From: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
>
> This is a complete rewrite of the documentation for the "fps" video
> filter. It describes the filter's behaviour more clearly and accurately.
> I based the rewrite on reading the source code in vf_fps.c closely.
>
> No code, or other documentation files, are touched by this change.
>
> Signed-off-by: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
> ---
>  doc/filters.texi | 167 ++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 149 insertions(+), 18 deletions(-)
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 71a6787289..bd4a1ad2a9 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -11139,27 +11139,34 @@ format=pix_fmts=yuv420p|yuv444p|yuv410p
>  @anchor{fps}
>  @section fps
>
> -Convert the video to specified constant frame rate by duplicating or dropping
> -frames as necessary.
> +Generate a video, having the specified constant frame rate, from the frames of

I am not saying that "convert" is ideal but "Generate a video" is simply wrong.

> +the input video, by copying or duplicating or dropping input frames based on

What is the difference between "copying" and "duplicating"?

> +their input presentation time stamps (PTSs). The output video has new PTSs. You
> +can choose the method for rounding from input PTS to output PTS.
>
>  It accepts the following parameters:
>  @table @option
>
>  @item fps
> -The desired output frame rate. The default is @code{25}.
> +The output frame rate, as a number of frames per second. This value can be an
> +integer, real, or rational number, or an abbreviation. The default is @code{25}.
>
>  @item start_time
> -Assume the first PTS should be the given value, in seconds. This allows for
> -padding/trimming at the start of stream. By default, no assumption is made
> -about the first frame's expected PTS, so no padding or trimming is done.
> -For example, this could be set to 0 to pad the beginning with duplicates of
> -the first frame if a video stream starts after the audio stream or to trim any
> -frames with a negative PTS.

> +The time, in seconds from the start of the input stream, which is converted to
> +an input starting PTS value and an output starting PTS value.

> +If set, @var{fps} drops input frames
> +which have PTS values less than the input starting PTS. If not set, the input
> +and output starting PTS values are zero, but @var{fps} drops no input frames based
> +on PTS.

This is different than the explanation above and (hopefully) wrong.

> +(See details below.)
>
>  @item round
> -Timestamp (PTS) rounding method.
> +Rounding method to use when calculating output Presentation Timestamp
> +(PTS) integer values from input PTS values. If the calculated output PTS value
> +is not exactly an integer, then the method determines which of the two
> +neighbouring integer values to choose.

I do not see any improvement with this change.

> -Possible values are:
> +Possible method names are:

Not being a native speaker, the change makes this harder to read.
("Verschlimmbesserung")

>  @table @option
>  @item zero
>  round towards 0
> @@ -11170,43 +11177,167 @@ round towards -infinity
>  @item up
>  round towards +infinity
>  @item near
> -round to nearest

> +round to nearest (and if exactly at midpoint, away from 0)

I wonder if this implementation detail should be documented.
Are you sure that the current behaviour is intended and must
not change?

>  @end table
>  The default is @code{near}.
>
>  @item eof_action
> -Action performed when reading the last frame.
> +Action which @var{fps} takes with the final input frame.

"Verschlimmbesserung", see above.

> The input video passes
> +in an ending input PTS, which @var{fps} converts to an ending output PTS.

> +@var{fps} drops any input frames with a PTS at or after this ending PTS.

What is this supposed to mean / isn't this wrong?

>  Possible values are:
>  @table @option
>  @item round
> -Use same timestamp rounding method as used for other frames.
> +Use same rounding method as for other frames, to convert the ending input PTS
> +to output PTS.

Useless change.

> +
>  @item pass
> -Pass through last frame if input duration has not been reached yet.
> +Round the ending input PTS using @code{up}. This can have the effect of passing
> +through one last input frame.
>  @end table
> +
>  The default is @code{round}.
>
>  @end table
>
> -Alternatively, the options can be specified as a flat string:
> +Alternatively, the options may be specified as a flat string:
>  @var{fps}[:@var{start_time}[:@var{round}]].

The remaining part is far too long and not acceptable.

Allow me to repeat: The patch is currently not ok.

Carl Eugen
Jim DeLaHunt April 30, 2020, 7:47 p.m. UTC | #5
On 2020-04-28 07:33, Josh de Kock wrote:
> On 27/04/2020 07:17, list+ffmpeg-dev@jdlh.com wrote:
>> From: Jim DeLaHunt <from+ffmpeg-dev@jdlh.com>
>>
>> This is a complete rewrite of the documentation for the "fps" video
>> filter. It describes the filter's behaviour more clearly and accurately.
>> I based the rewrite on reading the source code in vf_fps.c closely.
>>
Thank you for your review, Josh. The patch will be the better for your 
comments.

My responses interleaved.

>>   @anchor{fps}
>>   @section fps
>>   -Convert the video to specified constant frame rate by duplicating 
>> or dropping
>> -frames as necessary.
>> +Generate a video, having the specified constant frame rate, from the 
>> frames of
>> +the input video, by copying or duplicating or dropping input frames 
>> based on
>> +their input presentation time stamps (PTSs). The output video has 
>> new PTSs. You
>
> Should just be `PTS'.

This brings up that I expand "PTS" to "presentation time stamp" three 
times in this patch, and my spacing and capitalisation are inconsistent 
across those three cases. I have deleted the later two expansions, and 
reduced my word count.

But this is the first use of the acronym PTS, and I think it is 
important to spell it out. If the FFmpeg documentation had a Glossary, I 
would like to point to "PTS" there. If there was a Concepts Guide that 
explained PTS, I could point readers there. But FFmpeg has neither. 
Without them, using acronyms without definition presents an obstacle to 
the reader which I think is unacceptable.

My scope is limited to this one section. I wish to spell out the acronym 
here. Later, if someone adds a Glossary or a Concepts Guide, then 
clean-up from that can include removing the expansion of PTS here. But 
not until then.

Yes, applying this principle would result in a lot more acronyms being 
expanded. This would be an improvement for FFmpeg users just trying to 
get their work done.


>> +can choose the method for rounding from input PTS to output PTS.
>>     It accepts the following parameters:
>>   @table @option
>>     @item fps
>> -The desired output frame rate. The default is @code{25}.
>> +The output frame rate, as a number of frames per second. This value 
>> can be an
>> +integer, real, or rational number, or an abbreviation. The default 
>> is @code{25}.
>
> I don't think this change adds any value here.

The first sentence defines what "frame rate" means. It is valuable here 
because of the lack of a Glossary. FFmpeg should, somewhere, define what 
"frame rate" means.  The existing language fails to define the term 
"frame rate".

FFmpeg should say that frame rate is per second, not per minute. It 
should say that frame rate counts frames per unit time, not time per 
frame (1/25). It is not a sin for documentation to state the obvious, 
because readers new to FFmpeg and its jargon will find everything to be 
obvious.

The second sentence is a quick summary of what types the value of the 
parameter may take. The existing language fails to do this. The types 
are fleshed out below, in the parameters table.

>>   @item start_time
>> -Assume the first PTS should be the given value, in seconds. This 
>> allows for
>> -padding/trimming at the start of stream. By default, no assumption 
>> is made
>> -about the first frame's expected PTS, so no padding or trimming is 
>> done.
>> -For example, this could be set to 0 to pad the beginning with 
>> duplicates of
>> -the first frame if a video stream starts after the audio stream or 
>> to trim any
>> -frames with a negative PTS.
>> +The time, in seconds from the start of the input stream, which is 
>> converted to
>> +an input starting PTS value and an output starting PTS value.
>> +If set, @var{fps} drops input frames
>> +which have PTS values less than the input starting PTS. If not set, 
>> the input
>> +and output starting PTS values are zero, but @var{fps} drops no 
>> input frames based
>> +on PTS.
>> +(See details below.)
>>     @item round
>> -Timestamp (PTS) rounding method.
>> +Rounding method to use when calculating output Presentation Timestamp
>> +(PTS) integer values from input PTS values. If the calculated output 
>> PTS value
>> +is not exactly an integer, then the method determines which of the two
>> +neighbouring integer values to choose.
>>   -Possible values are:
>> +Possible method names are:
>
> They are still just values for the option.

I think this is a wording style point.  This section uses the word 
"value" to indicate numerical quantities, and the strings passed to the 
`round` parameter are not numerical. It seems clearer to use a different 
word, and to tie it to "method". But if this is all that stands in the 
way of getting the patch accepted, I will concede.

>>   @table @option
…[snip]…
>>   -Alternatively, the options can be specified as a flat string:
>> +Alternatively, the options may be specified as a flat string:
>>   @var{fps}[:@var{start_time}[:@var{round}]].
>>   +@var{fps} generates an output video with integer Presentation Time 
>> Stamp (PTS)
>
> You already specify what PTS is above.
Agreed. I don't think it's harmful, but people want me to reduce my word 
count. Expansion deleted.
>> +values which increment by one each output frame, and with a time 
>> base set to
>> +the inverse of the given frame rate. @var{fps} copies, duplicates, 
>> or drops
>> +input frames, in sequence, to the output video. It does so according 
>> to their
>> +input PTS values, as converted to seconds (via the input time base), 
>> then
>> +rounded to output PTS values.
>> +
>> +@var{fps} sets output PTS values in terms of a time line which 
>> starts at
>> +zero. The integer PTS value multipled by the output time base gives 
>> a point
>
> multiplied … occurring … calculates …

Thank you for pointing out these spelling errors. Corrected.

…[snip]…

>> +
>>   @end itemize
>>     @section framepack
>>
>
> Just a few nitpicks.
Thank you.
>
> Also your usage of `emit' may be confusing to non-native English 
> speakers and I don't think your writing has those people in mind 
> particularly. I would just s/emit/output/ for the most part (check 
> first this works).
I see your point. I need to consider which verbs I use, and see if I can 
choose clearer ones.
> Like Marton said, it's probably a bit on the verbose side, just have a 
> read over and see if you can say the same thing with less words. I 
> don't think being verbose is necessarily bad either, as you said 
> there's less chance that a user would have to end up looking into code 
> to know how to use it.

Thank you. I will submit a revised patch, with fewer words.

Best regards,
       —Jim DeLaHunt, software engineer, Vancouver Canada
Jim DeLaHunt April 30, 2020, 9:09 p.m. UTC | #6
On 2020-04-28 08:50, Carl Eugen Hoyos wrote:

> Am Mo., 27. Apr. 2020 um 08:18 Uhr schrieb <list+ffmpeg-dev@jdlh.com>:
>
> The suggested patch is currently not ok.
Thank you for your review, Carl Eugen. The patch will be the better for 
your comments.

My responses interleaved.

…[snip]…

>> -Convert the video to specified constant frame rate by duplicating or dropping
>> -frames as necessary.
>> +Generate a video, having the specified constant frame rate, from the frames of
> I am not saying that "convert" is ideal but "Generate a video" is simply wrong.

I will look at all the word choices for verbs and see if I can make this 
clearer.

The word "generate" was a response to the the original text using the 
word "convert". "Convert" gave me the impression that the filter might 
just change time base and frame rate metadata parameters, but leave the 
rest unchanged. The *fps* filter does more than that. It alters the PTS 
values. It can add frames. It can drop frames. To my reading the output 
video is a new thing, not the original thing with some tweaks.

>> +the input video, by copying or duplicating or dropping input frames based on
> What is the difference between "copying" and "duplicating"?

Good point. I will look harder at these word choices.

What I am trying to convey is that the filter can do one of three things 
to the input frames: pass them through, or drop some of them, or repeat 
some of them. I will look for words which communicate this three-way path.

>> +their input presentation time stamps (PTSs). The output video has new PTSs. You
>> +can choose the method for rounding from input PTS to output PTS.
>>
>>   It accepts the following parameters:
>>   @table @option
>>
>>   @item fps
>> -The desired output frame rate. The default is @code{25}.
>> +The output frame rate, as a number of frames per second. This value can be an
>> +integer, real, or rational number, or an abbreviation. The default is @code{25}.
>>
>>   @item start_time
>> -Assume the first PTS should be the given value, in seconds. This allows for
>> -padding/trimming at the start of stream. By default, no assumption is made
>> -about the first frame's expected PTS, so no padding or trimming is done.
>> -For example, this could be set to 0 to pad the beginning with duplicates of
>> -the first frame if a video stream starts after the audio stream or to trim any
>> -frames with a negative PTS.
>> +The time, in seconds from the start of the input stream, which is converted to
>> +an input starting PTS value and an output starting PTS value.
>> +If set, @var{fps} drops input frames
>> +which have PTS values less than the input starting PTS. If not set, the input
>> +and output starting PTS values are zero, but @var{fps} drops no input frames based
>> +on PTS.
> This is different than the explanation above and (hopefully) wrong.

I based my new text on reading the code. Take a look for yourself: 
ffmpeg/libavfilter/vf_fps.c:159-174 
<https://github.com/FFmpeg/FFmpeg/blob/1128aa875367f66ac11adc30364d5652919a2591/libavfilter/vf_fps.c#L159-L174>, 
within config_props(), and ffmpeg/libavfilter/vf_fps.c:195-204 
<https://github.com/FFmpeg/FFmpeg/blob/1128aa875367f66ac11adc30364d5652919a2591/libavfilter/vf_fps.c#L195-L204>, 
within read_frame().

I think the first sentence of the old text, "Assume the first PTS should 
be the given value", doesn't match what the code does. The code does not 
include a clear high-level statement of what the parameter does. I think 
that means we have to take the implementation as correct, and document 
what it actually does.

The rest of the old text is actually a guide on how to use the 
`start_time` parameter. I kept that, but moved it down to after the 
parameter definition table.

>> +(See details below.)
>>
>>   @item round
>> -Timestamp (PTS) rounding method.
>> +Rounding method to use when calculating output Presentation Timestamp
>> +(PTS) integer values from input PTS values. If the calculated output PTS value
>> +is not exactly an integer, then the method determines which of the two
>> +neighbouring integer values to choose.
> I do not see any improvement with this change.

The old text fails to answer these questions: what is being rounded? 
What is the calculation which leads to the result being rounded? What is 
the frame of reference for "towards 0", "towards infinity"? What 
possible values does the rounding choose among? The new text tries to 
answer those questions.

There may be a better way to answer these questions. I will try to think 
of better wording. But I think the documentation should provide answers.

>> -Possible values are:
>> +Possible method names are:
> Not being a native speaker, the change makes this harder to read.
> ("Verschlimmbesserung")
As I replied to Josh, I think this is a wording style point.  This 
section uses the word "value" to indicate numerical quantities, and the 
strings passed to the `round` parameter are not numerical. It seems 
clearer to use a different word, and to tie it to "method". But if this 
is all that stands in the way of getting the patch accepted, I will concede.
>>   @table @option
>>   @item zero
>>   round towards 0
>> @@ -11170,43 +11177,167 @@ round towards -infinity
>>   @item up
>>   round towards +infinity
>>   @item near
>> -round to nearest
>> +round to nearest (and if exactly at midpoint, away from 0)
> I wonder if this implementation detail should be documented.
> Are you sure that the current behaviour is intended and must
> not change?

Again, the code does not provide a high-level statement of what it 
intends. It does not separate essence of interface from accident of 
implementation. However, one clue is that the `near` value for `round` 
corresponds to AV_ROUND_NEAR_INF, which is defined in 
ffmpeg/libavutil/mathematics.c:84 
<https://github.com/FFmpeg/FFmpeg/blob/a0ac49e38ee1d1011c394d7be67d0f08b2281526/libavutil/mathematics.h#L84>, 
where it says:

AV_ROUND_NEAR_INF = 5, ///< Round to nearest and halfway cases away from zero.

So, is that comment in *mathematics.c* an interface specification?  Is 
the use of that value in *vf_fps.c* an interface specification? It sure 
would be nice if the code had a high-level statement of intent. But it 
seems not to.

I don't feel strongly about this point. If you all tell me to leave this 
halfway case behaviour out of the documentation, I can live with that.

>>   @end table
>>   The default is @code{near}.
>>
>>   @item eof_action
>> -Action performed when reading the last frame.
>> +Action which @var{fps} takes with the final input frame.
> "Verschlimmbesserung", see above.

The new text makes two changes to the old:

1. The new text is clear that the last /input/ frame, not output frame, 
is what the parameter controls. The old text is ambiguous.

2. The new text uses the active voice ("@var{fps} takes"). The old text 
uses "when" plus a present progressive clause. The style guides I 
remember say that active voice is more lively than indirect 
constructions. In any case, it looks like the old text should have used 
"while" instead of "when" 
<https://english.stackexchange.com/a/17318/33109>.  (Thank you for 
leading me to a discussion of subject complements, by the way.)

I care about #1, because I want the documentation to say what the filter 
does. I am flexible on #2.

>
>> The input video passes
>> +in an ending input PTS, which @var{fps} converts to an ending output PTS.
>> +@var{fps} drops any input frames with a PTS at or after this ending PTS.
> What is this supposed to mean / isn't this wrong?

I guess the wording is unclear. The code is even more unclear.

Take a look: ffmpeg/libavfilter/vf_fps.c:234-245 
<https://github.com/FFmpeg/FFmpeg/blob/1128aa875367f66ac11adc30364d5652919a2591/libavfilter/vf_fps.c#L234-L245>, 
within write_frame(). There is a comment, "If we have status (EOF) set, 
drop frames when we hit the status timestamp." I have a hard time 
relating it to what the code does.

If you can clarify what the code a) actually does, and b) is supposed to 
do, I am glad to try to describe that more clearly.

>>   Possible values are:
>>   @table @option
>>   @item round
>> -Use same timestamp rounding method as used for other frames.
>> +Use same rounding method as for other frames, to convert the ending input PTS
>> +to output PTS.
> Useless change.

"Useless"? No, it restates for clarity what the rounding operates on.

However, "not very useful, and not worth the cost in extra words"?  
Maybe so. I will take another look at these words when I edit to reduce 
word count.

>> +
>>   @item pass
>> -Pass through last frame if input duration has not been reached yet.
>> +Round the ending input PTS using @code{up}. This can have the effect of passing
>> +through one last input frame.
>>   @end table
>> +
>>   The default is @code{round}.
>>
>>   @end table
>>
>> -Alternatively, the options can be specified as a flat string:
>> +Alternatively, the options may be specified as a flat string:
>>   @var{fps}[:@var{start_time}[:@var{round}]].
> The remaining part is far too long and not acceptable.

By "the remaining part" I understand you to mean the remaining three 
paragraphs in the main section, and the entire subsection "Details". I 
presume you still want to keep the "Examples" subsection.

I think what I put in those paragraphs is all true. It all accurately 
describes the code. It all explains points which I found to be necessary 
to understand as I was trying, as a user, to figure out if the *fps* 
filter would accomplish a purpose I wanted to do.

> Allow me to repeat: The patch is currently not ok.

Your earlier comments are about specific points. Some I agree with, some 
I disagree with. But I can imagine working with you to resolve them.

Your final comment, "The remaining part is far too long and not 
acceptable", comes across as a rejection of the very idea of improving 
*FFmpeg* documentation.

*FFmpeg* is a complex tool. Even apparently simple filters are complex. 
The present documentation is inadequate to explain to a user how the 
code behaves. This presents a big obstacle for *FFmpeg* users. Traffic 
on the ffmpeg-users list proves this constantly.

What do you have against removing this obstacle? What do you have 
against documentation which describes what the code actually does?

I will go through all these comments and make a revised patch. Let's see 
how that looks.

> Carl Eugen
Best regards,

      —Jim DeLaHunt, software engineer, Vancouver, Canada
Jim DeLaHunt May 1, 2020, 3:51 a.m. UTC | #7
Thank you for the review. Here is a rewrite of the improvement to the fps filter
documentation, based on those comments.  Thought it was too wordy before? It now 
has 11% fewer words.                             

To confirm, no other documentation and no code are affected by this patch.

Comments?
    —Jim DeLaHunt, software engineer, Vancouver, Canada
diff mbox series

Patch

diff --git a/doc/filters.texi b/doc/filters.texi
index 71a6787289..bd4a1ad2a9 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -11139,27 +11139,34 @@  format=pix_fmts=yuv420p|yuv444p|yuv410p
 @anchor{fps}
 @section fps
 
-Convert the video to specified constant frame rate by duplicating or dropping
-frames as necessary.
+Generate a video, having the specified constant frame rate, from the frames of 
+the input video, by copying or duplicating or dropping input frames based on 
+their input presentation time stamps (PTSs). The output video has new PTSs. You 
+can choose the method for rounding from input PTS to output PTS. 
 
 It accepts the following parameters:
 @table @option
 
 @item fps
-The desired output frame rate. The default is @code{25}.
+The output frame rate, as a number of frames per second. This value can be an 
+integer, real, or rational number, or an abbreviation. The default is @code{25}.
 
 @item start_time
-Assume the first PTS should be the given value, in seconds. This allows for
-padding/trimming at the start of stream. By default, no assumption is made
-about the first frame's expected PTS, so no padding or trimming is done.
-For example, this could be set to 0 to pad the beginning with duplicates of
-the first frame if a video stream starts after the audio stream or to trim any
-frames with a negative PTS.
+The time, in seconds from the start of the input stream, which is converted to 
+an input starting PTS value and an output starting PTS value. 
+If set, @var{fps} drops input frames
+which have PTS values less than the input starting PTS. If not set, the input 
+and output starting PTS values are zero, but @var{fps} drops no input frames based 
+on PTS. 
+(See details below.)
 
 @item round
-Timestamp (PTS) rounding method.
+Rounding method to use when calculating output Presentation Timestamp 
+(PTS) integer values from input PTS values. If the calculated output PTS value
+is not exactly an integer, then the method determines which of the two 
+neighbouring integer values to choose.
 
-Possible values are:
+Possible method names are:
 @table @option
 @item zero
 round towards 0
@@ -11170,43 +11177,167 @@  round towards -infinity
 @item up
 round towards +infinity
 @item near
-round to nearest
+round to nearest (and if exactly at midpoint, away from 0)
 @end table
 The default is @code{near}.
 
 @item eof_action
-Action performed when reading the last frame.
+Action which @var{fps} takes with the final input frame. The input video passes
+in an ending input PTS, which @var{fps} converts to an ending output PTS. 
+@var{fps} drops any input frames with a PTS at or after this ending PTS.
 
 Possible values are:
 @table @option
 @item round
-Use same timestamp rounding method as used for other frames.
+Use same rounding method as for other frames, to convert the ending input PTS
+to output PTS.
+
 @item pass
-Pass through last frame if input duration has not been reached yet.
+Round the ending input PTS using @code{up}. This can have the effect of passing
+through one last input frame. 
 @end table
+
 The default is @code{round}.
 
 @end table
 
-Alternatively, the options can be specified as a flat string:
+Alternatively, the options may be specified as a flat string:
 @var{fps}[:@var{start_time}[:@var{round}]].
 
+@var{fps} generates an output video with integer Presentation Time Stamp (PTS) 
+values which increment by one each output frame, and with a time base set to 
+the inverse of the given frame rate. @var{fps} copies, duplicates, or drops 
+input frames, in sequence, to the output video. It does so according to their 
+input PTS values, as converted to seconds (via the input time base), then 
+rounded to output PTS values. 
+
+@var{fps} sets output PTS values in terms of a time line which starts at
+zero. The integer PTS value multipled by the output time base gives a point 
+in seconds of that output frame on that timeline. If the @var{start_time} 
+parameter is not set, or is zero, the first output frame's PTS value is zero. 
+Otherwise, the first PTS value is the output starting PTS value calculated
+from the @var{start_time} parameter. 
+
+@var{fps} interprets input PTS values in terms of the same time line. It 
+multiplies the input PTS value by the input time base time, to get a frame 
+position in seconds on the time line. It rounds that position to an integer 
+output PTS value. For example, if the input video has a frame rate
+of 30 fps, a time base of 1/30 seconds, and its first frame has a 
+PTS of 300, then @var{fps} treats that frame as occurring 10 seconds (300 * 1/30 
+seconds) after the start of the video, even though it is the first frame.
+
+Setting a @code{start_time} value allows for padding/trimming at the 
+start of stream. For example, @code{start_time} could be set to 0 to pad the 
+beginning with duplicates of the first frame if a video stream starts after 
+the audio stream or to trim any frames with a negative PTS. When 
+@code{start_time} is not set, the @var{fps} filter makes no assumption about 
+the first frame's expected PTS, and does not pad or trim input frames which 
+have a PTS set.
+
 See also the @ref{setpts} filter.
 
+@subsection Details
+
+@var{fps} emits exactly one frame for each output PTS value. If there is 
+exactly one input frame with an input PTS which converts to the current output 
+PTS value, @var{fps} emits that frame. If there are multiple frames which 
+convert to the same output PTS value, @var{fps} emits the final frame of that 
+group, and drops the previous frames. If no input frame PTS converts to a given 
+output PTS value, @var{fps} emits another copy of the previously emitted frame. 
+When the first input frame converts to an output PTS after the first frame, then 
+@var{fps} emits copies of that first frame until the output PTS reaches the 
+converted value of that first frame's input PTS. 
+
+@var{fps} always drops input frames which have no PTS value set, regardless
+of the @var{start_time} parameter. 
+
+The @var{frame rate} value may be provided in a variety of forms. Each form is
+converted into a rational number, with an integer numerator and denominator. 
+Each value must be zero or greater.
+
+@itemize
+
+@item
+An integer number, e.g. @code{25}. This converts to the rational number 
+@code{25/1}.
+
+@item
+A real number, e.g. @code{3.14145926}. This converts to a rational number, 
+e.g. @code{954708/303893}
+
+@item
+A rational number. The numerator and denominator may be either integers or real
+numbers. e.g. @code{30/1.001} or @code{30000/1001}, which both convert to 
+@code{30000/1001}.
+
+@item 
+An abbreviation. e.g @code{ntsc} as @code{30000/1001}, 
+@code{ntsc-film} as @code{24000/1001}. See the complete list at 
+@ref{Video rate,,the "Video rate" section in the ffmpeg-utils(1) manual,ffmpeg-utils}.
+
+@end itemize
+
+@var{fps} defines a sync point, where one input PTS value is treated as occuring
+at the same moment as one output PTS value. IT calcluates other PTS values
+as positive or negative time offsets from this sync point. 
+This affects the details of how rounding works. If @var{start_time} is set, 
+then the input and output PTS values which @var{fps} calculates from this 
+become the sync point. Otherwise, input and output PTS values of zero are 
+the sync point.
+
+Note that @var{fps} does not assume that input frames are separated by exactly 
+1/frame_rate seconds. It takes the input PTS values literally. If the increment 
+of PTS between frames varies along the video, fps will treat those frames as
+happening at varying time intervals. 
+
+As a consequence, if you have a video which is supposed to be at a certain frame 
+rate, but in reality the frames were not always captured at the exactly right 
+moment, and if the input PTS values reflect that variation, then you can pass 
+this video through an @var{fps} filter set to the same frame rate. A @var{round} 
+method of @code{near} will pass through each input frame exactly once, and 
+the output video will have new PTS values which reflect the exact time interval 
+(1/frame rate) between frames.
+
+Because @var{fps} treats input PTS values at face value when converting them to
+time on the time line, and because that time line starts at zero, an input 
+video with PTS values that do not start at zero might yield unexpected results.
+Suppose the input PTS values start with the value 300, which converts to 10
+seconds after the sync point. Then @var{fps} will repeat the first frame of the
+input video to fill the first 10 seconds of the output video. (However, 
+@command{ffmpeg} may suppress those repeated frames, depending on the 
+@option{-vsync} setting.) If you set @var{start_time} to 10 seconds, then 
+@var{fps} sets the sync point to the PTS values converted from the 10 second 
+moment on the time line. It no longer repeats the first frame. And, it starts 
+the output PTS at the value corresponding to 10 seconds, instead of zero.
+
 @subsection Examples
 
 @itemize
 @item
-A typical usage in order to set the fps to 25:
+A typical usage in order to emit a video with a frame rate of 25 frames per second:
 @example
 fps=fps=25
 @end example
 
 @item
-Sets the fps to 24, using abbreviation and rounding method to round to nearest:
+Emit a video with a frame rate of 24 frames per second, using an abbreviation, 
+and rounding method to round to nearest:
 @example
 fps=fps=film:round=near
 @end example
+
+@item
+If an input video is supposed to have a frame rate of 29.97 frames per second
+(NTSC standard), but the time base is 3003/90000, and the PTS values increment 
+variably at slightly more and less than that rate, this emits a video with the 
+same frames, but with a new time base and PTS values that increment at exactly 
+the NTSC rate. If some frames were dropped by the recorder, but the PTS values 
+still reflect when the remaining frames were captured, this will also repeat 
+frames to fill the gaps from the dropped frames.
+@example
+fps=fps=30/1.001:round=near
+@end example
+
 @end itemize
 
 @section framepack