Loading lib_isar/lib_isar_pre_rend.c +19 −0 Original line number Diff line number Diff line Loading @@ -153,10 +153,29 @@ ivas_error ISAR_PRE_REND_open( } else { #ifndef FIX_RENDER_FRAME_SIZE_IN_RTP_SR int16_t iNumBlocksPerFrame; iNumBlocksPerFrame = ( CLDFB_NO_COL_MAX * pSplitRendConfig->codec_frame_size_ms ) / 20; #else int16_t iNumBlocksPerFrame, iNumLCLDIterationsPerFrame; if ( pSplitRendConfig->codec_frame_size_ms > 0 ) { iNumLCLDIterationsPerFrame = (int16_t) pSplitRendConfig->isar_frame_size_ms / ( pSplitRendConfig->codec_frame_size_ms ); iNumLCLDIterationsPerFrame = max( 1, iNumLCLDIterationsPerFrame ); iNumBlocksPerFrame = CLDFB_NO_COL_MAX * pSplitRendConfig->codec_frame_size_ms / 20; } else { iNumLCLDIterationsPerFrame = 1; iNumBlocksPerFrame = CLDFB_NO_COL_MAX; } #endif #ifndef FIX_RENDER_FRAME_SIZE_IN_RTP_SR if ( ( error = isar_splitBinLCLDEncOpen( &hSplitBinRend->hSplitBinLCLDEnc, output_Fs, BINAURAL_CHANNELS, isar_get_lcld_bitrate( pSplitRendConfig->splitRendBitRate, hSplitBinRend->multiBinPoseData.poseCorrectionMode ), iNumBlocksPerFrame, 1 ) ) != IVAS_ERR_OK ) #else if ( ( error = isar_splitBinLCLDEncOpen( &hSplitBinRend->hSplitBinLCLDEnc, output_Fs, BINAURAL_CHANNELS, isar_get_lcld_bitrate( pSplitRendConfig->splitRendBitRate, hSplitBinRend->multiBinPoseData.poseCorrectionMode ), iNumBlocksPerFrame, iNumLCLDIterationsPerFrame ) ) != IVAS_ERR_OK ) #endif { return error; } Loading tests/conftest.py +139 −117 Original line number Diff line number Diff line Loading @@ -317,6 +317,14 @@ def pytest_addoption(parser): default=1, ) parser.addoption( "--mld-playback-level", action="store", help="Playback level arg for MLD calulcation.", type=float, default=92.0, ) parser.addoption( "--split-comparison", action="store_true", Loading Loading @@ -449,6 +457,7 @@ def test_info(request): if hasattr(request, "error"): pytest.fail(request.error) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): # Use hook to catch exceptions Loading @@ -464,6 +473,7 @@ def pytest_runtest_makereport(item, call): # Capture exception in test_info test_info.error = str(call.excinfo.value) @pytest.fixture(scope="session") def split_comparison(request): return request.config.option.split_comparison Loading Loading @@ -789,7 +799,6 @@ def dut_decoder_path(request) -> str: return path class DecoderFrontend: def __init__(self, path, dec_type, record_property, timeout=None, fr=20) -> None: self._path = str(Path(path).absolute()) Loading Loading @@ -866,9 +875,7 @@ class DecoderFrontend: # TODO: centralize this in a utils file if system == "Windows": netsim_path = SCRIPTS_DIR.joinpath( "tools/Win32/networkSimulator_g192.exe" ) netsim_path = SCRIPTS_DIR.joinpath("tools/Win32/networkSimulator_g192.exe") elif system == "Linux": netsim_path = SCRIPTS_DIR.joinpath("tools/Linux/networkSimulator_g192") elif system == "Darwin": Loading Loading @@ -1049,7 +1056,6 @@ def ref_decoder_frontend( # Fixture teardown decoder._check_run() @pytest.fixture(scope="session") def test_vector_path(request) -> str: """ Loading Loading @@ -1248,7 +1254,7 @@ class MaxDiffPatternNotFound(Exception): pass def compare_dmx_signals(ref_dmx_files, dut_dmx_files, fs) -> dict: def compare_dmx_signals(ref_dmx_files, dut_dmx_files, fs, mld_playback_level) -> dict: nchannels = len(dut_dmx_files) with NamedTemporaryFile(suffix=".ref_dmx.pcm") as dmx_file_ref_tmp: with NamedTemporaryFile(suffix=".dut_dmx.pcm") as dmx_file_dut_tmp: Loading @@ -1265,6 +1271,7 @@ def compare_dmx_signals(ref_dmx_files, dut_dmx_files, fs) -> dict: in_fs=fs * 1000, ) # TODO: needs mld-playback-level dmx_differs, reason = cmp_pcm( dmx_file_ref_tmp.name, dmx_file_dut_tmp.name, Loading Loading @@ -1341,93 +1348,89 @@ def get_format_from_enc_opts(enc_opts: str) -> str: return format @pytest.fixture(scope="session") def dut_postrend_path(request) -> Optional[str]: """ Return path of DUT postrend binary. """ if request.config.option.dut_postrend_path: return request.config.option.dut_postrend_path if request.config.option.update_ref == "1": return None here = Path(__file__).parent.resolve() system = platform.system() if system == "Windows": path = here.joinpath("../ISAR_post_rend.exe") elif system in ["Darwin", "Linux"]: path = here.joinpath("../ISAR_post_rend") else: raise ValueError(f'Wrong system "{system}"!') path = str(path.resolve()) if not os.path.isfile(path): raise FileNotFoundError(f"DUT postrend binary {path} not found!\n!") return path class PostRendFrontend: def __init__(self, path, postrend_type, timeout=None, fr=20) -> None: self._path = Path(path).absolute() def __init__(self, path, postrend_type, record_property, timeout=None, fr=20) -> None: self._path = str(Path(path).absolute()) self._type = postrend_type self.returncode = None self.stdout = None self.stderr = None self.timeout = timeout self.fr = fr self.record_property = record_property def run( self, output_sampling_rate: int, input_path: Path, output_path: Path, head_trajectory: Path, metadata_input_path: Optional[Path] = None, input_file: Path, input_file_format: str, output_file: Path, input_sampling_rate: Optional[int] = 48, input_metadata: Optional[Path] = None, quiet_mode: Optional[bool] = True, bfi_file: Optional[Path] = None, trajectory_file: Optional[Path] = None, prbfi_file: Optional[Path] = None, render_frame_size: Optional[int] = None, add_option_list: Optional[list] = None, run_dir: Optional[Path] = None, ) -> None: command = [str(self._path)] command = [self._path] # add optional parameters if quiet_mode: command.extend(["-q"]) if bfi_file is not None: command.extend(["-prbfi", str(bfi_file)]) if render_frame_size: if render_frame_size not in [5, 10, 20]: raise ValueError(f"Incorrect render frame size specified {render_frame_size}!\n") command.extend(["-fr", str(render_frame_size)]) else: command.extend(["-fr", self.fr]) if input_file_format == "BINAURAL_SPLIT_PCM": # check if input sampling rate has been provided if not input_sampling_rate: raise ValueError(f"No input sampling rate specified for PCM input file {input_file}!\n") elif input_sampling_rate not in [16, 32, 48]: raise ValueError(f"Incorrect input sampling rate specified for PCM input file {input_file}!\n") command.extend(["-fs", str(input_sampling_rate)]) # check if input metadata file has been provided if not input_metadata.exists(): raise FileNotFoundError(f"The metadata file {input_metadata} for PCM input file {input_file} not found!\n") command.extend(["-im", str(input_metadata)]) if prbfi_file: if not prbfi_file.exists(): raise FileNotFoundError(f"The file {prbfi_file} not found!\n") command.extend(["-prbfi", str(prbfi_file)]) if trajectory_file: if not trajectory_file.exists(): raise FileNotFoundError(f"The file {trajectory_file} not found!\n") command.extend(["-T", str(trajectory_file)]) if add_option_list is not None: command.extend(add_option_list) if metadata_input_path is not None: # If we have metadata input file, then input format must be PCM command.extend(["-if", "BINAURAL_SPLIT_PCM"]) command.extend(["-im", str(metadata_input_path)]) else: command.extend(["-if", "BINAURAL_SPLIT_CODED"]) command.extend( [ "-fr", str(self.fr), "-fs", str(output_sampling_rate), # add mandatory parameters command += [ "-i", str(input_path), str(input_file), "-if", str(input_file_format), "-o", str(output_path), "-T", str(head_trajectory), str(output_file), "-fs", "48", # !!!!! it's a mandatory argument but should be eventually removed ] ) cmd_str = textwrap.indent(" ".join(command), prefix="\t") log_dbg_msg(f"{self._type} post-rend command:\n{cmd_str}") log_dbg_msg(f"{self._type} post-renderer command:\n{cmd_str}") try: with tempfile.TemporaryDirectory() as tmp_dir: Loading @@ -1443,20 +1446,20 @@ class PostRendFrontend: cwd=cwd, ) except TimeoutExpired: pytest.fail(f"{self._type} post-rend run timed out after {self.timeout}s.") pytest.fail(f"{self._type} post-renderer run timed out after {self.timeout}s.") self.returncode = result.returncode self.stderr = result.stderr.decode("ascii") self.stdout = result.stdout.decode("ascii") if self.stdout: stdout_str = textwrap.indent(self.stdout, prefix="\t") log_dbg_msg(f"{self._type} post-rend stdout:\n{stdout_str}") log_dbg_msg(f"{self._type} post-renderer stdout:\n{stdout_str}") if self.stderr: stderr_str = textwrap.indent(self.stderr, prefix="\t") log_dbg_msg(f"{self._type} post-rend stderr:\n{stderr_str}") log_dbg_msg(f"{self._type} post-renderer stderr:\n{stderr_str}") if self.returncode: pytest.fail( f"{self._type} post-rend terminated with a non-0 return code: {self.returncode}" f"{self._type} post-renderer terminated with a non-0 return code: {self.returncode}" ) if self.stderr and "UndefinedBehaviorSanitizer" in self.stderr: pytest.fail("Undefined Behaviour runtime error encountered") Loading @@ -1465,39 +1468,12 @@ class PostRendFrontend: if self.returncode is not None: if self.returncode: pytest.fail( f"{self._type} post-rend terminated with a non-0 return code: {self.returncode}" f"{self._type} post-renderer terminated with a non-0 return code: {self.returncode}" ) else: logger.warning("%s post-rend was set-up, but not run", self._type) logger.warning("%s post-renderer was set-up, but not run", self._type) # next assert is not OK since stderr contains messages even when decoding was successful # assert not self.stderr, self._type + " decoder stderr is not empty" @pytest.fixture(scope="function") def dut_postrend_frontend(dut_postrend_path, request) -> Optional[PostRendFrontend]: """ Return a :class:`conftest.PostRendFrontend` instance as DUT for the test session. """ postrend = None if dut_postrend_path and request.node.funcargs["out_format"] in [ "BINAURAL_SPLIT_CODED", "BINAURAL_SPLIT_PCM", ]: timeout = request.config.getoption("--testcase_timeout") postrend = PostRendFrontend( dut_postrend_path, "DUT", timeout=timeout, fr=request.config.option.dut_fr, ) yield postrend # Fixture teardown if postrend is not None: postrend._check_run() # assert not self.stderr, self._type + " post-renderer stderr is not empty" @pytest.fixture(scope="session") def ref_postrend_path(request) -> Optional[str]: Loading @@ -1507,9 +1483,6 @@ def ref_postrend_path(request) -> Optional[str]: if request.config.option.ref_postrend_path: return request.config.option.ref_postrend_path if request.config.option.update_ref == "0": return None here = Path(__file__).parent.resolve() system = platform.system() Loading @@ -1529,20 +1502,42 @@ def ref_postrend_path(request) -> Optional[str]: @pytest.fixture(scope="function") def ref_postrend_frontend(ref_postrend_path, request) -> Optional[PostRendFrontend]: def ref_postrend_frontend(ref_postrend_path, request, record_property) -> Union[None, PostRendFrontend]: """ Return a :class:`conftest.PostRendFrontend` instance as REF for the test session. """ postrend = None if ref_postrend_path and request.node.funcargs["out_format"] in [ "BINAURAL_SPLIT_CODED", "BINAURAL_SPLIT_PCM", ]: if ref_postrend_path: timeout = request.config.getoption("--testcase_timeout") postrend = PostRendFrontend( ref_postrend_path, "REF", record_property, timeout=timeout, fr=request.config.option.dut_fr, ) yield postrend # Fixture teardown if postrend is not None: postrend._check_run() @pytest.fixture(scope="function") def dut_postrend_frontend(dut_postrend_path, request, record_property) -> Union[None, PostRendFrontend]: """ Return a :class:`conftest.PostRendFrontend` instance as DUT for the test session. """ postrend = None if dut_postrend_path: timeout = request.config.getoption("--testcase_timeout") postrend = PostRendFrontend( dut_postrend_path, "DUT", record_property, timeout=timeout, fr=request.config.option.dut_fr, ) Loading @@ -1552,3 +1547,30 @@ def ref_postrend_frontend(ref_postrend_path, request) -> Optional[PostRendFronte # Fixture teardown if postrend is not None: postrend._check_run() @pytest.fixture(scope="session") def dut_postrend_path(request) -> Optional[str]: """ Return path of DUT postrend binary. """ if request.config.option.dut_postrend_path: return request.config.option.dut_postrend_path here = Path(__file__).parent.resolve() system = platform.system() if system == "Windows": path = here.joinpath("../ISAR_post_rend.exe") elif system in ["Darwin", "Linux"]: path = here.joinpath("../ISAR_post_rend") else: raise ValueError(f'Wrong system "{system}"!') path = str(path.resolve()) if not os.path.isfile(path): raise FileNotFoundError(f"DUT postrend binary {path} not found!\n!") return path No newline at end of file tests/rtp/test_rtp.py +153 −18 Original line number Diff line number Diff line Loading @@ -67,7 +67,7 @@ from pyaudio3dtools.audiofile import readfile ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) sys.path.append(ROOT_DIR) from tests.conftest import EncoderFrontend, DecoderFrontend from tests.conftest import EncoderFrontend, DecoderFrontend, PostRendFrontend @pytest.mark.parametrize("dtx", [False, True]) Loading @@ -89,6 +89,7 @@ def test_rtp_bitstream_amrwb( "MONO", dtx, framesPerPacket, False, dut_encoder_frontend, dut_decoder_frontend, ) Loading Loading @@ -117,6 +118,7 @@ def test_rtp_bitstream_evs( "MONO", dtx, framesPerPacket, False, dut_encoder_frontend, dut_decoder_frontend, ) Loading @@ -143,6 +145,7 @@ def test_rtp_bitstream_ivas_nodtx( format, False, framesPerPacket, False, dut_encoder_frontend, dut_decoder_frontend, ) Loading @@ -169,11 +172,51 @@ def test_rtp_bitstream_ivas_dtx( format, True, framesPerPacket, False, dut_encoder_frontend, dut_decoder_frontend, ) @pytest.mark.parametrize("bitrate", [48000]) @pytest.mark.parametrize("bandwidth", ["FB"]) @pytest.mark.parametrize("format", ["SBA"]) @pytest.mark.parametrize("framesPerPacket", [8]) @pytest.mark.parametrize("splitRenderingCodec", ["LCLD", "LC3PLUS"]) @pytest.mark.parametrize("dof", [0, 1]) @pytest.mark.parametrize("codecFrameSize", [5, 10, 20]) @pytest.mark.parametrize("splitRendering_format", ["BINAURAL_SPLIT_CODED"]) def test_rtp_bitstream_isar( test_info, bitrate: int, bandwidth: str, format: str, framesPerPacket: int, dut_encoder_frontend: EncoderFrontend, dut_decoder_frontend: DecoderFrontend, splitRendering_format: str, dut_postrend_frontend: PostRendFrontend, splitRenderingCodec: str, dof: int, codecFrameSize: int, ): run_rtp_bitstream_tests( CODECS.IVAS, bitrate, bandwidth, "OFF", format, False, framesPerPacket, dut_encoder_frontend, dut_decoder_frontend, splitRendering_format, dut_postrend_frontend, splitRenderingCodec, dof, codecFrameSize, ) def generateRequests(startTs: int, endTs: int) -> dict: requests = dict() return requests Loading Loading @@ -556,6 +599,16 @@ class TVARGS: self.deviceFile = ( Path(ROOT_DIR).joinpath("scripts/trajectories/headrot-1.5s.csv").absolute() ) self.preRendHeadRotFile = ( Path(ROOT_DIR).joinpath("scripts/trajectories/rotate_euler_quaternion_30s_delayed.csv").absolute() ) self.postRendHeadRotFile = ( Path(ROOT_DIR).joinpath("scripts/trajectories/rotate_euler_quaternion_30s.csv").absolute() ) # self.renderConfigFile = ( # Path(ROOT_DIR).joinpath("tests/split_rendering/renderer_configs/framing/framing_lcld_0dof_20ms.txt").absolute() # ) def add(self, fmt: str, inputFile: str, args: list[str] = []): inputFile = str(TVARGS.TVROOT.joinpath(inputFile).absolute()) Loading @@ -577,6 +630,11 @@ class TVARGS: ] return args def renderConfigFile(self, sr_codec: str, dof: int, frame_size: int): return ( Path(ROOT_DIR).joinpath(f"tests/split_rendering/renderer_configs/framing/framing_{sr_codec}_{dof}dof_{frame_size}ms.txt").absolute() ) def piFiles(self) -> tuple[Path]: return (self.sceneFile, self.deviceFile) Loading @@ -591,6 +649,11 @@ def run_rtp_bitstream_tests( framesPerPacket: int, dut_encoder_frontend: EncoderFrontend, dut_decoder_frontend: DecoderFrontend, splitRendering_format: Optional[str] = None, dut_postrend_frontend: Optional[PostRendFrontend] = None, splitRenderingCodec: Optional[str] = None, dof: Optional[int] = None, codecFrameSize: Optional[int] = None, ): tvArgs = TVARGS() tvArgs.add("MONO", "stv48n.wav") Loading @@ -609,6 +672,16 @@ def run_rtp_bitstream_tests( ): pytest.skip() if dof == 0: dut_decoder_frontend.fr = codecFrameSize else: dut_decoder_frontend.fr = 20 renderConfigFile = tvArgs.renderConfigFile(splitRenderingCodec, dof, codecFrameSize) if not renderConfigFile.exists(): # skip, if the corresponding render config file does not exist pytest.skip() print( "Test: dut_encoder_frontend={}, dtx={}, codec:={}, bitrate={}, bandwidth={}, caMode={}, format={},".format( dut_encoder_frontend._path, dtx, codec, bitrate, bandwidth, caMode, format Loading @@ -635,6 +708,16 @@ def run_rtp_bitstream_tests( .joinpath(f"input-{codec}-{bitrate}-{caMode}-{format}-{dtx}.rtpdump") .absolute() ) rtpdumpSplitOut = ( Path(tmp_dir) .joinpath(f"split-{codec}-{bitrate}-{caMode}-{format}-{dtx}.rtpdump") .absolute() ) splitOut = ( Path(tmp_dir) .joinpath(f"split-{codec}-{bitrate}-{caMode}-{format}-{dtx}.cod") .absolute() ) pcmOut = ( Path(tmp_dir) .joinpath(f"output-{codec}-{bitrate}-{caMode}-{format}-{dtx}.wav") Loading @@ -652,6 +735,7 @@ def run_rtp_bitstream_tests( ) # Run WITHOUT rtpdump first to generate reference bitstream # ./IVAS_cod -sba +1 -dtx -rtpdump 8 48000 48 scripts/testv/WYZX_Harvard_Sentences_1-13s_48khz.wav sba.rtp dut_encoder_frontend.run( bitrate=bitrate, input_sampling_rate=48, Loading @@ -666,20 +750,29 @@ def run_rtp_bitstream_tests( packer = IvasRtp(numFramesPerPacket=framesPerPacket, codec=codec) split_rendering = False if codec == CODECS.IVAS: if splitRendering_format: split_rendering = True outMode = splitRendering_format else: outMode = "STEREO" if format == "STEREO" else "BINAURAL" generatedPIData = generatePiData(0, 16000) generatedRequests = generateRequests(0, 1600) else: outMode = "" generatedPIData = dict() generatedRequests = dict() packer.packG192File( g192File=g192Out, rtpDumpOut=rtpdumpIn, piData=generatedPIData, requestsData=generateRequests(0, 1600), requestsData=generatedRequests, ) if not split_rendering: # run with G.192 bitstream dut_decoder_frontend.run( output_config=outMode, output_sampling_rate=48, Loading @@ -688,6 +781,7 @@ def run_rtp_bitstream_tests( add_option_list=[], ) # run with RTP dump dut_decoder_frontend.run( output_config=outMode, output_sampling_rate=48, Loading @@ -696,10 +790,51 @@ def run_rtp_bitstream_tests( add_option_list=["-VOIP_HF_ONLY=1", "-PiDataFile", str(piDataOutJson)], ) else: # run with G.192 bitstream # ./IVAS_dec -fr 20 -no_delay_cmp -render_config tests/split_rendering/renderer_configs/framing/framing_lcld_0dof_20ms.txt BINAURAL_SPLIT_CODED 48 sba.g192 outSR_0dof_20ms.cod dut_decoder_frontend.run( output_config=outMode, output_sampling_rate=48, input_bitstream_path=g192Out, output_path=splitOut, add_option_list=["-no_delay_cmp", "-render_config", str(renderConfigFile)], ) # ./ISAR_post_rend -fr 20 -no_delay_cmp -fs 48 -i outSR_0dof_20ms.cod -if BINAURAL_SPLIT_CODED -o output_SR_0dof_20ms.g192.wav -T scripts/trajectories/rotate_euler_quaternion_30s.csv dut_postrend_frontend.run( input_file=splitOut, input_file_format=outMode, output_file=pcmOutG192, render_frame_size=dut_decoder_frontend.fr, add_option_list=["-no_delay_cmp", "-T", str(tvArgs.postRendHeadRotFile)], ) # run with RTP dump # ./IVAS_dec -rtpOutSR -VOIP_HF_ONLY=1 -no_delay_cmp -render_config tests/split_rendering/renderer_configs/framing/framing_lcld_0dof_20ms.txt BINAURAL_SPLIT_CODED 48 sba.rtp outSR_0dof_20ms.rtp dut_decoder_frontend.run( output_config=outMode, output_sampling_rate=48, input_bitstream_path=rtpdumpIn, output_path=rtpdumpSplitOut, add_option_list=["-rtpOutSR", "-VOIP_HF_ONLY=1", "-no_delay_cmp", "-render_config", str(renderConfigFile), "-PiDataFile", str(piDataOutJson)], ) # ./ISAR_post_rend -fr 20 -no_delay_cmp -fs 48 -s outSR_0dof_20ms.rtp.sr.txt -i outSR_0dof_20ms.rtp -if RTPDUMP -o output_SR_0dof_20ms.wav -T scripts/trajectories/rotate_euler_quaternion_30s.csv dut_postrend_frontend.run( input_file=rtpdumpSplitOut, input_file_format="RTPDUMP", output_file=pcmOut, render_frame_size=dut_decoder_frontend.fr, add_option_list=["-no_delay_cmp", "-s", str(rtpdumpSplitOut.with_name(rtpdumpSplitOut.name + '.sr.txt')) , "-T", str(tvArgs.postRendHeadRotFile)], ) decAudio, fs = readfile(pcmOut) g192Audio, Fs = readfile(pcmOutG192) decAudio /= 32768.0 # readfile reuturns 16 bit int g192Audio /= 32768.0 # readfile reuturns 16 bit int if not split_rendering: decAudio = decAudio[4 * 960 :] assert abs(decAudio.shape[0] - g192Audio.shape[0]) <= ( 4 * 960 Loading Loading
lib_isar/lib_isar_pre_rend.c +19 −0 Original line number Diff line number Diff line Loading @@ -153,10 +153,29 @@ ivas_error ISAR_PRE_REND_open( } else { #ifndef FIX_RENDER_FRAME_SIZE_IN_RTP_SR int16_t iNumBlocksPerFrame; iNumBlocksPerFrame = ( CLDFB_NO_COL_MAX * pSplitRendConfig->codec_frame_size_ms ) / 20; #else int16_t iNumBlocksPerFrame, iNumLCLDIterationsPerFrame; if ( pSplitRendConfig->codec_frame_size_ms > 0 ) { iNumLCLDIterationsPerFrame = (int16_t) pSplitRendConfig->isar_frame_size_ms / ( pSplitRendConfig->codec_frame_size_ms ); iNumLCLDIterationsPerFrame = max( 1, iNumLCLDIterationsPerFrame ); iNumBlocksPerFrame = CLDFB_NO_COL_MAX * pSplitRendConfig->codec_frame_size_ms / 20; } else { iNumLCLDIterationsPerFrame = 1; iNumBlocksPerFrame = CLDFB_NO_COL_MAX; } #endif #ifndef FIX_RENDER_FRAME_SIZE_IN_RTP_SR if ( ( error = isar_splitBinLCLDEncOpen( &hSplitBinRend->hSplitBinLCLDEnc, output_Fs, BINAURAL_CHANNELS, isar_get_lcld_bitrate( pSplitRendConfig->splitRendBitRate, hSplitBinRend->multiBinPoseData.poseCorrectionMode ), iNumBlocksPerFrame, 1 ) ) != IVAS_ERR_OK ) #else if ( ( error = isar_splitBinLCLDEncOpen( &hSplitBinRend->hSplitBinLCLDEnc, output_Fs, BINAURAL_CHANNELS, isar_get_lcld_bitrate( pSplitRendConfig->splitRendBitRate, hSplitBinRend->multiBinPoseData.poseCorrectionMode ), iNumBlocksPerFrame, iNumLCLDIterationsPerFrame ) ) != IVAS_ERR_OK ) #endif { return error; } Loading
tests/conftest.py +139 −117 Original line number Diff line number Diff line Loading @@ -317,6 +317,14 @@ def pytest_addoption(parser): default=1, ) parser.addoption( "--mld-playback-level", action="store", help="Playback level arg for MLD calulcation.", type=float, default=92.0, ) parser.addoption( "--split-comparison", action="store_true", Loading Loading @@ -449,6 +457,7 @@ def test_info(request): if hasattr(request, "error"): pytest.fail(request.error) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): # Use hook to catch exceptions Loading @@ -464,6 +473,7 @@ def pytest_runtest_makereport(item, call): # Capture exception in test_info test_info.error = str(call.excinfo.value) @pytest.fixture(scope="session") def split_comparison(request): return request.config.option.split_comparison Loading Loading @@ -789,7 +799,6 @@ def dut_decoder_path(request) -> str: return path class DecoderFrontend: def __init__(self, path, dec_type, record_property, timeout=None, fr=20) -> None: self._path = str(Path(path).absolute()) Loading Loading @@ -866,9 +875,7 @@ class DecoderFrontend: # TODO: centralize this in a utils file if system == "Windows": netsim_path = SCRIPTS_DIR.joinpath( "tools/Win32/networkSimulator_g192.exe" ) netsim_path = SCRIPTS_DIR.joinpath("tools/Win32/networkSimulator_g192.exe") elif system == "Linux": netsim_path = SCRIPTS_DIR.joinpath("tools/Linux/networkSimulator_g192") elif system == "Darwin": Loading Loading @@ -1049,7 +1056,6 @@ def ref_decoder_frontend( # Fixture teardown decoder._check_run() @pytest.fixture(scope="session") def test_vector_path(request) -> str: """ Loading Loading @@ -1248,7 +1254,7 @@ class MaxDiffPatternNotFound(Exception): pass def compare_dmx_signals(ref_dmx_files, dut_dmx_files, fs) -> dict: def compare_dmx_signals(ref_dmx_files, dut_dmx_files, fs, mld_playback_level) -> dict: nchannels = len(dut_dmx_files) with NamedTemporaryFile(suffix=".ref_dmx.pcm") as dmx_file_ref_tmp: with NamedTemporaryFile(suffix=".dut_dmx.pcm") as dmx_file_dut_tmp: Loading @@ -1265,6 +1271,7 @@ def compare_dmx_signals(ref_dmx_files, dut_dmx_files, fs) -> dict: in_fs=fs * 1000, ) # TODO: needs mld-playback-level dmx_differs, reason = cmp_pcm( dmx_file_ref_tmp.name, dmx_file_dut_tmp.name, Loading Loading @@ -1341,93 +1348,89 @@ def get_format_from_enc_opts(enc_opts: str) -> str: return format @pytest.fixture(scope="session") def dut_postrend_path(request) -> Optional[str]: """ Return path of DUT postrend binary. """ if request.config.option.dut_postrend_path: return request.config.option.dut_postrend_path if request.config.option.update_ref == "1": return None here = Path(__file__).parent.resolve() system = platform.system() if system == "Windows": path = here.joinpath("../ISAR_post_rend.exe") elif system in ["Darwin", "Linux"]: path = here.joinpath("../ISAR_post_rend") else: raise ValueError(f'Wrong system "{system}"!') path = str(path.resolve()) if not os.path.isfile(path): raise FileNotFoundError(f"DUT postrend binary {path} not found!\n!") return path class PostRendFrontend: def __init__(self, path, postrend_type, timeout=None, fr=20) -> None: self._path = Path(path).absolute() def __init__(self, path, postrend_type, record_property, timeout=None, fr=20) -> None: self._path = str(Path(path).absolute()) self._type = postrend_type self.returncode = None self.stdout = None self.stderr = None self.timeout = timeout self.fr = fr self.record_property = record_property def run( self, output_sampling_rate: int, input_path: Path, output_path: Path, head_trajectory: Path, metadata_input_path: Optional[Path] = None, input_file: Path, input_file_format: str, output_file: Path, input_sampling_rate: Optional[int] = 48, input_metadata: Optional[Path] = None, quiet_mode: Optional[bool] = True, bfi_file: Optional[Path] = None, trajectory_file: Optional[Path] = None, prbfi_file: Optional[Path] = None, render_frame_size: Optional[int] = None, add_option_list: Optional[list] = None, run_dir: Optional[Path] = None, ) -> None: command = [str(self._path)] command = [self._path] # add optional parameters if quiet_mode: command.extend(["-q"]) if bfi_file is not None: command.extend(["-prbfi", str(bfi_file)]) if render_frame_size: if render_frame_size not in [5, 10, 20]: raise ValueError(f"Incorrect render frame size specified {render_frame_size}!\n") command.extend(["-fr", str(render_frame_size)]) else: command.extend(["-fr", self.fr]) if input_file_format == "BINAURAL_SPLIT_PCM": # check if input sampling rate has been provided if not input_sampling_rate: raise ValueError(f"No input sampling rate specified for PCM input file {input_file}!\n") elif input_sampling_rate not in [16, 32, 48]: raise ValueError(f"Incorrect input sampling rate specified for PCM input file {input_file}!\n") command.extend(["-fs", str(input_sampling_rate)]) # check if input metadata file has been provided if not input_metadata.exists(): raise FileNotFoundError(f"The metadata file {input_metadata} for PCM input file {input_file} not found!\n") command.extend(["-im", str(input_metadata)]) if prbfi_file: if not prbfi_file.exists(): raise FileNotFoundError(f"The file {prbfi_file} not found!\n") command.extend(["-prbfi", str(prbfi_file)]) if trajectory_file: if not trajectory_file.exists(): raise FileNotFoundError(f"The file {trajectory_file} not found!\n") command.extend(["-T", str(trajectory_file)]) if add_option_list is not None: command.extend(add_option_list) if metadata_input_path is not None: # If we have metadata input file, then input format must be PCM command.extend(["-if", "BINAURAL_SPLIT_PCM"]) command.extend(["-im", str(metadata_input_path)]) else: command.extend(["-if", "BINAURAL_SPLIT_CODED"]) command.extend( [ "-fr", str(self.fr), "-fs", str(output_sampling_rate), # add mandatory parameters command += [ "-i", str(input_path), str(input_file), "-if", str(input_file_format), "-o", str(output_path), "-T", str(head_trajectory), str(output_file), "-fs", "48", # !!!!! it's a mandatory argument but should be eventually removed ] ) cmd_str = textwrap.indent(" ".join(command), prefix="\t") log_dbg_msg(f"{self._type} post-rend command:\n{cmd_str}") log_dbg_msg(f"{self._type} post-renderer command:\n{cmd_str}") try: with tempfile.TemporaryDirectory() as tmp_dir: Loading @@ -1443,20 +1446,20 @@ class PostRendFrontend: cwd=cwd, ) except TimeoutExpired: pytest.fail(f"{self._type} post-rend run timed out after {self.timeout}s.") pytest.fail(f"{self._type} post-renderer run timed out after {self.timeout}s.") self.returncode = result.returncode self.stderr = result.stderr.decode("ascii") self.stdout = result.stdout.decode("ascii") if self.stdout: stdout_str = textwrap.indent(self.stdout, prefix="\t") log_dbg_msg(f"{self._type} post-rend stdout:\n{stdout_str}") log_dbg_msg(f"{self._type} post-renderer stdout:\n{stdout_str}") if self.stderr: stderr_str = textwrap.indent(self.stderr, prefix="\t") log_dbg_msg(f"{self._type} post-rend stderr:\n{stderr_str}") log_dbg_msg(f"{self._type} post-renderer stderr:\n{stderr_str}") if self.returncode: pytest.fail( f"{self._type} post-rend terminated with a non-0 return code: {self.returncode}" f"{self._type} post-renderer terminated with a non-0 return code: {self.returncode}" ) if self.stderr and "UndefinedBehaviorSanitizer" in self.stderr: pytest.fail("Undefined Behaviour runtime error encountered") Loading @@ -1465,39 +1468,12 @@ class PostRendFrontend: if self.returncode is not None: if self.returncode: pytest.fail( f"{self._type} post-rend terminated with a non-0 return code: {self.returncode}" f"{self._type} post-renderer terminated with a non-0 return code: {self.returncode}" ) else: logger.warning("%s post-rend was set-up, but not run", self._type) logger.warning("%s post-renderer was set-up, but not run", self._type) # next assert is not OK since stderr contains messages even when decoding was successful # assert not self.stderr, self._type + " decoder stderr is not empty" @pytest.fixture(scope="function") def dut_postrend_frontend(dut_postrend_path, request) -> Optional[PostRendFrontend]: """ Return a :class:`conftest.PostRendFrontend` instance as DUT for the test session. """ postrend = None if dut_postrend_path and request.node.funcargs["out_format"] in [ "BINAURAL_SPLIT_CODED", "BINAURAL_SPLIT_PCM", ]: timeout = request.config.getoption("--testcase_timeout") postrend = PostRendFrontend( dut_postrend_path, "DUT", timeout=timeout, fr=request.config.option.dut_fr, ) yield postrend # Fixture teardown if postrend is not None: postrend._check_run() # assert not self.stderr, self._type + " post-renderer stderr is not empty" @pytest.fixture(scope="session") def ref_postrend_path(request) -> Optional[str]: Loading @@ -1507,9 +1483,6 @@ def ref_postrend_path(request) -> Optional[str]: if request.config.option.ref_postrend_path: return request.config.option.ref_postrend_path if request.config.option.update_ref == "0": return None here = Path(__file__).parent.resolve() system = platform.system() Loading @@ -1529,20 +1502,42 @@ def ref_postrend_path(request) -> Optional[str]: @pytest.fixture(scope="function") def ref_postrend_frontend(ref_postrend_path, request) -> Optional[PostRendFrontend]: def ref_postrend_frontend(ref_postrend_path, request, record_property) -> Union[None, PostRendFrontend]: """ Return a :class:`conftest.PostRendFrontend` instance as REF for the test session. """ postrend = None if ref_postrend_path and request.node.funcargs["out_format"] in [ "BINAURAL_SPLIT_CODED", "BINAURAL_SPLIT_PCM", ]: if ref_postrend_path: timeout = request.config.getoption("--testcase_timeout") postrend = PostRendFrontend( ref_postrend_path, "REF", record_property, timeout=timeout, fr=request.config.option.dut_fr, ) yield postrend # Fixture teardown if postrend is not None: postrend._check_run() @pytest.fixture(scope="function") def dut_postrend_frontend(dut_postrend_path, request, record_property) -> Union[None, PostRendFrontend]: """ Return a :class:`conftest.PostRendFrontend` instance as DUT for the test session. """ postrend = None if dut_postrend_path: timeout = request.config.getoption("--testcase_timeout") postrend = PostRendFrontend( dut_postrend_path, "DUT", record_property, timeout=timeout, fr=request.config.option.dut_fr, ) Loading @@ -1552,3 +1547,30 @@ def ref_postrend_frontend(ref_postrend_path, request) -> Optional[PostRendFronte # Fixture teardown if postrend is not None: postrend._check_run() @pytest.fixture(scope="session") def dut_postrend_path(request) -> Optional[str]: """ Return path of DUT postrend binary. """ if request.config.option.dut_postrend_path: return request.config.option.dut_postrend_path here = Path(__file__).parent.resolve() system = platform.system() if system == "Windows": path = here.joinpath("../ISAR_post_rend.exe") elif system in ["Darwin", "Linux"]: path = here.joinpath("../ISAR_post_rend") else: raise ValueError(f'Wrong system "{system}"!') path = str(path.resolve()) if not os.path.isfile(path): raise FileNotFoundError(f"DUT postrend binary {path} not found!\n!") return path No newline at end of file
tests/rtp/test_rtp.py +153 −18 Original line number Diff line number Diff line Loading @@ -67,7 +67,7 @@ from pyaudio3dtools.audiofile import readfile ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) sys.path.append(ROOT_DIR) from tests.conftest import EncoderFrontend, DecoderFrontend from tests.conftest import EncoderFrontend, DecoderFrontend, PostRendFrontend @pytest.mark.parametrize("dtx", [False, True]) Loading @@ -89,6 +89,7 @@ def test_rtp_bitstream_amrwb( "MONO", dtx, framesPerPacket, False, dut_encoder_frontend, dut_decoder_frontend, ) Loading Loading @@ -117,6 +118,7 @@ def test_rtp_bitstream_evs( "MONO", dtx, framesPerPacket, False, dut_encoder_frontend, dut_decoder_frontend, ) Loading @@ -143,6 +145,7 @@ def test_rtp_bitstream_ivas_nodtx( format, False, framesPerPacket, False, dut_encoder_frontend, dut_decoder_frontend, ) Loading @@ -169,11 +172,51 @@ def test_rtp_bitstream_ivas_dtx( format, True, framesPerPacket, False, dut_encoder_frontend, dut_decoder_frontend, ) @pytest.mark.parametrize("bitrate", [48000]) @pytest.mark.parametrize("bandwidth", ["FB"]) @pytest.mark.parametrize("format", ["SBA"]) @pytest.mark.parametrize("framesPerPacket", [8]) @pytest.mark.parametrize("splitRenderingCodec", ["LCLD", "LC3PLUS"]) @pytest.mark.parametrize("dof", [0, 1]) @pytest.mark.parametrize("codecFrameSize", [5, 10, 20]) @pytest.mark.parametrize("splitRendering_format", ["BINAURAL_SPLIT_CODED"]) def test_rtp_bitstream_isar( test_info, bitrate: int, bandwidth: str, format: str, framesPerPacket: int, dut_encoder_frontend: EncoderFrontend, dut_decoder_frontend: DecoderFrontend, splitRendering_format: str, dut_postrend_frontend: PostRendFrontend, splitRenderingCodec: str, dof: int, codecFrameSize: int, ): run_rtp_bitstream_tests( CODECS.IVAS, bitrate, bandwidth, "OFF", format, False, framesPerPacket, dut_encoder_frontend, dut_decoder_frontend, splitRendering_format, dut_postrend_frontend, splitRenderingCodec, dof, codecFrameSize, ) def generateRequests(startTs: int, endTs: int) -> dict: requests = dict() return requests Loading Loading @@ -556,6 +599,16 @@ class TVARGS: self.deviceFile = ( Path(ROOT_DIR).joinpath("scripts/trajectories/headrot-1.5s.csv").absolute() ) self.preRendHeadRotFile = ( Path(ROOT_DIR).joinpath("scripts/trajectories/rotate_euler_quaternion_30s_delayed.csv").absolute() ) self.postRendHeadRotFile = ( Path(ROOT_DIR).joinpath("scripts/trajectories/rotate_euler_quaternion_30s.csv").absolute() ) # self.renderConfigFile = ( # Path(ROOT_DIR).joinpath("tests/split_rendering/renderer_configs/framing/framing_lcld_0dof_20ms.txt").absolute() # ) def add(self, fmt: str, inputFile: str, args: list[str] = []): inputFile = str(TVARGS.TVROOT.joinpath(inputFile).absolute()) Loading @@ -577,6 +630,11 @@ class TVARGS: ] return args def renderConfigFile(self, sr_codec: str, dof: int, frame_size: int): return ( Path(ROOT_DIR).joinpath(f"tests/split_rendering/renderer_configs/framing/framing_{sr_codec}_{dof}dof_{frame_size}ms.txt").absolute() ) def piFiles(self) -> tuple[Path]: return (self.sceneFile, self.deviceFile) Loading @@ -591,6 +649,11 @@ def run_rtp_bitstream_tests( framesPerPacket: int, dut_encoder_frontend: EncoderFrontend, dut_decoder_frontend: DecoderFrontend, splitRendering_format: Optional[str] = None, dut_postrend_frontend: Optional[PostRendFrontend] = None, splitRenderingCodec: Optional[str] = None, dof: Optional[int] = None, codecFrameSize: Optional[int] = None, ): tvArgs = TVARGS() tvArgs.add("MONO", "stv48n.wav") Loading @@ -609,6 +672,16 @@ def run_rtp_bitstream_tests( ): pytest.skip() if dof == 0: dut_decoder_frontend.fr = codecFrameSize else: dut_decoder_frontend.fr = 20 renderConfigFile = tvArgs.renderConfigFile(splitRenderingCodec, dof, codecFrameSize) if not renderConfigFile.exists(): # skip, if the corresponding render config file does not exist pytest.skip() print( "Test: dut_encoder_frontend={}, dtx={}, codec:={}, bitrate={}, bandwidth={}, caMode={}, format={},".format( dut_encoder_frontend._path, dtx, codec, bitrate, bandwidth, caMode, format Loading @@ -635,6 +708,16 @@ def run_rtp_bitstream_tests( .joinpath(f"input-{codec}-{bitrate}-{caMode}-{format}-{dtx}.rtpdump") .absolute() ) rtpdumpSplitOut = ( Path(tmp_dir) .joinpath(f"split-{codec}-{bitrate}-{caMode}-{format}-{dtx}.rtpdump") .absolute() ) splitOut = ( Path(tmp_dir) .joinpath(f"split-{codec}-{bitrate}-{caMode}-{format}-{dtx}.cod") .absolute() ) pcmOut = ( Path(tmp_dir) .joinpath(f"output-{codec}-{bitrate}-{caMode}-{format}-{dtx}.wav") Loading @@ -652,6 +735,7 @@ def run_rtp_bitstream_tests( ) # Run WITHOUT rtpdump first to generate reference bitstream # ./IVAS_cod -sba +1 -dtx -rtpdump 8 48000 48 scripts/testv/WYZX_Harvard_Sentences_1-13s_48khz.wav sba.rtp dut_encoder_frontend.run( bitrate=bitrate, input_sampling_rate=48, Loading @@ -666,20 +750,29 @@ def run_rtp_bitstream_tests( packer = IvasRtp(numFramesPerPacket=framesPerPacket, codec=codec) split_rendering = False if codec == CODECS.IVAS: if splitRendering_format: split_rendering = True outMode = splitRendering_format else: outMode = "STEREO" if format == "STEREO" else "BINAURAL" generatedPIData = generatePiData(0, 16000) generatedRequests = generateRequests(0, 1600) else: outMode = "" generatedPIData = dict() generatedRequests = dict() packer.packG192File( g192File=g192Out, rtpDumpOut=rtpdumpIn, piData=generatedPIData, requestsData=generateRequests(0, 1600), requestsData=generatedRequests, ) if not split_rendering: # run with G.192 bitstream dut_decoder_frontend.run( output_config=outMode, output_sampling_rate=48, Loading @@ -688,6 +781,7 @@ def run_rtp_bitstream_tests( add_option_list=[], ) # run with RTP dump dut_decoder_frontend.run( output_config=outMode, output_sampling_rate=48, Loading @@ -696,10 +790,51 @@ def run_rtp_bitstream_tests( add_option_list=["-VOIP_HF_ONLY=1", "-PiDataFile", str(piDataOutJson)], ) else: # run with G.192 bitstream # ./IVAS_dec -fr 20 -no_delay_cmp -render_config tests/split_rendering/renderer_configs/framing/framing_lcld_0dof_20ms.txt BINAURAL_SPLIT_CODED 48 sba.g192 outSR_0dof_20ms.cod dut_decoder_frontend.run( output_config=outMode, output_sampling_rate=48, input_bitstream_path=g192Out, output_path=splitOut, add_option_list=["-no_delay_cmp", "-render_config", str(renderConfigFile)], ) # ./ISAR_post_rend -fr 20 -no_delay_cmp -fs 48 -i outSR_0dof_20ms.cod -if BINAURAL_SPLIT_CODED -o output_SR_0dof_20ms.g192.wav -T scripts/trajectories/rotate_euler_quaternion_30s.csv dut_postrend_frontend.run( input_file=splitOut, input_file_format=outMode, output_file=pcmOutG192, render_frame_size=dut_decoder_frontend.fr, add_option_list=["-no_delay_cmp", "-T", str(tvArgs.postRendHeadRotFile)], ) # run with RTP dump # ./IVAS_dec -rtpOutSR -VOIP_HF_ONLY=1 -no_delay_cmp -render_config tests/split_rendering/renderer_configs/framing/framing_lcld_0dof_20ms.txt BINAURAL_SPLIT_CODED 48 sba.rtp outSR_0dof_20ms.rtp dut_decoder_frontend.run( output_config=outMode, output_sampling_rate=48, input_bitstream_path=rtpdumpIn, output_path=rtpdumpSplitOut, add_option_list=["-rtpOutSR", "-VOIP_HF_ONLY=1", "-no_delay_cmp", "-render_config", str(renderConfigFile), "-PiDataFile", str(piDataOutJson)], ) # ./ISAR_post_rend -fr 20 -no_delay_cmp -fs 48 -s outSR_0dof_20ms.rtp.sr.txt -i outSR_0dof_20ms.rtp -if RTPDUMP -o output_SR_0dof_20ms.wav -T scripts/trajectories/rotate_euler_quaternion_30s.csv dut_postrend_frontend.run( input_file=rtpdumpSplitOut, input_file_format="RTPDUMP", output_file=pcmOut, render_frame_size=dut_decoder_frontend.fr, add_option_list=["-no_delay_cmp", "-s", str(rtpdumpSplitOut.with_name(rtpdumpSplitOut.name + '.sr.txt')) , "-T", str(tvArgs.postRendHeadRotFile)], ) decAudio, fs = readfile(pcmOut) g192Audio, Fs = readfile(pcmOutG192) decAudio /= 32768.0 # readfile reuturns 16 bit int g192Audio /= 32768.0 # readfile reuturns 16 bit int if not split_rendering: decAudio = decAudio[4 * 960 :] assert abs(decAudio.shape[0] - g192Audio.shape[0]) <= ( 4 * 960 Loading