================================== FAILURES ===================================
_ TestVideoEncoder.test_video_encoder_against_ffmpeg_cli[30-to_file-encode_params2-flv] _
self = <test.test_encoders.TestVideoEncoder object at 0x000001D382E05950>
tmp_path = WindowsPath('C:/Users/runneradmin/AppData/Local/Temp/pytest-of-runneradmin/pytest-0/test_video_encoder_against_ffm14')
format = 'flv'
encode_params = {'crf': None, 'pixel_format': 'yuv420p', 'preset': 'ultrafast'}
method = 'to_file', frame_rate = 30
@needs_ffmpeg_cli
@pytest.mark.parametrize(
"format",
(
"mov",
"mp4",
"avi",
"mkv",
"flv",
pytest.param(
"webm",
marks=[
pytest.mark.slow,
pytest.mark.skipif(
ffmpeg_major_version == 4
or (IS_WINDOWS and ffmpeg_major_version >= 6),
reason="Codec for webm is not available in this FFmpeg installation.",
),
],
),
),
)
@pytest.mark.parametrize(
"encode_params",
[
{"pixel_format": "yuv444p", "crf": 0, "preset": None},
{"pixel_format": "yuv420p", "crf": 30, "preset": None},
{"pixel_format": "yuv420p", "crf": None, "preset": "ultrafast"},
{"pixel_format": "yuv420p", "crf": None, "preset": None},
],
)
@pytest.mark.parametrize("method", ("to_file", "to_tensor", "to_file_like"))
@pytest.mark.parametrize("frame_rate", [30, 29.97])
def test_video_encoder_against_ffmpeg_cli(
self, tmp_path, format, encode_params, method, frame_rate
):
pixel_format = encode_params["pixel_format"]
crf = encode_params["crf"]
preset = encode_params["preset"]
if format in ("avi", "flv") and pixel_format == "yuv444p":
pytest.skip(f"Default codec for {format} does not support {pixel_format}")
source_frames = self.decode(TEST_SRC_2_720P.path)
# Encode with FFmpeg CLI
temp_raw_path = str(tmp_path / "temp_input.raw")
with open(temp_raw_path, "wb") as f:
f.write(source_frames.permute(0, 2, 3, 1).cpu().numpy().tobytes())
ffmpeg_encoded_path = str(tmp_path / f"ffmpeg_output.{format}")
# Some codecs (ex. MPEG4) do not support CRF or preset.
# Flags not supported by the selected codec will be ignored.
ffmpeg_cmd = [
"ffmpeg",
"-y",
"-f",
"rawvideo",
"-pix_fmt",
"rgb24", # Input format
"-s",
f"{source_frames.shape[3]}x{source_frames.shape[2]}",
"-r",
str(frame_rate),
"-i",
temp_raw_path,
]
if pixel_format is not None: # Output format
ffmpeg_cmd.extend(["-pix_fmt", pixel_format])
if preset is not None:
ffmpeg_cmd.extend(["-preset", preset])
if crf is not None:
ffmpeg_cmd.extend(["-crf", str(crf)])
# Output path must be last
ffmpeg_cmd.append(ffmpeg_encoded_path)
subprocess.run(ffmpeg_cmd, check=True)
ffmpeg_frames = self.decode(ffmpeg_encoded_path).data
# Encode with our video encoder
encoder = VideoEncoder(frames=source_frames, frame_rate=frame_rate)
encoder_output_path = str(tmp_path / f"encoder_output.{format}")
if method == "to_file":
encoder.to_file(
dest=encoder_output_path,
pixel_format=pixel_format,
crf=crf,
preset=preset,
)
encoder_frames = self.decode(encoder_output_path)
elif method == "to_tensor":
encoded_output = encoder.to_tensor(
format=format,
pixel_format=pixel_format,
crf=crf,
preset=preset,
)
encoder_frames = self.decode(encoded_output)
elif method == "to_file_like":
file_like = io.BytesIO()
encoder.to_file_like(
file_like=file_like,
format=format,
pixel_format=pixel_format,
crf=crf,
preset=preset,
)
encoder_frames = self.decode(file_like.getvalue())
else:
raise ValueError(f"Unknown method: {method}")
assert ffmpeg_frames.shape[0] == encoder_frames.shape[0]
# MPEG codec used for avi format does not accept CRF
percentage = 94 if format == "avi" else 99
# Check that PSNR between both encoded versions is high
for ff_frame, enc_frame in zip(ffmpeg_frames, encoder_frames):
res = psnr(ff_frame, enc_frame)
assert res > 30
assert_tensor_close_on_at_least(
ff_frame, enc_frame, percentage=percentage, atol=2
)
# Only compare video metadata on ffmpeg versions >= 6, as older versions
# are often missing metadata
if ffmpeg_major_version >= 6 and method == "to_file":
fields = [
"duration",
"duration_ts",
"r_frame_rate",
"time_base",
"nb_frames",
]
ffmpeg_metadata = self._get_video_metadata(
ffmpeg_encoded_path,
fields=fields,
)
encoder_metadata = self._get_video_metadata(
encoder_output_path,
fields=fields,
)
assert ffmpeg_metadata == encoder_metadata
# Check that frame timestamps and duration are the same
fields = ("pts", "pts_time")
if format != "flv":
fields += ("duration", "duration_time")
> ffmpeg_frames_info = self._get_frames_info(
ffmpeg_encoded_path, fields=fields
)
test\test_encoders.py:1172:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
test\test_encoders.py:669: in _get_frames_info
frames = json.loads(result.stdout)["frames"]
^^^^^^^^^^^^^^^^^^^^^^^^^
C:\Users\runneradmin\miniconda3\envs\test\Lib\json\__init__.py:352: in loads
return _default_decoder.decode(s)
^^^^^^^^^^^^^^^^^^^^^^^^^^
C:\Users\runneradmin\miniconda3\envs\test\Lib\json\decoder.py:345: in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <json.decoder.JSONDecoder object at 0x000001D3E8A36E40>
s = '{\n "frames": [\n {\n "pts": 0,\n "pts_time": "0.000000"\n },\n {\n ... "pts_time": "0.933000"\n },\n {\n "pts": 967,\n "pts_time": "0.967000"\n }'
idx = 0
def raw_decode(self, s, idx=0):
"""Decode a JSON document from ``s`` (a ``str`` beginning with
a JSON document) and return a 2-tuple of the Python
representation and the index in ``s`` where the document ended.
This can be used to decode a JSON document from a string that may
have extraneous data at the end.
"""
try:
> obj, end = self.scan_once(s, idx)
^^^^^^^^^^^^^^^^^^^^^^
E json.decoder.JSONDecodeError: Expecting ',' delimiter: line 122 column 10 (char 2412)
C:\Users\runneradmin\miniconda3\envs\test\Lib\json\decoder.py:361: JSONDecodeError
---------------------------- Captured stderr call -----------------------------
ffmpeg version 8.0.1 Copyright (c) 2000-2025 the FFmpeg developers
built with clang version 22.1.0
configuration: --prefix=/d/bld/ffmpeg_1773007679189/_h_env/Library --cc=clang.exe --cxx=clang++.exe --nm=llvm-nm --ar=llvm-ar --disable-doc --enable-openssl --enable-demuxer=dash --enable-hardcoded-tables --enable-libfreetype --enable-libharfbuzz --enable-libfontconfig --enable-libopenh264 --enable-libdav1d --ld=lld-link --target-os=win64 --enable-cross-compile --toolchain=msvc --host-cc=clang.exe --extra-libs=ucrt.lib --extra-libs=vcruntime.lib --extra-libs=oldnames.lib --strip=llvm-strip --disable-stripping --host-extralibs= --disable-libopenvino --enable-gpl --enable-libx264 --enable-libx265 --enable-libmp3lame --enable-libaom --enable-libsvtav1 --enable-libxml2 --enable-pic --enable-shared --disable-static --enable-version3 --enable-zlib --enable-libvorbis --enable-libopus --enable-librsvg --enable-libjxl --enable-libwebp --enable-ffplay --enable-vulkan --enable-libshaderc --pkg-config=/d/bld/ffmpeg_1773007679189/_build_env/Library/bin/pkg-config
libavutil 60. 8.100 / 60. 8.100
libavcodec 62. 11.100 / 62. 11.100
libavformat 62. 3.100 / 62. 3.100
libavdevice 62. 1.100 / 62. 1.100
libavfilter 11. 4.100 / 11. 4.100
libswscale 9. 1.100 / 9. 1.100
libswresample 6. 1.100 / 6. 1.100
[rawvideo @ 00000230F0BD3FC0] Estimating duration from bitrate, this may be inaccurate
Input #0, rawvideo, from 'C:\Users\runneradmin\AppData\Local\Temp\pytest-of-runneradmin\pytest-0\test_video_encoder_against_ffm14\temp_input.raw':
Duration: 00:00:01.00, start: 0.000000, bitrate: 663552 kb/s
Stream #0:0: Video: rawvideo (RGB[24] / 0x18424752), rgb24, 1280x720, 663552 kb/s, 30 tbr, 30 tbn
[out#0/flv @ 00000230F0B97FC0] Codec AVOption preset (Encoding preset) has not been used for any stream. The most likely reason is either wrong type (e.g. a video option with no video streams) or that it is a private option of some decoder which was not actually used for any stream.
Stream mapping:
Stream #0:0 -> #0:0 (rawvideo (native) -> flv1 (flv))
Press [q] to stop, [?] for help
Output #0, flv, to 'C:\Users\runneradmin\AppData\Local\Temp\pytest-of-runneradmin\pytest-0\test_video_encoder_against_ffm14\ffmpeg_output.flv':
Metadata:
encoder : Lavf62.3.100
Stream #0:0: Video: flv1 ([2][0][0][0] / 0x0002), yuv420p(tv, progressive), 1280x720, q=2-31, 200 kb/s, 30 fps, 1k tbn
Metadata:
encoder : Lavc62.11.100 flv
Side data:
cpb: bitrate max/min/avg: 0/0/200000 buffer size: 0 vbv_delay: N/A
[out#0/flv @ 00000230F0B97FC0] video:334KiB audio:0KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: 0.201926%
frame= 30 fps=0.0 q=31.0 Lsize= 335KiB time=00:00:01.00 bitrate=2743.2kbits/s speed=9.94x elapsed=0:00:00.10
=========================== short test summary info ===========================
FAILED test/test_encoders.py::TestVideoEncoder::test_video_encoder_against_ffmpeg_cli[30-to_file-encode_params2-flv] - json.decoder.JSONDecodeError: Expecting ',' delimiter: line 122 column 10 (char 2412)
=========== 1 failed, 1217 passed, 596 skipped in 525.29s (0:08:45) ===========
Context:
We have encoder tests that rely on
ffprobeto compare our encoded media's metadata with ffmpeg's.TestAudioEncoder.test_against_cliusesvalidate_frames_propertiesTestVideoEncoder.test_video_encoder_against_ffmpeg_cliuses_get_frames_info-of jsonto return the metadata as a JSON.Problem:
These tests are flaky and sometimes fail on Windows + FFmpeg8 with the error:
json.decoder.JSONDecodeError: Expecting ',' delimiter.These tests normally pass after a retry, but currently they add noisy failures to PRs, so we should find and resolve the underlying cause, or switch to a more reliable output format such as ffprobe's default format, which we use in _get_video_metadata.
Example failures:
TestAudioEncoder::test_against_clion Windows + FFmpeg8, formp3andflac:Details
TestVideoEncoder::test_video_encoder_against_ffmpeg_clion Windows + FFmpeg8 forflv, probably others.Details