diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4c830957a91e64735ed024a9f461f50c1afc9cba..5b7092254c3656dc32877bbe74dc01631ac1d59a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1346,6 +1346,7 @@ ivas-conformance: - cp Readme_IVAS_enc.txt testvec - cp Readme_IVAS_rend.txt testvec - cp Readme_IVAS_JBM_dec.txt testvec + - cp Readme_IVAS_ISAR_enc.txt testvec - cp Readme_IVAS_ISAR_dec.txt testvec - cp Readme_IVAS_ISAR_post_rend.txt testvec - cp IVAS_cod.exe testvec/bin @@ -1371,6 +1372,7 @@ ivas-conformance: - Readme_IVAS_enc.txt - Readme_IVAS_rend.txt - Readme_IVAS_JBM_dec.txt + - Readme_IVAS_ISAR_enc.txt - Readme_IVAS_ISAR_dec.txt - Readme_IVAS_ISAR_post_rend.txt expose_as: "Draft IVAS conformance" @@ -1436,6 +1438,7 @@ ivas-conformance-linux: - cp Readme_IVAS_enc.txt testvec - cp Readme_IVAS_rend.txt testvec - cp Readme_IVAS_JBM_dec.txt testvec + - cp Readme_IVAS_ISAR_enc.txt testvec - cp Readme_IVAS_ISAR_dec.txt testvec - cp Readme_IVAS_ISAR_post_rend.txt testvec @@ -1474,6 +1477,7 @@ ivas-conformance-linux: - Readme_IVAS_enc.txt - Readme_IVAS_rend.txt - Readme_IVAS_JBM_dec.txt + - Readme_IVAS_ISAR_enc.txt - Readme_IVAS_ISAR_dec.txt - Readme_IVAS_ISAR_post_rend.txt - $COVERAGE_OUTPUT_FILE diff --git a/scripts/enc_isar_header.txt b/scripts/enc_isar_header.txt new file mode 100644 index 0000000000000000000000000000000000000000..00134190a94c383d4843f30cc0f252ebc35d44a3 --- /dev/null +++ b/scripts/enc_isar_header.txt @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + exit -1 +fi + +CUT_ENC_BIN=$1 +DIFF_BIN="diff" + +TESTV_PATH="." +REF_PATH="./testv" +CUT_PATH="./TMP_ENC_ISAR" +LOG_FILE=Readme_IVAS_ISAR_enc_log.txt + +rm -rf tmp +rm -rf $CUT_PATH +mkdir -p $CUT_PATH +mkdir -p $CUT_PATH/split_rendering/cut + diff --git a/scripts/parse_commands.py b/scripts/parse_commands.py index 984374f66a12f1a31d33991652de16c1c42047a0..4dc1e0e17b074571e3389e5d168299842cacd58b 100644 --- a/scripts/parse_commands.py +++ b/scripts/parse_commands.py @@ -24,6 +24,7 @@ if __name__ == '__main__': cmds_enc=[] cmds_dec=[] cmds_rend=[] + cmds_isar_enc=[] cmds_isar_post_rend=[] all_args = set() @@ -39,15 +40,18 @@ if __name__ == '__main__': cmds_enc.extend(re.findall(r"REF encoder command:\\n\\t(.*?)\\n", line)) cmds_dec.extend(re.findall(r"REF decoder command:\\n\\t(.*?)\\n", line)) cmds_rend.extend(re.findall(r"Running command\\n(.*?)\\n", line)) + cmds_isar_enc.extend(re.findall(r"Running IVAS ISAR encoder command\\n(.*?)\\n", line)) cmds_isar_post_rend.extend(re.findall(r"Running ISAR post renderer command\\n(.*?)\\n", line)) + cmds_isar_post_rend.extend(re.findall(r"Running ISAR EXT REND command\\n(.*?)\\n", line)) # If pytest-html < v4 is used, the parsing will fail and render empty lists. This is a work-around in case that happens. - if all(not x for x in [cmds_enc, cmds_dec, cmds_rend, cmds_isar_post_rend]): + if all(not x for x in [cmds_enc, cmds_dec, cmds_rend, cmds_isar_enc, cmds_isar_post_rend]): for html_report in input: with open(html_report,'r') as infile: enc_cmd = False dec_cmd = False rend_cmd = False + isar_enc_cmd = False isar_post_rend_cmd = False for line in infile.readlines(): line = line.split("
")[0] # Remove trailing html tags @@ -60,6 +64,9 @@ if __name__ == '__main__': elif rend_cmd: cmds_rend.append(line) rend_cmd = False + elif isar_enc_cmd: + cmds_isar_enc.append(line) + isar_enc_cmd = False elif isar_post_rend_cmd: cmds_isar_post_rend.append(line) isar_post_rend_cmd = False @@ -70,13 +77,18 @@ if __name__ == '__main__': dec_cmd = True elif "Running command" in line: rend_cmd = True + elif "Running IVAS ISAR encoder command" in line: + isar_enc_cmd = True elif "Running ISAR post renderer command" in line: isar_post_rend_cmd = True + elif "Running ISAR EXT REND command" in line: + isar_post_rend_cmd = True # Sort lists to keep deterministic order between runs cmds_enc.sort() cmds_dec.sort() cmds_rend.sort() + cmds_isar_enc.sort() cmds_isar_post_rend.sort() # Remove duplicates from cmds_enc -- some decoder tests use same encoder options @@ -116,6 +128,28 @@ if __name__ == '__main__': with open('scripts/script_footer.txt','r') as footer: outfile.write(footer.read()) + with open(txt_file.replace('.','_ISAR_enc.'),'w', newline='\n') as outfile: + with open('scripts/enc_isar_header.txt','r') as header: + outfile.write(header.read()) + for cmd in cmds_isar_enc: + args = [] + for arg in cmd.split(): + # Adjust file arguments, pass other arguments as they are + if path.exists(arg): + arg = path.relpath(arg).replace('\\','/') + arg = re.sub('IVAS_cod(.exe)?', '$CUT_ENC_BIN', arg) + arg = re.sub('scripts', TESTV_PATH, arg) + arg = re.sub('tests', CUT_PATH, arg) + args.append(arg) + cmd = ' '.join(args) + outfile.write(cmd+'\n') + bts = re.search(r"\s(([\S]+)(.bts|.192|.pkt|.fer))$", cmd) + if bts: + outfile.write('$DIFF_BIN '+bts.group(1).replace(CUT_PATH + r'/dut',REF_PATH + r'/ref')+' '+bts.group(1)+' >> $LOG_FILE 2>&1\n') + outfile.write('\n') + with open('scripts/script_footer.txt','r') as footer: + outfile.write(footer.read()) + with open(txt_file.replace('.','_dec.'),'w', newline='\n') as outfile_dec, open(txt_file.replace('.','_JBM_dec.'),'w', newline='\n') as outfile_jbm, open(txt_file.replace('.','_ISAR_dec.'),'w', newline='\n') as outfile_isar: with open('scripts/dec_header.txt','r') as header: outfile_dec.write(header.read()) diff --git a/tests/conformance-test/test_26252.py b/tests/conformance-test/test_26252.py index 0179f58bfcfb30d01fc37046896b960d82e97bad..a60fe019942bf204dbd007bc1a01b5a14c9744fb 100644 --- a/tests/conformance-test/test_26252.py +++ b/tests/conformance-test/test_26252.py @@ -59,7 +59,7 @@ def replace_paths(instr, testv_path, ref_path, cut_path): test_dict = {} TEST_DIR = "." -scripts=["Readme_IVAS_enc.txt", "Readme_IVAS_dec.txt", "Readme_IVAS_rend.txt", "Readme_IVAS_JBM_dec.txt", "Readme_IVAS_ISAR_dec.txt", "Readme_IVAS_ISAR_post_rend.txt"] +scripts=["Readme_IVAS_enc.txt", "Readme_IVAS_ISAR_enc.txt", "Readme_IVAS_dec.txt", "Readme_IVAS_rend.txt", "Readme_IVAS_JBM_dec.txt", "Readme_IVAS_ISAR_dec.txt", "Readme_IVAS_ISAR_post_rend.txt"] for s in scripts: with open(os.path.join(TEST_DIR, s), "r", encoding="UTF-8") as fp: diff --git a/tests/conftest.py b/tests/conftest.py index 6f293b9502b058903416b4d99f4601055300c26e..742c42060fc8887e2b1a47e59a1e5d222f9f3a46 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1546,3 +1546,15 @@ def ref_postrend_frontend(ref_postrend_path, request) -> Optional[PostRendFronte # Fixture teardown if postrend is not None: postrend._check_run() + +def pytest_sessionstart(session): + session.test_functions = [] + +def pytest_collection_finish(session): + # Collect all test function names + session.test_functions = [item.nodeid for item in session.items] + +@pytest.fixture(scope="session") +def collected_tests(request): + # Access the collected test function names + return getattr(request.session, "test_functions", []) \ No newline at end of file diff --git a/tests/renderer/utils.py b/tests/renderer/utils.py index 9acb1b7f18101c802f3c9edb5b65af498ce85806..1aa7370f08dd8d919e2cf028bf3e4cfb223447b8 100644 --- a/tests/renderer/utils.py +++ b/tests/renderer/utils.py @@ -67,8 +67,7 @@ from pyaudio3dtools.audiofile import readfile from ..cmp_pcm import cmp_pcm from ..conftest import get_split_idx, parse_properties - -def _run_cmd(cmd, env, test_info=None): +def _run_cmd(cmd, test_info=None, env=None ): """ Helper function for running some command. Raises a SystemError if either the return code is non-zero or a USAN printout is detected @@ -93,32 +92,30 @@ def _run_cmd(cmd, env, test_info=None): raise SystemError(error) -def run_cmd(cmd, test_info, env=None): +def run_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning command\n{' '.join(cmd)}\n") - _run_cmd(cmd, env, test_info) + _run_cmd(cmd, test_info=test_info, env=env) -def run_isar_ext_rend_cmd(cmd, env=None): +def run_isar_ext_rend_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning ISAR EXT REND command\n{' '.join(cmd)}\n") - _run_cmd(cmd, env) + _run_cmd(cmd, test_info=test_info, env=env) -def run_ivas_isar_enc_cmd(cmd, env=None): +def run_ivas_isar_enc_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning IVAS ISAR encoder command\n{' '.join(cmd)}\n") - _run_cmd(cmd, env) - + _run_cmd(cmd, test_info=test_info, env=env) -def run_ivas_isar_dec_cmd(cmd, env=None): +def run_ivas_isar_dec_cmd(cmd, test_info=None, env=None): if BIN_SUFFIX_MERGETARGET in cmd[0]: logging.info(f"\nREF decoder command:\n\t{' '.join(cmd)}\n") else: logging.info(f"\nDUT decoder command:\n\t{' '.join(cmd)}\n") - _run_cmd(cmd, env) - + _run_cmd(cmd, test_info=test_info, env=env) -def run_isar_post_rend_cmd(cmd, env=None): +def run_isar_post_rend_cmd(cmd, test_info=None, env=None): logging.info(f"\nRunning ISAR post renderer command\n{' '.join(cmd)}\n") - _run_cmd(cmd, env) + _run_cmd(cmd, test_info=test_info, env=env) def check_BE( diff --git a/tests/split_rendering/test_split_rendering.py b/tests/split_rendering/test_split_rendering.py index 0ff32fa290ccdb0693d1d2f296630a652ab20881..72da6fcf8ed33c11cffa4b34faaa279f0b9292b2 100644 --- a/tests/split_rendering/test_split_rendering.py +++ b/tests/split_rendering/test_split_rendering.py @@ -33,9 +33,72 @@ the United Nations Convention on Contracts on the International Sales of Goods. import pytest from tests.split_rendering.utils import * +import pdb + + +@pytest.fixture(scope="session", autouse=True) +def truncate_input_files(collected_tests, request): + """ + Truncate the test vectors to maximum INPUT_DURATION_SEC seconds + """ + + INPUT_FORMATS_FULL_CHAIN_SPLIT_PCM = [entry[0] for entry in full_chain_split_pcm_params] + INPUT_FORMATS_EXT_SPLIT_PCM = [entry[0] for entry in external_split_pcm_params] + + TEST_FUNCTIONS_TO_FORMATS = { + "test_ambisonics_full_chain_split": INPUT_FORMATS_AMBI_SPLIT_REND, + "test_ambisonics_external_split": INPUT_FORMATS_AMBI_SPLIT_REND, + "test_multichannel_full_chain_split": INPUT_FORMATS_MC_SPLIT_REND, + "test_multichannel_external_split": INPUT_FORMATS_MC_SPLIT_REND, + "test_ism_full_chain_split": INPUT_FORMATS_ISM_SPLIT_REND, + "test_ism_external_split": INPUT_FORMATS_ISM_SPLIT_REND, + "test_masa_full_chain_split": INPUT_FORMATS_MASA_SPLIT_REND, + "test_masa_external_split": INPUT_FORMATS_MASA_SPLIT_REND, + "test_omasa_full_chain_split": INPUT_FORMATS_OMASA_SPLIT_REND, + "test_omasa_external_split": INPUT_FORMATS_OMASA_SPLIT_REND, + "test_osba_full_chain_split": INPUT_FORMATS_OSBA_SPLIT_REND, + "test_osba_external_split": INPUT_FORMATS_OSBA_SPLIT_REND, + "test_post_rend_plc": [INPUT_FORMATS_AMBI_SPLIT_REND[-1]], + "test_full_chain_split_pcm": INPUT_FORMATS_FULL_CHAIN_SPLIT_PCM, + "test_external_split_pcm": INPUT_FORMATS_EXT_SPLIT_PCM, + "test_framing_combinations_full_chain_split": ["5_1", "FOA"], + "test_framing_combinations_external_split": ["5_1"], + } + + pattern = r'::([^\[]+)\[' + + # get the list of test functions to run + functions_to_run = {re.search(pattern, entry).group(1) for entry in collected_tests if re.search(pattern, entry)} + functions_to_run = list(functions_to_run) + + # get the list of file formats for test functions + formats = [] + for name in functions_to_run: + formats.extend(TEST_FUNCTIONS_TO_FORMATS.get(name, [])) + seen = set() + formats = [fmt for fmt in formats if not (fmt in seen or seen.add(fmt))] # remove duplicates + + logging.info(f"\nCutting input files for the following formats: {', '.join(formats)}\n") + + # pdb.set_trace() + for in_fmt in formats: + in_file = FORMAT_TO_FILE_COMPARETEST[in_fmt] + cut_in_file = in_file.with_stem(in_file.stem + "_cut") + + if not path.exists(cut_in_file): + data, fs = readfile(in_file) + + if data.ndim == 1: + data_out = data[: INPUT_DURATION_SEC * fs] + elif data.ndim == 2: + data_out = data[: INPUT_DURATION_SEC * fs, :] + else: + raise ValueError(f"Cannot truncate data with dimension of {data.ndim}") + + writefile(cut_in_file, data_out, fs) -""" Ambisonics """ +""" Ambisonics """ @pytest.mark.parametrize("delay_profile", DELAY_PROFILES) @pytest.mark.parametrize("trajectory", SPLIT_REND_HR_TRAJECTORIES_TO_TEST) @@ -557,7 +620,6 @@ def test_post_rend_plc( """ BINAURAL_SPLIT_PCM """ - full_chain_split_pcm_params = [ ("HOA3", "96000", "split_renderer_config_1dof_512k_default"), ("7_1_4", "512000", "split_renderer_config_3dofhq_512k_lc3plus"), diff --git a/tests/split_rendering/utils.py b/tests/split_rendering/utils.py index f2125bcc909b528b355d26f5063dca8b3aa9da9c..4d320576973c66a22933cb1b802ef2fe72c71cc0 100644 --- a/tests/split_rendering/utils.py +++ b/tests/split_rendering/utils.py @@ -35,6 +35,8 @@ import sys from pathlib import Path from tempfile import TemporaryDirectory from typing import Tuple, Optional +from os import path +# from filelock import FileLock import numpy as np import pytest @@ -61,6 +63,7 @@ from ..conftest import parse_properties sys.path.append(SCRIPTS_DIR) from pyaudio3dtools.audiofile import readfile, writefile +# file_lock = FileLock("tmp_truncate_signal_token.lock") def lc3plus_used(test_info, in_fmt, render_config): return ( @@ -148,24 +151,25 @@ def check_xfail( ) -def truncate_signal( - in_file: Path, - out_file: Path, -) -> None: - """ - Truncate the signal in in_file to maximum INPUT_DURATION_SEC seconds, - and write the truncated signal to out_file - """ - data, fs = readfile(in_file) +# def truncate_signal( + # in_file: Path, + # out_file: Path, +# ) -> None: + # """ + # Truncate the signal in in_file to maximum INPUT_DURATION_SEC seconds, + # and write the truncated signal to out_file + # """ + + # data, fs = readfile(in_file) - if data.ndim == 1: - data_out = data[: INPUT_DURATION_SEC * fs] - elif data.ndim == 2: - data_out = data[: INPUT_DURATION_SEC * fs, :] - else: - raise ValueError(f"Cannot truncate data with dimension of {data.ndim}") + # if data.ndim == 1: + # data_out = data[: INPUT_DURATION_SEC * fs] + # elif data.ndim == 2: + # data_out = data[: INPUT_DURATION_SEC * fs, :] + # else: + # raise ValueError(f"Cannot truncate data with dimension of {data.ndim}") - writefile(out_file, data_out, fs) + # writefile(out_file, data_out, fs) def run_full_chain_split_rendering( @@ -199,7 +203,6 @@ def run_full_chain_split_rendering( with TemporaryDirectory() as tmp_dir: tmp_dir = Path(tmp_dir) - cut_in_file = tmp_dir.joinpath("cut_input.wav") renderer_fmt_for_filename = renderer_fmt.replace("BINAURAL_", "") filename_base = f"{in_fmt}_{bitrate}_{renderer_fmt_for_filename}_full_cfg_{render_config.stem}_fr_pre_{pre_rend_fr}_post_{post_rend_fr}" @@ -223,6 +226,7 @@ def run_full_chain_split_rendering( ivas_bitstream = output_path_base.joinpath(ivas_bitstream_stem) split_bitstream = output_path_base.joinpath(split_bitstream_stem) out_file = output_path_base.joinpath(out_file_stem) + if renderer_fmt == "BINAURAL_SPLIT_PCM": split_md_file = output_path_base.joinpath(split_md_file_stem) @@ -245,7 +249,10 @@ def run_full_chain_split_rendering( # if in REF or CUT creation mode use the comparetestv if test_info.config.option.create_ref or test_info.config.option.create_cut: in_file = FORMAT_TO_FILE_COMPARETEST[in_fmt] - truncate_signal(in_file, cut_in_file) + cut_in_file = in_file.with_stem(in_file.stem + "_cut") + # with file_lock: + # if not path.exists(cut_in_file): + # truncate_signal(in_file, cut_in_file) enc_cmd[3] = str(cut_in_file) else: @@ -257,7 +264,7 @@ def run_full_chain_split_rendering( enc_cmd[1:1] = FORMAT_TO_IVAS_COD_FORMAT[in_fmt] - run_ivas_isar_enc_cmd(enc_cmd) + run_ivas_isar_enc_cmd(enc_cmd, test_info=test_info) if delay_profile: rtp_bitstream = ivas_bitstream.with_suffix(".rtpg192") @@ -292,8 +299,8 @@ def run_full_chain_split_rendering( if delay_profile: dec_cmd[5:5] = ["-voip"] - run_ivas_isar_dec_cmd(dec_cmd) - + run_ivas_isar_dec_cmd(dec_cmd, test_info=test_info) + # run split renderer post_rend_cmd = SPLIT_POST_REND_CMD[:] @@ -309,7 +316,7 @@ def run_full_chain_split_rendering( if renderer_fmt == "BINAURAL_SPLIT_PCM": post_rend_cmd[7:7] = ["-im", str(split_md_file)] - run_isar_post_rend_cmd(post_rend_cmd) + run_isar_post_rend_cmd(post_rend_cmd, test_info=test_info) if test_info.config.option.create_cut: # CUT creation mode will run a comparison with REF @@ -374,22 +381,30 @@ def run_external_split_rendering( with TemporaryDirectory() as tmp_dir: tmp_dir = Path(tmp_dir) - cut_in_file = tmp_dir.joinpath("cut_input.wav") - split_bitstream = tmp_dir.joinpath("split.bit") + + renderer_fmt_for_filename = renderer_fmt.replace("BINAURAL_", "") + filename_base = f"{in_fmt}_{renderer_fmt_for_filename}_ext_cfg_{render_config.stem}_fr_pre_{pre_rend_fr}_post_{post_rend_fr}" + + split_bitstream_stem = f"{filename_base}.splt.bit" if renderer_fmt == "BINAURAL_SPLIT_PCM": - split_md_file = tmp_dir.joinpath("split_md.bin") + split_md_file_stem = f"{filename_base}.spltmd.bit" - renderer_fmt_for_filename = renderer_fmt.replace("BINAURAL_", "") - out_file_stem = f"{in_fmt}_{renderer_fmt_for_filename}_ext_cfg_{render_config.stem}_fr_pre_{pre_rend_fr}_post_{post_rend_fr}.wav" + out_file_stem = f"{filename_base}.wav" if test_info.config.option.create_ref: output_path_base = OUTPUT_PATH_REF else: output_path_base = OUTPUT_PATH_CUT + split_bitstream = output_path_base.joinpath(split_bitstream_stem) out_file = output_path_base.joinpath(out_file_stem) + if renderer_fmt == "BINAURAL_SPLIT_PCM": + split_md_file = output_path_base.joinpath(split_md_file_stem) if plc_error_pattern: + split_bitstream = split_bitstream.with_stem( + f"{split_bitstream.stem}_plc_{plc_error_pattern.stem}" + ) out_file = out_file.with_stem( f"{out_file.stem}_plc_{plc_error_pattern.stem}" ) @@ -403,7 +418,7 @@ def run_external_split_rendering( else: in_meta_files = None - # generate split-rendering bitstream + # run ISAR pre-renderer split_pre_cmd = SPLIT_PRE_REND_CMD[:] if test_info.config.option.create_ref: @@ -413,7 +428,10 @@ def run_external_split_rendering( # if in REF or CUT creation mode use the comparetestv if test_info.config.option.create_ref or test_info.config.option.create_cut: in_file = FORMAT_TO_FILE_COMPARETEST[in_fmt] - truncate_signal(in_file, cut_in_file) + cut_in_file = in_file.with_stem(in_file.stem + "_cut") + # with file_lock: + # if not path.exists(cut_in_file): + # truncate_signal(in_file, cut_in_file) split_pre_cmd[6] = str(cut_in_file) else: @@ -430,9 +448,9 @@ def run_external_split_rendering( if in_meta_files: split_pre_cmd[9:9] = ["-im", *in_meta_files] - run_isar_ext_rend_cmd(split_pre_cmd) + run_isar_ext_rend_cmd(split_pre_cmd, test_info=test_info) - # run split renderer + # run ISAR post-renderer split_post_cmd = SPLIT_POST_REND_CMD[:] if test_info.config.option.create_ref: @@ -450,7 +468,7 @@ def run_external_split_rendering( if plc_error_pattern: split_post_cmd[1:1] = ["-prbfi", str(plc_error_pattern)] - run_isar_ext_rend_cmd(split_post_cmd) + run_isar_ext_rend_cmd(split_post_cmd, test_info=test_info) if test_info.config.option.create_cut: # CUT creation mode will run a comparison with REF