Skip to content

Commit f28ba9e

Browse files
committed
lib/ffmpeg: compensate for seek in metrics
Signed-off-by: U. Artie Eoff <[email protected]>
1 parent 21c311d commit f28ba9e

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

lib/ffmpeg/decoderbase.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,17 @@ class Decoder(PropertyHandler, BaseFormatMapper):
2828
osdecoded = property(lambda s: filepath2os(s.decoded))
2929
hwaccel = property(lambda s: s.props["hwaccel"])
3030

31-
# optional properties
32-
ffdecoder = property(lambda s: s.ifprop("ffdecoder", "-c:v {ffdecoder}"))
33-
3431
width = property(lambda s: s.props["width"])
3532
height = property(lambda s: s.props["height"])
3633
statsfile = property(lambda s: s._statsfile)
3734
osstatsfile = property(lambda s: filepath2os(s.statsfile))
3835
reference = property(lambda s: s.props["reference"])
3936
osreference = property(lambda s: filepath2os(s.reference))
4037

38+
# optional properties
39+
ffdecoder = property(lambda s: s.ifprop("ffdecoder", "-c:v {ffdecoder}"))
40+
refseek = property(lambda s: s.ifprop("refseek", "-ss {refseek}"))
41+
4142
def __init__(self, scope = Scope.TEST, **properties):
4243
super().__init__(**properties)
4344
self._scope = scope
@@ -84,7 +85,7 @@ def decode(self):
8485
f"{exe2os('ffmpeg')} -v verbose {self.hwinit}"
8586
f" {self.ffdecoder} -r:v {fps} -i {self.ossource}"
8687
f" -f rawvideo -pix_fmt {self.format} -s:v {self.width}x{self.height}"
87-
f" -r:v {fps} -i {self.osreference}"
88+
f" -r:v {fps} {self.refseek} -i {self.osreference}"
8889
f" -lavfi \"{self.scale_range},{mtype}=f=\\'{self.osstatsfile}\\':shortest=1\""
8990
f" -fps_mode passthrough -noautoscale -vframes {self.frames} -f null -"
9091
)

lib/ffmpeg/encoderbase.py

+28-3
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,14 @@ def check_output(self):
243243
pass
244244

245245
def check_bitrate(self):
246+
# ffmpeg default framerate is 25 fps
247+
fps = vars(self).get("fps", 25)
248+
249+
# calculate frame offset to compensate for seek
250+
offset = vars(self).get("seek", 0) * fps
251+
246252
encsize = os.path.getsize(self.encoder.encoded)
247-
bitrate_actual = encsize * 8 * vars(self).get("fps", 25) / 1024.0 / self.frames
253+
bitrate_actual = encsize * 8 * fps / 1024.0 / (self.frames - offset)
248254
get_media()._set_test_details(
249255
size_encoded = encsize,
250256
bitrate_actual = "{:-.2f}".format(bitrate_actual))
@@ -271,20 +277,38 @@ def check_bitrate(self):
271277
def check_metrics(self):
272278
vars(self).setdefault("metric", dict(type = "psnr"))
273279

280+
# The decoder reference file (i.e. the encoder source file) includes all of
281+
# the frames without the seek. The encoder output file includes only the
282+
# frames after the seek. Therefore, the decoder should seek/advance the
283+
# reference frames before applying any frame comparison metrics
284+
# (e.g. PSNR, SSIM) on the decoder source file (i.e. encoder output file).
274285
self.decoder.update(
275286
reference = self.encoder.source,
276287
source = self.encoder.encoded,
277-
metric = self.metric
288+
metric = self.metric,
289+
refseek = vars(self).get("seek", 0),
278290
)
279291
self.decoder.decode()
280292

293+
# ffmpeg default framerate is 25 fps
294+
fps = vars(self).get("fps", 25)
295+
296+
# calculate frame offset to compensate for seek
297+
offset = vars(self).get("seek", 0) * fps
298+
299+
# NOTE: Ref/filetrue seek compensation is not currently captured in this
300+
# metric. The metric would need this information for its internal
301+
# comparision functions. However, since we are exploiting the "inline"
302+
# metric feature (i.e. setting metric.actual directly), the internal
303+
# comparison functions will be bypassed.
281304
metric = metrics2.factory.create(**vars(self))
282305
metric.update(
283306
filetrue = self.encoder.source,
284307
filecoded = self.encoder.encoded,
285308
filetest = self.decoder.decoded,
309+
frames = self.frames - offset,
286310
)
287-
metric.actual = parse_psnr_stats(self.decoder.statsfile, self.frames)
311+
metric.actual = parse_psnr_stats(self.decoder.statsfile, self.frames - offset)
288312

289313
metric.check()
290314

@@ -311,6 +335,7 @@ def check_forced_idr(self):
311335
f" -v verbose -i {self.encoder.osencoded} -c:v copy -bsf:v trace_headers"
312336
f" -f null - 2>&1 | grep 'nal_unit_type.*{judge}' | wc -l"
313337
)
338+
assert vars(self).get("seek", 0) == 0, "Seek is not currently supported with forced_idr"
314339
assert str(self.frames) == output.strip(), "It appears that the forced_idr did not work"
315340

316341
def check_max_frame_size(self):

0 commit comments

Comments
 (0)