From 66d45261e9d8b386821e8f9588b0e20324b15edb Mon Sep 17 00:00:00 2001 From: msabiniok Date: Wed, 26 Nov 2025 17:04:29 +0100 Subject: [PATCH] feat: add TestVector optional decoder parameters adding the capability to include an optional decoder parameters set for a specfic test vector in the test suite definition. optional parameters are utilized in Decoder class decode function abstraction and are applied in Gstreamer(Decoder) and Ffmpeg(Decoder) classes implementations. It's child class responsibility to handle the optional parameters. --- fluster/decoder.py | 3 ++- fluster/decoders/av1_aom.py | 2 ++ fluster/decoders/av1_dav1d.py | 2 ++ fluster/decoders/chromium.py | 2 ++ fluster/decoders/cros_codecs.py | 2 ++ fluster/decoders/dummy.py | 2 ++ fluster/decoders/ffmpeg.py | 8 +++++++- fluster/decoders/gstreamer.py | 16 +++++++++++++--- fluster/decoders/h264_jct_vt.py | 2 ++ fluster/decoders/h265_jct_vt.py | 2 ++ fluster/decoders/h266_vvc_vtm.py | 2 ++ fluster/decoders/h266_vvdec.py | 2 ++ fluster/decoders/iso_mpeg2_aac.py | 2 ++ fluster/decoders/iso_mpeg2_video.py | 2 ++ fluster/decoders/iso_mpeg4_aac.py | 2 ++ fluster/decoders/iso_mpeg4_aac_er.py | 2 ++ fluster/decoders/iso_mpeg4_video.py | 3 ++- fluster/decoders/libvpx.py | 2 ++ fluster/decoders/vk_video_decoder.py | 2 ++ fluster/test.py | 1 + fluster/test_vector.py | 8 ++++++++ 21 files changed, 63 insertions(+), 6 deletions(-) diff --git a/fluster/decoder.py b/fluster/decoder.py index a8785c3c..4eb27c5e 100644 --- a/fluster/decoder.py +++ b/fluster/decoder.py @@ -18,7 +18,7 @@ from abc import ABC, abstractmethod from functools import lru_cache from shutil import which -from typing import List, Optional, Type +from typing import Any, Dict, List, Optional, Type from fluster.codec import Codec, OutputFormat from fluster.utils import normalize_binary_cmd @@ -47,6 +47,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" raise Exception("Not implemented") diff --git a/fluster/decoders/av1_aom.py b/fluster/decoders/av1_aom.py index a867904a..480b714a 100644 --- a/fluster/decoders/av1_aom.py +++ b/fluster/decoders/av1_aom.py @@ -14,6 +14,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -38,6 +39,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" fmt = "--rawvideo" diff --git a/fluster/decoders/av1_dav1d.py b/fluster/decoders/av1_dav1d.py index 526cc5e5..e70f50dd 100644 --- a/fluster/decoders/av1_dav1d.py +++ b/fluster/decoders/av1_dav1d.py @@ -14,6 +14,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -37,6 +38,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" fmt = "yuv" diff --git a/fluster/decoders/chromium.py b/fluster/decoders/chromium.py index 04ff48e8..d5f05ad0 100644 --- a/fluster/decoders/chromium.py +++ b/fluster/decoders/chromium.py @@ -16,6 +16,7 @@ # License along with this library. If not, see . from functools import lru_cache +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -50,6 +51,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: return str(main(input_filepath)) diff --git a/fluster/decoders/cros_codecs.py b/fluster/decoders/cros_codecs.py index 718e5437..6f96779d 100644 --- a/fluster/decoders/cros_codecs.py +++ b/fluster/decoders/cros_codecs.py @@ -14,6 +14,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -38,6 +39,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" diff --git a/fluster/decoders/dummy.py b/fluster/decoders/dummy.py index 0fb8eb57..9659e1eb 100644 --- a/fluster/decoders/dummy.py +++ b/fluster/decoders/dummy.py @@ -14,6 +14,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -36,5 +37,6 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: return file_checksum(input_filepath) diff --git a/fluster/decoders/ffmpeg.py b/fluster/decoders/ffmpeg.py index 1e6f9c28..f6724bdf 100644 --- a/fluster/decoders/ffmpeg.py +++ b/fluster/decoders/ffmpeg.py @@ -19,7 +19,7 @@ import re import subprocess from functools import lru_cache -from typing import Dict, Optional, Tuple +from typing import Any, Dict, Optional, Tuple from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -70,6 +70,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" command = [self.binary, "-hide_banner", "-nostdin"] @@ -99,6 +100,11 @@ def decode( elif self.ffmpeg_codec: command.extend(["-codec", self.ffmpeg_codec]) + # Optional decoder parameters + if optional_params: + for key, value in optional_params.items(): + command.extend([key, str(value)]) + # Input file command.extend(["-i", input_filepath]) diff --git a/fluster/decoders/gstreamer.py b/fluster/decoders/gstreamer.py index 7a61f91f..793ad19f 100644 --- a/fluster/decoders/gstreamer.py +++ b/fluster/decoders/gstreamer.py @@ -21,7 +21,7 @@ import shlex import subprocess from functools import lru_cache -from typing import List, Optional +from typing import Any, Dict, List, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -109,14 +109,21 @@ def gen_pipeline( input_filepath: str, output_filepath: Optional[str], output_format: OutputFormat, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Generate the GStreamer pipeline used to decode the test vector""" output = f"location={output_filepath}" if output_filepath else "" + + decoder_params = "" + if optional_params: + for key, value in optional_params.items(): + decoder_params += f" {key}={value} " + return PIPELINE_TPL.format( self.cmd, input_filepath, self.parser if self.parser else "parsebin", - self.decoder_bin, + self.decoder_bin + decoder_params, self.caps, self.sink, output, @@ -149,6 +156,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decode the test vector and do the checksum""" # When using videocodectestsink we can avoid writing files to disk @@ -162,7 +170,7 @@ def decode( data = run_command_with_output(command, timeout=timeout, verbose=verbose).splitlines() return self.parse_videocodectestsink_md5sum(data) - pipeline = self.gen_pipeline(input_filepath, output_filepath, output_format) + pipeline = self.gen_pipeline(input_filepath, output_filepath, output_format, optional_params) run_command(shlex.split(pipeline), timeout=timeout, verbose=verbose) return file_checksum(output_filepath) @@ -193,6 +201,7 @@ def gen_pipeline( input_filepath: str, output_filepath: Optional[str], output_format: OutputFormat, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: raw_caps = "video/x-raw" try: @@ -785,6 +794,7 @@ def gen_pipeline( input_filepath: str, output_filepath: Optional[str], output_format: OutputFormat, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: caps = f"{self.caps} ! videoconvert dither=none ! video/x-raw,format={output_format_to_gst(output_format)}" output = f"location={output_filepath}" if output_filepath else "" diff --git a/fluster/decoders/h264_jct_vt.py b/fluster/decoders/h264_jct_vt.py index c2720f2e..1f35a398 100644 --- a/fluster/decoders/h264_jct_vt.py +++ b/fluster/decoders/h264_jct_vt.py @@ -15,6 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -38,6 +39,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" run_command( diff --git a/fluster/decoders/h265_jct_vt.py b/fluster/decoders/h265_jct_vt.py index 4e9b1702..c70909d9 100644 --- a/fluster/decoders/h265_jct_vt.py +++ b/fluster/decoders/h265_jct_vt.py @@ -15,6 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -38,6 +39,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" run_command( diff --git a/fluster/decoders/h266_vvc_vtm.py b/fluster/decoders/h266_vvc_vtm.py index c7d183e1..9865792f 100644 --- a/fluster/decoders/h266_vvc_vtm.py +++ b/fluster/decoders/h266_vvc_vtm.py @@ -14,6 +14,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -37,6 +38,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" # pylint: disable=unused-argument diff --git a/fluster/decoders/h266_vvdec.py b/fluster/decoders/h266_vvdec.py index cd53c90e..64e86aba 100644 --- a/fluster/decoders/h266_vvdec.py +++ b/fluster/decoders/h266_vvdec.py @@ -14,6 +14,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -37,6 +38,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" run_command( diff --git a/fluster/decoders/iso_mpeg2_aac.py b/fluster/decoders/iso_mpeg2_aac.py index 0c2ee5ee..61da3aa5 100644 --- a/fluster/decoders/iso_mpeg2_aac.py +++ b/fluster/decoders/iso_mpeg2_aac.py @@ -16,6 +16,7 @@ # License along with this library. If not, see . import glob import os +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -40,6 +41,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" # Addition of .pcm as extension is a must. If it is something else, e.g. ".out" the decoder will output a diff --git a/fluster/decoders/iso_mpeg2_video.py b/fluster/decoders/iso_mpeg2_video.py index c0001644..4c2a2aaf 100644 --- a/fluster/decoders/iso_mpeg2_video.py +++ b/fluster/decoders/iso_mpeg2_video.py @@ -17,6 +17,7 @@ import glob import os import tempfile +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -41,6 +42,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" with tempfile.TemporaryDirectory() as temp_dir: diff --git a/fluster/decoders/iso_mpeg4_aac.py b/fluster/decoders/iso_mpeg4_aac.py index c402a837..dd39e115 100644 --- a/fluster/decoders/iso_mpeg4_aac.py +++ b/fluster/decoders/iso_mpeg4_aac.py @@ -16,6 +16,7 @@ # License along with this library. If not, see . import glob +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -40,6 +41,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" # Addition of .pcm as extension is a must. If it is something else, e.g. ".out" the decoder will output a diff --git a/fluster/decoders/iso_mpeg4_aac_er.py b/fluster/decoders/iso_mpeg4_aac_er.py index 280bbdda..b7c948a8 100644 --- a/fluster/decoders/iso_mpeg4_aac_er.py +++ b/fluster/decoders/iso_mpeg4_aac_er.py @@ -16,6 +16,7 @@ # License along with this library. If not, see . import glob +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -40,6 +41,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" output_filepath += ".raw" diff --git a/fluster/decoders/iso_mpeg4_video.py b/fluster/decoders/iso_mpeg4_video.py index a91b7e96..fdeee0d6 100644 --- a/fluster/decoders/iso_mpeg4_video.py +++ b/fluster/decoders/iso_mpeg4_video.py @@ -16,7 +16,7 @@ # License along with this library. If not, see . import os import subprocess -from typing import Tuple +from typing import Any, Dict, Optional, Tuple from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -41,6 +41,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath to output_filepath""" width, height = self._get_video_resolution(input_filepath, verbose) diff --git a/fluster/decoders/libvpx.py b/fluster/decoders/libvpx.py index 1a390455..eca6cccd 100644 --- a/fluster/decoders/libvpx.py +++ b/fluster/decoders/libvpx.py @@ -15,6 +15,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -42,6 +43,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" fmt = None diff --git a/fluster/decoders/vk_video_decoder.py b/fluster/decoders/vk_video_decoder.py index 0ead4ee7..e700ce98 100644 --- a/fluster/decoders/vk_video_decoder.py +++ b/fluster/decoders/vk_video_decoder.py @@ -14,6 +14,7 @@ # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . +from typing import Any, Dict, Optional from fluster.codec import Codec, OutputFormat from fluster.decoder import Decoder, register_decoder @@ -38,6 +39,7 @@ def decode( timeout: int, verbose: bool, keep_files: bool, + optional_params: Optional[Dict[str, Any]] = None, ) -> str: """Decodes input_filepath in output_filepath""" codec_mapping = { diff --git a/fluster/test.py b/fluster/test.py index b068fefc..aa3c39eb 100644 --- a/fluster/test.py +++ b/fluster/test.py @@ -86,6 +86,7 @@ def _execute_decode(self) -> str: self.timeout, self.verbose, keep_files_for_decode, + self.test_vector.optional_params, ) def _cleanup_if_needed(self) -> None: diff --git a/fluster/test_vector.py b/fluster/test_vector.py index d4436cd9..676bd92d 100644 --- a/fluster/test_vector.py +++ b/fluster/test_vector.py @@ -44,6 +44,7 @@ def __init__( output_format: OutputFormat, result: str, profile: Optional[Profile] = None, + optional_params: Optional[Dict[str, Any]] = None, ): # JSON members self.name = name @@ -51,6 +52,7 @@ def __init__( self.source_checksum = source_checksum self.input_file = input_file self.profile = profile + self.optional_params = optional_params self.output_format = output_format self.result = result @@ -84,6 +86,11 @@ def data_to_serialize(self) -> Dict[str, object]: data["profile"] = str(self.profile.value) else: data.pop("profile") + if self.optional_params is not None: + data["optional_params"] = self.optional_params + else: + data.pop("optional_params") + return data def __str__(self) -> str: @@ -92,6 +99,7 @@ def __str__(self) -> str: f" Source: {self.source}\n" f" Input: {self.input_file}\n" f" Profile: {self.profile}\n" + f" Options: {self.optional_params}\n" f" Result: {self.result}" ) return ret