Commit 45293309 authored by Jan Kiene's avatar Jan Kiene
Browse files

use programmatically generated br sw patterns in encoder

parent 8c5ff7c6
Loading
Loading
Loading
Loading
+112 −3
Original line number Diff line number Diff line
@@ -28,8 +28,11 @@ accordance with the laws of the Federal Republic of Germany excluding its confli
the United Nations Convention on Contracts on the International Sales of Goods.
"""

from itertools import product
from itertools import product, permutations
from typing import Optional
import random
from pathlib import Path
import numpy as np
import os

HERE = Path(__file__).parent
@@ -58,6 +61,105 @@ def get_file_from_repo(path, to_str=True):
    return str(filepath) if to_str else filepath


def random_choice(
    values: list, n, allow_repetitions: bool = False, last_init=None, seed=None
):
    """
    Generator for randomly picking from a list of values.
    """
    assert len(values) > 1

    # create copy to be safe against external modifications
    values = list(values)
    last = last_init
    if seed is not None:
        random.seed(seed)

    for i in range(n):
        while ((curr := random.choice(values)) == last) and not allow_repetitions:
            pass
        yield curr
        last = curr


def create_br_switching_file(
    bitrates: np.ndarray,
    strategy: str,
    switch_time: int = 1,
    length_frames: int = 0,
    seed: Optional[int] = None,
    starting_br: Optional[int] = None,
):
    """
    Create bitrate switching pattern files on the fly and return path to it

    bitrates - array of bitrate values to include
    strategy - how to create pattern:
        "from_array" - use given array as is
        "exhaustive" - generate array where every bitrate is preceeded and followed at least once by every other bitrate
        "random" - randomly pick from bitrates
    switch_time - number of frames before next switch
    length_frames - for "random" strategy: length of pattern in frames
    seed - for "exhaustive" and "random" strategies: inject seed for shuffling/choosing from array
    starting_br - for "exhaustive" and "random" strategies: start pattern with given bitrate, has to be contained in bitrates
    """
    STRATEGIES = ["from_array", "exhaustive", "random"]
    assert strategy in STRATEGIES
    assert starting_br is None or starting_br in bitrates

    brs = np.asarray(bitrates)

    # if no seed given, compute it from other parameters, so that same params => same seed
    if seed is None:
        seed = brs.sum() + STRATEGIES.index(strategy) + switch_time + length_frames
    random.seed(seed)

    seed_str = f"_{seed}" if strategy == "random" or strategy == "exhaustive" else ""
    length_str = f"-l{length_frames}" if length_frames > 0 else ""
    start_str = f"-start_{starting_br}" if starting_br is not None else ""
    fname = f"br_sw_pattern-{strategy}{seed_str}-{switch_time}{length_str}{start_str}"

    if strategy == "exhaustive":
        n = len(brs) - 1
        permuts = list(permutations(bitrates, 2))
        split_permuts = [permuts[i * n : (i + 1) * n] for i in range(len(brs))]
        for i in range(len(split_permuts)):
            random.shuffle(split_permuts[i])
        split_permuts = np.asarray(split_permuts)
        brs = np.concatenate([split_permuts[:, i] for i in range(n)])

        if starting_br is not None:
            # brs is still a 2-D array here
            # find first row with starting br in first column, then roll array by that amount
            i_row, i_col = np.where(brs == starting_br)
            idx_col = np.where(i_col == 0)[0][0]
            shift = i_row[idx_col]
            brs = np.roll(brs, -shift, axis=0)

    elif strategy == "random":
        brs = list()

        # if starting bitrate is given, put it at first place and sample one br less
        if starting_br is not None:
            brs = [starting_br]
            length_frames -= 1

        rand_gen = random_choice(
            bitrates,
            length_frames,
            allow_repetitions=False,
            last_init=starting_br,
            seed=seed,
        )
        brs += [x for x in rand_gen]

    brs = np.repeat(brs, switch_time).astype(np.int32)
    out_path = DUT_PATH.joinpath(fname)
    brs.tofile(out_path)

    return out_path


DTX_ON = "DTXon"
DTX_OFF = "DTXoff"

@@ -207,8 +309,15 @@ BITRATES_ISM4_EXTENDED = [b for b in BITRATES_ISM4 if b >= 64000]
BITRATES_EVS = [5900, 7200, 8000, 9600] + BITRATES_ALL[:6] + BITRATES_ALL[7:9]

### Bitrate switching files
BR_SW_FILE_STEREO = get_file_from_repo(
    "scripts/switchPaths/sw_13k2_128k.bin", to_str=False
BR_SW_START_STEREO = [13200, 32000, 48000, 80000, 256000]
BR_SW_FILES_STEREO = dict(
    (
        start_br,
        create_br_switching_file(
            BITRATES_STEREO, "exhaustive", switch_time=5, starting_br=start_br
        ),
    )
    for start_br in BR_SW_START_STEREO
)

SAMPLING_RATES_ALL = [16, 32, 48]
+0 −98
Original line number Diff line number Diff line
@@ -49,104 +49,6 @@ from ..constants import (
)


def random_choice(
    values: list, n, allow_repetitions: bool = False, last_init=None, seed=None
):
    """
    Generator for randomly picking from a list of values.
    """
    assert len(values) > 1

    # create copy to be safe against external modifications
    values = list(values)
    last = last_init
    if seed is not None:
        random.seed(seed)

    for i in range(n):
        while ((curr := random.choice(values)) == last) and not allow_repetitions:
            pass
        yield curr
        last = curr


def create_br_switching_file(
    bitrates: np.ndarray,
    strategy: str,
    switch_time: int = 1,
    length_frames: int = 0,
    seed: Optional[int] = None,
    starting_br: Optional[int] = None,
):
    """
    Create bitrate switching pattern files on the fly and return path to it

    bitrates - array of bitrate values to include
    strategy - how to create pattern:
        "from_array" - use given array as is
        "exhaustive" - generate array where every bitrate is preceeded and followed at least once by every other bitrate
        "random" - randomly pick from bitrates
    switch_time - number of frames before next switch
    length_frames - for "random" strategy: length of pattern in frames
    seed - for "exhaustive" and "random" strategies: inject seed for shuffling/choosing from array
    starting_br - for "exhaustive" and "random" strategies: start pattern with given bitrate, has to be contained in bitrates
    """
    STRATEGIES = ["from_array", "exhaustive", "random"]
    assert strategy in STRATEGIES
    assert starting_br is None or starting_br in bitrates

    seed_str = f"_{seed}" if strategy == "random" or strategy == "exhaustive" else ""
    length_str = f"-l{length_frames}" if length_frames > 0 else ""
    fname = f"br_sw_pattern-{strategy}{seed_str}-{switch_time}{length_str}"

    brs = np.asarray(bitrates)

    # if no seed given, compute it from other parameters, so that same params => same seed
    if seed is None:
        seed = brs.sum() + STRATEGIES.index(strategy) + switch_time + length_frames
    random.seed(seed)

    if strategy == "exhaustive":
        n = len(brs) - 1
        permuts = list(permutations(bitrates, 2))
        split_permuts = [permuts[i * n : (i + 1) * n] for i in range(len(brs))]
        for i in range(len(split_permuts)):
            random.shuffle(split_permuts[i])
        split_permuts = np.asarray(split_permuts)
        brs = np.concatenate([split_permuts[:, i] for i in range(n)])

        if starting_br is not None:
            # brs is still a 2-D array here
            # find first row with starting br in first column, then roll array by that amount
            i_row, i_col = np.where(brs == starting_br)
            idx_col = np.where(i_col == 0)[0][0]
            shift = i_row[idx_col]
            brs = np.roll(brs, -shift, axis=0)

    elif strategy == "random":
        brs = list()

        # if starting bitrate is given, put it at first place and sample one br less
        if starting_br is not None:
            brs = [starting_br]
            length_frames -= 1

        rand_gen = random_choice(
            bitrates,
            length_frames,
            allow_repetitions=False,
            last_init=starting_br,
            seed=seed,
        )
        brs += [x for x in rand_gen]

    brs = np.repeat(brs, switch_time).astype(np.int32)
    out_path = DUT_PATH.joinpath(fname)
    brs.tofile(out_path)

    return out_path


def get_md(input_format, md_type=None):
    md_files = METADATA_FOR_INPUT_FORMAT.get(input_format, list())
    if md_type == ISM_MD_NULL:
+4 −2
Original line number Diff line number Diff line
@@ -30,14 +30,16 @@ the United Nations Convention on Contracts on the International Sales of Goods.

import pytest
from ..constants import *
from . import get_options, run_check, create_br_switching_file
from . import get_options, run_check
from .. import get_testv_path, get_bitstream_path


@pytest.mark.parametrize("sampling_rate", SAMPLING_RATES_ALL)
@pytest.mark.parametrize("max_band", MAX_BAND_ALL)
@pytest.mark.parametrize("dtx", [DTX_OFF, DTX_ON])
@pytest.mark.parametrize("start_bitrate", [13200, 32000, 48000, 80000, 256000])
def test_encoder_br_switching_stereo(
    start_bitrate,
    sampling_rate,
    max_band,
    dtx,
@@ -47,7 +49,7 @@ def test_encoder_br_switching_stereo(
    input_format = "STEREO"
    testv = get_testv_path(input_format, sampling_rate)
    options = get_options(input_format, max_band=max_band)
    br_sw_file = BR_SW_FILE_STEREO
    br_sw_file = BR_SW_FILES_STEREO[start_bitrate]
    run_check(
        input_format,
        br_sw_file,