From ac28cdc4784eb7ccc860cb702db156e080458838 Mon Sep 17 00:00:00 2001 From: ekisu Date: Fri, 11 Sep 2026 13:40:38 -0300 Subject: [PATCH 1/3] Fix strict filesize minrate/maxrate using undefined bitrate calculate_bitrate returns video_bitrate/audio_bitrate, but the strict_filesize_constraint block still interpolated an undefined bitrate variable, producing minrate=nilk/maxrate=nilk which ffmpeg cannot parse. Select the bitrate of the constrained stream (video for video formats, audio for audio-only). --- src/encode.moon | 5 +- tests/testcases/test_strict_filesize.py | 167 ++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 tests/testcases/test_strict_filesize.py diff --git a/src/encode.moon b/src/encode.moon index 5dc2bed..6121ee5 100644 --- a/src/encode.moon +++ b/src/encode.moon @@ -341,9 +341,10 @@ encode = (region, startTime, endTime) -> if options.strict_filesize_constraint type = format.videoCodec != "" and "ovc" or "oac" + strict_bitrate = format.videoCodec != "" and video_bitrate or audio_bitrate append(command, { - "--#{type}opts-add=minrate=#{bitrate}k", - "--#{type}opts-add=maxrate=#{bitrate}k", + "--#{type}opts-add=minrate=#{strict_bitrate}k", + "--#{type}opts-add=maxrate=#{strict_bitrate}k", }) else type = format.videoCodec != "" and "ovc" or "oac" diff --git a/tests/testcases/test_strict_filesize.py b/tests/testcases/test_strict_filesize.py new file mode 100644 index 0000000..ae2c11b --- /dev/null +++ b/tests/testcases/test_strict_filesize.py @@ -0,0 +1,167 @@ +import json +import re +import subprocess +import tempfile +import time +import unittest +import uuid +from pathlib import Path + +from tests.mpv_ipc import MpvIPC + + +REPO_ROOT = Path(__file__).resolve().parents[2] +ENCODE_MOON = REPO_ROOT / "src" / "encode.moon" +TEST_VIDEO = REPO_ROOT / "tests" / "videos" / "big_buck_bunny_10s.mp4" +# 10s clip, so target_filesize=1000kB means 1000*8/10 = 800kbit/s. +TARGET_FILESIZE = 1000 +CLIP_LENGTH = 10 +EXPECTED_BITRATE = TARGET_FILESIZE * 8 // CLIP_LENGTH + + +def read_source(): + return ENCODE_MOON.read_text(encoding="utf-8") + + +def strict_block(source): + """Return the strict minrate/maxrate branch body inside encode().""" + # NOTE: calculate_bitrate() also mentions strict_filesize_constraint, so + # anchor on the bare `if` line (no trailing `and ...`) used by encode(). + match = re.search(r"if options\.strict_filesize_constraint\r?\n", source) + if match is None: + raise AssertionError("strict_filesize_constraint block not found") + start = match.start() + end = source.find("\n\t\telse", start) + if end == -1: + raise AssertionError("end of strict block not found") + return source[start:end] + + +def select_strict_bitrate(has_video_codec, video_bitrate, audio_bitrate): + """Python mirror of the strict-bitrate selection in src/encode.moon.""" + return video_bitrate if has_video_codec else audio_bitrate + + +def run_strict_encode_command_line(testcase, output_format): + """Boot mpv (stdout drained) and capture the strict encode command line. + + Returns the logged `Command line: mpv ...` string. The encode itself is + not awaited; the child is terminated once the command line is captured. + """ + script = REPO_ROOT / "build" / "webm.lua" + testcase.assertTrue(script.exists(), "build/webm.lua missing; run make first") + testcase.assertTrue(TEST_VIDEO.exists(), f"test video missing: {TEST_VIDEO}") + + tmpdir = tempfile.mkdtemp(prefix="mpv-webm-strict-test-") + socket_address = f"/tmp/mpvsocket-strict-{uuid.uuid4()}" + log_path = Path(tmpdir) / "mpv.log" + args = [ + "mpv", "--no-config", "--vo=null", "--ao=null", + "--load-scripts=no", "--idle=yes", + f"--input-ipc-server={socket_address}", + f"--log-file={log_path}", "-v", + ] + # Drain stdout to a file: mpv -v over a PIPE stalls and hangs the test. + with open(Path(tmpdir) / "stdout.log", "wb") as out: + proc = subprocess.Popen(args, stdout=out, stderr=subprocess.STDOUT) + try: + ipc = MpvIPC() + ipc.connect(socket_address) + ipc.start() + try: + ipc.send_command({"command": ["enable_event", "client-message"]}) + ipc.send_command({"command": ["load-script", str(script)]}) + testcase.assertTrue( + ipc.wait_for_event("webm-script-loaded", 15), + "script did not load", + ) + ipc.send_command({"command": ["script-message", + "mpv-webm-set-options", json.dumps({ + "output_format": output_format, + "target_filesize": TARGET_FILESIZE, + "strict_filesize_constraint": True, + "twopass": False, + "run_detached": False, + "display_progress": "no", + "output_directory": tmpdir, + })]}) + ipc.send_command({"command": ["loadfile", str(TEST_VIDEO), "replace"]}) + testcase.assertTrue( + ipc.wait_for_event("file-loaded", 15), "file did not load") + ipc.send_command({"command": ["keypress", "Shift+W"]}) + testcase.assertTrue( + ipc.wait_for_event("webm-show-main-page", 15), + "main page did not open", + ) + time.sleep(1) + ipc.send_command({"command": ["keypress", "e"]}) + deadline = time.time() + 60 + while time.time() < deadline: + text = log_path.read_text(errors="replace") \ + if log_path.exists() else "" + match = re.search(r"Command line: mpv .*", text) + if match and "minrate=" in match.group(0): + return match.group(0) + time.sleep(0.2) + testcase.fail("strict encode command line was not logged") + finally: + ipc.stop() + finally: + proc.terminate() + proc.wait(timeout=15) + + +class TestStrictFilesize(unittest.TestCase): + def test_no_undefined_bitrate_in_strict_flags(self): + block = strict_block(read_source()) + self.assertIn("minrate", block) + self.assertIn("maxrate", block) + # Regression (see issue #48 strict-size reports): an earlier refactor + # left `#{bitrate}` referencing an undefined variable, which encodes + # as minrate=nilk / maxrate=nilk and makes ffmpeg reject the flags. + self.assertNotRegex(block, r"#\{bitrate\}") + + def test_strict_flags_use_matching_stream_bitrate(self): + block = strict_block(read_source()) + # The constrained stream is ovc for video formats, oac for audio-only. + self.assertIn('format.videoCodec != "" and "ovc" or "oac"', block) + # ...and its min/max rates must come from calculate_bitrate's + # video_bitrate / audio_bitrate return values. + self.assertIn("video_bitrate", block) + self.assertIn("audio_bitrate", block) + minrate_vars = set(re.findall(r"minrate=#\{(\w+)\}", block)) + maxrate_vars = set(re.findall(r"maxrate=#\{(\w+)\}", block)) + self.assertTrue(minrate_vars, "no minrate bitrate variable found") + self.assertEqual(minrate_vars, maxrate_vars) + self.assertTrue( + minrate_vars <= {"video_bitrate", "audio_bitrate", "strict_bitrate"}, + f"unexpected bitrate variable(s): {minrate_vars}", + ) + + def test_strict_bitrate_selection_video(self): + # Video formats constrain the video stream. + self.assertEqual(select_strict_bitrate(True, 800, 64), 800) + + def test_strict_bitrate_selection_audio_only(self): + # Audio-only formats (e.g. mp3) constrain the audio stream. + self.assertEqual(select_strict_bitrate(False, None, 800), 800) + + +class TestStrictFilesizeCommandLine(unittest.TestCase): + def test_strict_video_minmax_match_video_bitrate(self): + cmdline = run_strict_encode_command_line(self, "avc") + self.assertIn(f"--ovcopts-add=b={EXPECTED_BITRATE}k", cmdline) + self.assertIn(f"--ovcopts-add=minrate={EXPECTED_BITRATE}k", cmdline) + self.assertIn(f"--ovcopts-add=maxrate={EXPECTED_BITRATE}k", cmdline) + self.assertNotIn("nilk", cmdline) + + def test_strict_audio_only_minmax_match_audio_bitrate(self): + cmdline = run_strict_encode_command_line(self, "mp3") + self.assertIn(f"--oacopts-add=b={EXPECTED_BITRATE}k", cmdline) + self.assertIn(f"--oacopts-add=minrate={EXPECTED_BITRATE}k", cmdline) + self.assertIn(f"--oacopts-add=maxrate={EXPECTED_BITRATE}k", cmdline) + self.assertNotIn("nilk", cmdline) + + +if __name__ == "__main__": + unittest.main() From 5d5ec88800459a006dd7e04cc22ed7e72bef473b Mon Sep 17 00:00:00 2001 From: ekisu Date: Fri, 11 Sep 2026 13:42:39 -0300 Subject: [PATCH 2/3] Verify strict rates with completed audio and video encodes --- tests/testcases/test_strict_filesize.py | 235 +++++++++--------------- 1 file changed, 84 insertions(+), 151 deletions(-) diff --git a/tests/testcases/test_strict_filesize.py b/tests/testcases/test_strict_filesize.py index ae2c11b..798781c 100644 --- a/tests/testcases/test_strict_filesize.py +++ b/tests/testcases/test_strict_filesize.py @@ -1,167 +1,100 @@ +"""Verify strict bitrate flags by completing real video and audio-only encodes.""" +import array import json -import re +import math import subprocess +import sys import tempfile import time import unittest -import uuid +import wave from pathlib import Path from tests.mpv_ipc import MpvIPC -REPO_ROOT = Path(__file__).resolve().parents[2] -ENCODE_MOON = REPO_ROOT / "src" / "encode.moon" -TEST_VIDEO = REPO_ROOT / "tests" / "videos" / "big_buck_bunny_10s.mp4" -# 10s clip, so target_filesize=1000kB means 1000*8/10 = 800kbit/s. -TARGET_FILESIZE = 1000 -CLIP_LENGTH = 10 -EXPECTED_BITRATE = TARGET_FILESIZE * 8 // CLIP_LENGTH +ROOT = Path(__file__).resolve().parents[2] -def read_source(): - return ENCODE_MOON.read_text(encoding="utf-8") - - -def strict_block(source): - """Return the strict minrate/maxrate branch body inside encode().""" - # NOTE: calculate_bitrate() also mentions strict_filesize_constraint, so - # anchor on the bare `if` line (no trailing `and ...`) used by encode(). - match = re.search(r"if options\.strict_filesize_constraint\r?\n", source) - if match is None: - raise AssertionError("strict_filesize_constraint block not found") - start = match.start() - end = source.find("\n\t\telse", start) - if end == -1: - raise AssertionError("end of strict block not found") - return source[start:end] - - -def select_strict_bitrate(has_video_codec, video_bitrate, audio_bitrate): - """Python mirror of the strict-bitrate selection in src/encode.moon.""" - return video_bitrate if has_video_codec else audio_bitrate - - -def run_strict_encode_command_line(testcase, output_format): - """Boot mpv (stdout drained) and capture the strict encode command line. - - Returns the logged `Command line: mpv ...` string. The encode itself is - not awaited; the child is terminated once the command line is captured. - """ - script = REPO_ROOT / "build" / "webm.lua" - testcase.assertTrue(script.exists(), "build/webm.lua missing; run make first") - testcase.assertTrue(TEST_VIDEO.exists(), f"test video missing: {TEST_VIDEO}") - - tmpdir = tempfile.mkdtemp(prefix="mpv-webm-strict-test-") - socket_address = f"/tmp/mpvsocket-strict-{uuid.uuid4()}" - log_path = Path(tmpdir) / "mpv.log" - args = [ - "mpv", "--no-config", "--vo=null", "--ao=null", - "--load-scripts=no", "--idle=yes", - f"--input-ipc-server={socket_address}", - f"--log-file={log_path}", "-v", - ] - # Drain stdout to a file: mpv -v over a PIPE stalls and hangs the test. - with open(Path(tmpdir) / "stdout.log", "wb") as out: - proc = subprocess.Popen(args, stdout=out, stderr=subprocess.STDOUT) - try: +class TestStrictFilesize(unittest.TestCase): + def encode(self, output_format): + with tempfile.TemporaryDirectory(prefix="mpv-webm-strict-") as directory: + tmp = Path(directory) + if output_format == "mp3": + source = tmp / "tone.wav" + with wave.open(str(source), "wb") as audio: + audio.setparams((1, 2, 44100, 0, "NONE", "not compressed")) + samples = array.array("h", (int(4000 * math.sin(2 * math.pi * 440 * i / 44100)) + for i in range(2 * 44100))) + if sys.byteorder != "little": + samples.byteswap() + audio.writeframes(samples.tobytes()) + target, expected, stream, extension = 32, 128, "oac", "mp3" + else: + source = ROOT / "tests/videos/big_buck_bunny_10s.mp4" + target, expected, stream, extension = 1000, 800, "ovc", "mp4" + output = tmp / ("strict." + extension) + log = tmp / "mpv.log" + socket = str(tmp / "ipc") + process = subprocess.Popen([ + "mpv", "--no-config", "--load-scripts=no", "--scripts-clr", + "--vo=null", "--ao=null", "--idle=yes", "-v", + "--input-ipc-server=" + socket, "--log-file=" + str(log), + ], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) ipc = MpvIPC() - ipc.connect(socket_address) - ipc.start() + started = False try: - ipc.send_command({"command": ["enable_event", "client-message"]}) - ipc.send_command({"command": ["load-script", str(script)]}) - testcase.assertTrue( - ipc.wait_for_event("webm-script-loaded", 15), - "script did not load", - ) - ipc.send_command({"command": ["script-message", - "mpv-webm-set-options", json.dumps({ - "output_format": output_format, - "target_filesize": TARGET_FILESIZE, - "strict_filesize_constraint": True, - "twopass": False, - "run_detached": False, - "display_progress": "no", - "output_directory": tmpdir, - })]}) - ipc.send_command({"command": ["loadfile", str(TEST_VIDEO), "replace"]}) - testcase.assertTrue( - ipc.wait_for_event("file-loaded", 15), "file did not load") - ipc.send_command({"command": ["keypress", "Shift+W"]}) - testcase.assertTrue( - ipc.wait_for_event("webm-show-main-page", 15), - "main page did not open", - ) + ipc.connect(socket) + ipc.start() + started = True + def command(*args): + reply = ipc.send_command({"command": list(args)}) + self.assertEqual(reply.error, "success") + command("enable_event", "client-message") + command("load-script", str(ROOT / "build/webm.lua")) + self.assertIsNotNone(ipc.wait_for_event("webm-script-loaded", 10)) + command("script-message", "mpv-webm-set-options", json.dumps({ + "output_format": output_format, + "target_filesize": target, + "strict_filesize_constraint": True, + "twopass": False, + "run_detached": False, + "display_progress": False, + "scale_height": 144, + "output_directory": str(tmp), + "output_template": "strict", + "additional_flags": "--no-config --scripts-clr", + })) + command("loadfile", str(source), "replace") + self.assertIsNotNone(ipc.wait_for_event("file-loaded", 10)) + command("keypress", "Shift+W") + self.assertIsNotNone(ipc.wait_for_event("webm-show-main-page", 10)) time.sleep(1) - ipc.send_command({"command": ["keypress", "e"]}) - deadline = time.time() + 60 - while time.time() < deadline: - text = log_path.read_text(errors="replace") \ - if log_path.exists() else "" - match = re.search(r"Command line: mpv .*", text) - if match and "minrate=" in match.group(0): - return match.group(0) - time.sleep(0.2) - testcase.fail("strict encode command line was not logged") + command("keypress", "e") + event = ipc.wait_for_event("webm-encode-finished", 60) + self.assertIsNotNone(event, "encode did not finish") + self.assertEqual(event.args, ["webm-encode-finished", "success"], + log.read_text(errors="replace")[-4000:]) + self.assertTrue(output.is_file()) + self.assertGreater(output.stat().st_size, 100) + text = log.read_text(errors="replace") + command_line = next(line for line in text.splitlines() + if "Command line: mpv " in line) + for option in ("b", "minrate", "maxrate"): + self.assertIn(f"--{stream}opts-add={option}={expected}k", command_line) + self.assertNotIn("nilk", command_line) finally: - ipc.stop() - finally: - proc.terminate() - proc.wait(timeout=15) - - -class TestStrictFilesize(unittest.TestCase): - def test_no_undefined_bitrate_in_strict_flags(self): - block = strict_block(read_source()) - self.assertIn("minrate", block) - self.assertIn("maxrate", block) - # Regression (see issue #48 strict-size reports): an earlier refactor - # left `#{bitrate}` referencing an undefined variable, which encodes - # as minrate=nilk / maxrate=nilk and makes ffmpeg reject the flags. - self.assertNotRegex(block, r"#\{bitrate\}") - - def test_strict_flags_use_matching_stream_bitrate(self): - block = strict_block(read_source()) - # The constrained stream is ovc for video formats, oac for audio-only. - self.assertIn('format.videoCodec != "" and "ovc" or "oac"', block) - # ...and its min/max rates must come from calculate_bitrate's - # video_bitrate / audio_bitrate return values. - self.assertIn("video_bitrate", block) - self.assertIn("audio_bitrate", block) - minrate_vars = set(re.findall(r"minrate=#\{(\w+)\}", block)) - maxrate_vars = set(re.findall(r"maxrate=#\{(\w+)\}", block)) - self.assertTrue(minrate_vars, "no minrate bitrate variable found") - self.assertEqual(minrate_vars, maxrate_vars) - self.assertTrue( - minrate_vars <= {"video_bitrate", "audio_bitrate", "strict_bitrate"}, - f"unexpected bitrate variable(s): {minrate_vars}", - ) - - def test_strict_bitrate_selection_video(self): - # Video formats constrain the video stream. - self.assertEqual(select_strict_bitrate(True, 800, 64), 800) - - def test_strict_bitrate_selection_audio_only(self): - # Audio-only formats (e.g. mp3) constrain the audio stream. - self.assertEqual(select_strict_bitrate(False, None, 800), 800) - - -class TestStrictFilesizeCommandLine(unittest.TestCase): - def test_strict_video_minmax_match_video_bitrate(self): - cmdline = run_strict_encode_command_line(self, "avc") - self.assertIn(f"--ovcopts-add=b={EXPECTED_BITRATE}k", cmdline) - self.assertIn(f"--ovcopts-add=minrate={EXPECTED_BITRATE}k", cmdline) - self.assertIn(f"--ovcopts-add=maxrate={EXPECTED_BITRATE}k", cmdline) - self.assertNotIn("nilk", cmdline) - - def test_strict_audio_only_minmax_match_audio_bitrate(self): - cmdline = run_strict_encode_command_line(self, "mp3") - self.assertIn(f"--oacopts-add=b={EXPECTED_BITRATE}k", cmdline) - self.assertIn(f"--oacopts-add=minrate={EXPECTED_BITRATE}k", cmdline) - self.assertIn(f"--oacopts-add=maxrate={EXPECTED_BITRATE}k", cmdline) - self.assertNotIn("nilk", cmdline) - - -if __name__ == "__main__": - unittest.main() + process.terminate() + try: + process.wait(timeout=10) + except subprocess.TimeoutExpired: + process.kill() + process.wait() + if started: + ipc.stop() + + def test_video_strict_bitrate(self): + self.encode("avc") + + def test_audio_only_strict_bitrate(self): + self.encode("mp3") From c9a7386e43159cf0f7ba2a54d501691b2c28ad99 Mon Sep 17 00:00:00 2001 From: ekisu Date: Fri, 11 Sep 2026 21:52:45 -0300 Subject: [PATCH 3/3] Exercise regression through the shared real-mpv harness --- tests/testcases/test_strict_filesize.py | 121 ++++++------------------ 1 file changed, 27 insertions(+), 94 deletions(-) diff --git a/tests/testcases/test_strict_filesize.py b/tests/testcases/test_strict_filesize.py index 798781c..e74b37d 100644 --- a/tests/testcases/test_strict_filesize.py +++ b/tests/testcases/test_strict_filesize.py @@ -1,100 +1,33 @@ -"""Verify strict bitrate flags by completing real video and audio-only encodes.""" -import array -import json -import math -import subprocess -import sys -import tempfile -import time -import unittest -import wave -from pathlib import Path +from .base_test_case import BaseTestCase -from tests.mpv_ipc import MpvIPC - -ROOT = Path(__file__).resolve().parents[2] - - -class TestStrictFilesize(unittest.TestCase): - def encode(self, output_format): - with tempfile.TemporaryDirectory(prefix="mpv-webm-strict-") as directory: - tmp = Path(directory) - if output_format == "mp3": - source = tmp / "tone.wav" - with wave.open(str(source), "wb") as audio: - audio.setparams((1, 2, 44100, 0, "NONE", "not compressed")) - samples = array.array("h", (int(4000 * math.sin(2 * math.pi * 440 * i / 44100)) - for i in range(2 * 44100))) - if sys.byteorder != "little": - samples.byteswap() - audio.writeframes(samples.tobytes()) - target, expected, stream, extension = 32, 128, "oac", "mp3" - else: - source = ROOT / "tests/videos/big_buck_bunny_10s.mp4" - target, expected, stream, extension = 1000, 800, "ovc", "mp4" - output = tmp / ("strict." + extension) - log = tmp / "mpv.log" - socket = str(tmp / "ipc") - process = subprocess.Popen([ - "mpv", "--no-config", "--load-scripts=no", "--scripts-clr", - "--vo=null", "--ao=null", "--idle=yes", "-v", - "--input-ipc-server=" + socket, "--log-file=" + str(log), - ], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) - ipc = MpvIPC() - started = False - try: - ipc.connect(socket) - ipc.start() - started = True - def command(*args): - reply = ipc.send_command({"command": list(args)}) - self.assertEqual(reply.error, "success") - command("enable_event", "client-message") - command("load-script", str(ROOT / "build/webm.lua")) - self.assertIsNotNone(ipc.wait_for_event("webm-script-loaded", 10)) - command("script-message", "mpv-webm-set-options", json.dumps({ - "output_format": output_format, - "target_filesize": target, - "strict_filesize_constraint": True, - "twopass": False, - "run_detached": False, - "display_progress": False, - "scale_height": 144, - "output_directory": str(tmp), - "output_template": "strict", - "additional_flags": "--no-config --scripts-clr", - })) - command("loadfile", str(source), "replace") - self.assertIsNotNone(ipc.wait_for_event("file-loaded", 10)) - command("keypress", "Shift+W") - self.assertIsNotNone(ipc.wait_for_event("webm-show-main-page", 10)) - time.sleep(1) - command("keypress", "e") - event = ipc.wait_for_event("webm-encode-finished", 60) - self.assertIsNotNone(event, "encode did not finish") - self.assertEqual(event.args, ["webm-encode-finished", "success"], - log.read_text(errors="replace")[-4000:]) - self.assertTrue(output.is_file()) - self.assertGreater(output.stat().st_size, 100) - text = log.read_text(errors="replace") - command_line = next(line for line in text.splitlines() - if "Command line: mpv " in line) - for option in ("b", "minrate", "maxrate"): - self.assertIn(f"--{stream}opts-add={option}={expected}k", command_line) - self.assertNotIn("nilk", command_line) - finally: - process.terminate() - try: - process.wait(timeout=10) - except subprocess.TimeoutExpired: - process.kill() - process.wait() - if started: - ipc.stop() +class TestStrictFilesize(BaseTestCase): + def checkEncode(self, output_format): + if output_format == "mp3": + source = self.tempdir / "tone.wav" + self.runTool("ffmpeg", "-v", "error", "-f", "lavfi", "-i", + "sine=frequency=440:duration=2", str(source)) + target, end, expected, stream, extension = 32, 2, 128, "oac", "mp3" + else: + source = self.createVideo() + target, end, expected, stream, extension = 100, 1, 800, "ovc", "mp4" + self.openTestVideoFile(source) + self.encodeClip(0, end, options={ + "output_format": output_format, "target_filesize": target, + "strict_filesize_constraint": True, "twopass": False, + "output_template": "strict", + }) + output = self.tempdir / ("strict." + extension) + self.assertGreater(output.stat().st_size, 100) + self.assertTrue(self.probeVideo(output)["streams"]) + self.waitUntil(lambda: "Command line:" in self.getLog(), "encoder command log") + command = next(line for line in self.getLog().splitlines() if "Command line:" in line) + for option in ("b", "minrate", "maxrate"): + self.assertIn(f"--{stream}opts-add={option}={expected}k", command) + self.assertNotIn("nilk", command) def test_video_strict_bitrate(self): - self.encode("avc") + self.checkEncode("avc") def test_audio_only_strict_bitrate(self): - self.encode("mp3") + self.checkEncode("mp3")