Skip to content

Commit

Permalink
ffmpeg/encoder: encode only N minus offset frames after seek
Browse files Browse the repository at this point in the history
FFmpeg will always encode N frames specified in the
-vframes option unless it hits input EOF before N
frames have been read.

We assumed that the input file to the test case only
contained N frames specified in the test case config.
Thus, we compensated the frame count when setting up
the metrics in seek cases... asserting that ffmpeg
always hit input EOF.

For example: input = 50 frames, case specifies 50
frames at 25 fps and seek = 1... ffmpeg will only
encode 25 frames even with "-vframes 50".

However, if the input file contains more frames than
specified in the test case configuration, then ffmpeg
will encode up to N frames after the seek.

For example: input = 150 frames, case specifies 50
frames at 25 fps and seek = 1... ffmpeg will encode
50 frames with "-vframes 50".

Unfortunately, we don't have a current method to
determine the "actual" length of the input file to
compensate this properly.

Therefore, to make it deterministic, we need to update
the test cases' frames property to "N - offset" so that
ffmpeg -vframes only encodes "N - offset" frames for
seek cases regardless of input length.  This also removes
the need to compensate the number of frames in the metrics
setup.

Fixes: VIZ-20687

Signed-off-by: U. Artie Eoff <[email protected]>
  • Loading branch information
uartie committed May 10, 2024
1 parent 2b35830 commit f8f499c
Showing 1 changed file with 14 additions and 12 deletions.
26 changes: 14 additions & 12 deletions lib/ffmpeg/encoderbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,17 @@ def validate_caps(self):
# FIXME: handle brframes for bitrate rcmodes (see ffmpeg/qsv/encoder.py)
# May require rebaseline for other components/elements

# ffmpeg default framerate is 25 fps
fps = vars(self).get("fps", 25)

# calculate frame offset to compensate for seek
offset = vars(self).get("seek", 0) * fps

# only encode remaining requested frames after seek
self.frames -= offset

assert self.frames > 0, "No more frames after applying seek... invalid test configuration"

self.encoder = self.EncoderClass(encoded_ext = self.get_file_ext(), **vars(self))
self.decoder = self.DecoderClass(
caps = self.caps,
Expand Down Expand Up @@ -245,11 +256,8 @@ def check_bitrate(self):
# ffmpeg default framerate is 25 fps
fps = vars(self).get("fps", 25)

# calculate frame offset to compensate for seek
offset = vars(self).get("seek", 0) * fps

encsize = os.path.getsize(self.encoder.encoded)
bitrate_actual = encsize * 8 * fps / 1024.0 / (self.frames - offset)
bitrate_actual = encsize * 8 * fps / 1024.0 / self.frames
get_media()._set_test_details(
size_encoded = encsize,
bitrate_actual = "{:-.2f}".format(bitrate_actual))
Expand Down Expand Up @@ -289,12 +297,6 @@ def check_metrics(self):
)
self.decoder.decode()

# ffmpeg default framerate is 25 fps
fps = vars(self).get("fps", 25)

# calculate frame offset to compensate for seek
offset = vars(self).get("seek", 0) * fps

# NOTE: Ref/filetrue seek compensation is not currently captured in this
# metric. The metric would need this information for its internal
# comparision functions. However, since we are exploiting the "inline"
Expand All @@ -305,9 +307,9 @@ def check_metrics(self):
filetrue = self.encoder.source,
filecoded = self.encoder.encoded,
filetest = self.decoder.decoded,
frames = self.frames - offset,
frames = self.frames,
)
metric.actual = parse_psnr_stats(self.decoder.statsfile, self.frames - offset)
metric.actual = parse_psnr_stats(self.decoder.statsfile, self.frames)

metric.check()

Expand Down

0 comments on commit f8f499c

Please sign in to comment.