Tools: Testbench: Add capture direction and multi-pipeline run

This patch changes testbench to retrieve pipeline direction
from pipeline host direction. Earlier all pipelines were forced
to playback.

The topology parsing filters the parsed pipelines to command line
specified pipeline IDs. All the pipeline operations are done
looped to these pipelines to be able to simulate multiple simultaneous
pipelines.

The pipeline context struct remains single. So the simulation works
only for pipelines with identical PCM format and rate.

Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
This commit is contained in:
Seppo Ingalsuo 2022-03-18 19:45:26 +02:00 committed by Liam Girdwood
parent d09844ab98
commit 69c69d01a4
8 changed files with 221 additions and 121 deletions

View File

@ -245,6 +245,7 @@ struct sof_ipc_comp_file {
char *fn;
uint32_t mode;
uint32_t frame_fmt;
uint32_t direction; /**< SOF_IPC_STREAM_ */
} __attribute__((packed, aligned(4)));
/* frees components, buffers and pipelines

View File

@ -98,6 +98,7 @@ struct ipc_comp_file {
char *fn;
uint32_t mode;
uint32_t frame_fmt;
uint32_t direction; /**< SOF_IPC_STREAM_ */
} __attribute__((packed, aligned(4)));
#endif

View File

@ -217,6 +217,7 @@ static void comp_specific_builder(struct sof_ipc_comp *comp,
config->file.frame_fmt = file->frame_fmt;
config->file.mode = file->mode;
config->file.rate = file->rate;
config->file.direction = file->direction;
break;
#else
case SOF_COMP_HOST:

View File

@ -97,32 +97,26 @@ void tb_free(struct sof *sof)
free(sof->ipc);
}
/* set up pcm params, prepare and trigger pipeline */
int tb_pipeline_start(struct ipc *ipc, struct pipeline *p,
struct tplg_context *ctx)
/* Get pipeline host component */
static struct comp_dev *tb_get_pipeline_host(struct pipeline *p)
{
struct comp_dev *cd;
cd = p->source_comp;
if (cd->direction == SOF_IPC_STREAM_CAPTURE)
cd = p->sink_comp;
return cd;
}
/* set up pcm params, prepare and trigger pipeline */
int tb_pipeline_start(struct ipc *ipc, struct pipeline *p)
{
struct ipc_comp_dev *pcm_dev;
struct comp_dev *cd;
int ret;
/* set up pipeline params */
ret = tb_pipeline_params(ipc, p, ctx);
if (ret < 0) {
fprintf(stderr, "error: pipeline params failed: %s\n",
strerror(ret));
return ret;
}
/* Get IPC component device for pipeline */
pcm_dev = ipc_get_comp_by_id(ipc, p->sched_id);
if (!pcm_dev) {
fprintf(stderr, "error: ipc get comp failed: %s\n",
strerror(ret));
return ret;
}
/* Point to pipeline component device */
cd = pcm_dev->cd;
/* Get pipeline host component */
cd = tb_get_pipeline_host(p);
/* Component prepare */
ret = pipeline_prepare(p, cd);
@ -133,7 +127,7 @@ int tb_pipeline_start(struct ipc *ipc, struct pipeline *p,
}
/* Start the pipeline */
ret = pipeline_trigger(p, cd, COMP_TRIGGER_PRE_START);
ret = pipeline_trigger(cd->pipeline, cd, COMP_TRIGGER_PRE_START);
if (ret < 0) {
fprintf(stderr, "error: Failed to start pipeline command: %s\n",
strerror(ret));
@ -144,24 +138,15 @@ int tb_pipeline_start(struct ipc *ipc, struct pipeline *p,
}
/* set up pcm params, prepare and trigger pipeline */
int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p,
struct tplg_context *ctx)
int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p)
{
struct ipc_comp_dev *pcm_dev;
struct comp_dev *cd;
int ret;
/* Get IPC component device for pipeline */
pcm_dev = ipc_get_comp_by_id(ipc, p->sched_id);
if (!pcm_dev) {
fprintf(stderr, "error: ipc get comp failed\n");
return -EINVAL;
}
/* Get pipeline host component */
cd = tb_get_pipeline_host(p);
/* Point to pipeline component device */
cd = pcm_dev->cd;
ret = pipeline_trigger(p, cd, COMP_TRIGGER_STOP);
ret = pipeline_trigger(cd->pipeline, cd, COMP_TRIGGER_STOP);
if (ret < 0) {
fprintf(stderr, "error: Failed to stop pipeline command: %s\n",
strerror(ret));
@ -171,22 +156,13 @@ int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p,
}
/* set up pcm params, prepare and trigger pipeline */
int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p,
struct tplg_context *ctx)
int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p)
{
struct ipc_comp_dev *pcm_dev;
struct comp_dev *cd;
int ret;
/* Get IPC component device for pipeline */
pcm_dev = ipc_get_comp_by_id(ipc, p->sched_id);
if (!pcm_dev) {
fprintf(stderr, "error: ipc get comp failed\n");
return -EINVAL;
}
/* Point to pipeline component device */
cd = pcm_dev->cd;
/* Get pipeline host component */
cd = tb_get_pipeline_host(p);
ret = pipeline_reset(p, cd);
if (ret < 0)
@ -223,7 +199,6 @@ int tb_pipeline_params(struct ipc *ipc, struct pipeline *p,
params.comp_id = p->comp_id;
params.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
params.params.frame_fmt = ctx->frame_fmt;
params.params.direction = SOF_IPC_STREAM_PLAYBACK;
params.params.rate = ctx->fs_in;
params.params.channels = ctx->channels_in;
@ -255,8 +230,11 @@ int tb_pipeline_params(struct ipc *ipc, struct pipeline *p,
return -EINVAL;
}
/* point to pipeline component device */
cd = pcm_dev->cd;
/* Get pipeline host component */
cd = tb_get_pipeline_host(p);
/* Set pipeline params direction from scheduling component */
params.params.direction = cd->direction;
/* pipeline params */
ret = pipeline_params(p, cd, &params);

View File

@ -587,6 +587,7 @@ static struct comp_dev *file_new(const struct comp_driver *drv,
cd->rate = ipc_file->rate;
cd->channels = ipc_file->channels;
cd->frame_fmt = ipc_file->frame_fmt;
dev->direction = ipc_file->direction;
/* open file handle(s) depending on mode */
switch (cd->fs.mode) {

View File

@ -21,6 +21,7 @@
#define DEBUG_MSG_LEN 1024
#define MAX_LIB_NAME_LEN 1024
#define MAX_INPUT_FILE_NUM 16
#define MAX_OUTPUT_FILE_NUM 16
/* number of widgets types supported in testbench */
@ -36,8 +37,9 @@ struct tplg_context;
*/
struct testbench_prm {
char *tplg_file; /* topology file to use */
char *input_file; /* input file name */
char *input_file[MAX_INPUT_FILE_NUM]; /* input file names */
char *output_file[MAX_OUTPUT_FILE_NUM]; /* output file names */
int input_file_num; /* number of input files */
int output_file_num; /* number of output files */
char *bits_in; /* input bit format */
int pipelines[MAX_OUTPUT_FILE_NUM]; /* output file names */
@ -58,6 +60,7 @@ struct testbench_prm {
FILE *file;
char *pipeline_string;
int output_file_index;
int input_file_index;
/* global cmd line args that can override topology */
enum sof_ipc_frame cmd_frame_fmt;
@ -89,17 +92,14 @@ void sys_comp_filewrite_init(void);
int tb_setup(struct sof *sof, struct testbench_prm *tp);
void tb_free(struct sof *sof);
int tb_pipeline_start(struct ipc *ipc, struct pipeline *p,
struct tplg_context *ctx);
int tb_pipeline_start(struct ipc *ipc, struct pipeline *p);
int tb_pipeline_params(struct ipc *ipc, struct pipeline *p,
struct tplg_context *ctx);
int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p,
struct tplg_context *ctx);
int tb_pipeline_stop(struct ipc *ipc, struct pipeline *p);
int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p,
struct tplg_context *ctx);
int tb_pipeline_reset(struct ipc *ipc, struct pipeline *p);
void debug_print(char *message);

View File

@ -117,6 +117,36 @@ static int parse_output_files(char *outputs, struct testbench_prm *tp)
return 0;
}
/*
* Parse inputfilenames from user input
*/
static int parse_input_files(char *inputs, struct testbench_prm *tp)
{
char *input_token = NULL;
char *token = strtok_r(inputs, ",", &input_token);
int index;
for (index = 0; index < MAX_INPUT_FILE_NUM && token; index++) {
/* get input file name with current index */
tp->input_file[index] = strdup(token);
/* next input */
token = strtok_r(NULL, ",", &input_token);
}
if (index == MAX_INPUT_FILE_NUM && token) {
fprintf(stderr, "error: max input file number is %d\n",
MAX_INPUT_FILE_NUM);
for (index = 0; index < MAX_INPUT_FILE_NUM; index++)
free(tp->input_file[index]);
return -EINVAL;
}
/* set total input file number */
tp->input_file_num = index;
return 0;
}
static int parse_pipelines(char *pipelines, struct testbench_prm *tp)
{
char *output_token = NULL;
@ -355,7 +385,7 @@ static int parse_input_args(int argc, char **argv, struct testbench_prm *tp)
switch (option) {
/* input sample file */
case 'i':
tp->input_file = strdup(optarg);
ret = parse_input_files(optarg, tp);
break;
/* output sample files */
@ -456,81 +486,143 @@ static int parse_input_args(int argc, char **argv, struct testbench_prm *tp)
return ret;
}
static int test_pipeline_stop(struct pipeline_thread_data *ptdata,
struct tplg_context *ctx)
static struct pipeline *get_pipeline_by_id(int id)
{
struct ipc_comp_dev *pcm_dev;
struct pipeline *p;
struct ipc *ipc = sof_get()->ipc;
pcm_dev = ipc_get_comp_by_id(sof_get()->ipc, ctx->sched_id);
p = pcm_dev->cd->pipeline;
return tb_pipeline_stop(sof_get()->ipc, p, ctx);
pcm_dev = ipc_get_ppl_src_comp(ipc, id);
return pcm_dev->cd->pipeline;
}
static int test_pipeline_reset(struct pipeline_thread_data *ptdata,
struct tplg_context *ctx)
static int test_pipeline_stop(struct pipeline_thread_data *ptdata)
{
struct ipc_comp_dev *pcm_dev;
struct testbench_prm *tp = ptdata->tp;
struct pipeline *p;
struct ipc *ipc = sof_get()->ipc;
int ret = 0;
int i;
pcm_dev = ipc_get_comp_by_id(sof_get()->ipc, ctx->sched_id);
p = pcm_dev->cd->pipeline;
for (i = 0; i < tp->pipeline_num; i++) {
p = get_pipeline_by_id(tp->pipelines[i]);
ret = tb_pipeline_stop(ipc, p);
if (ret < 0)
break;
}
return tb_pipeline_reset(sof_get()->ipc, p, ctx);
return ret;
}
static int test_pipeline_start(struct pipeline_thread_data *ptdata,
struct tplg_context *ctx)
static int test_pipeline_reset(struct pipeline_thread_data *ptdata)
{
struct testbench_prm *tp = ptdata->tp;
struct pipeline *p;
struct ipc *ipc = sof_get()->ipc;
int ret = 0;
int i;
for (i = 0; i < tp->pipeline_num; i++) {
p = get_pipeline_by_id(tp->pipelines[i]);
ret = tb_pipeline_reset(ipc, p);
if (ret < 0)
break;
}
return ret;
}
static void test_pipeline_free(struct pipeline_thread_data *ptdata)
{
struct testbench_prm *tp = ptdata->tp;
int i;
for (i = 0; i < tp->pipeline_num; i++)
test_pipeline_free_comps(tp->pipelines[i]);
}
static int test_pipeline_params(struct pipeline_thread_data *ptdata, struct tplg_context *ctx)
{
struct testbench_prm *tp = ptdata->tp;
struct ipc_comp_dev *pcm_dev;
struct pipeline *p;
struct ipc *ipc = sof_get()->ipc;
int ret = 0;
int i;
/* Run pipeline until EOF from fileread */
pcm_dev = ipc_get_comp_by_id(sof_get()->ipc, ctx->sched_id);
if (!pcm_dev) {
fprintf(stderr, "error: pipeline has no scheduling component %d\n",
ctx->sched_id);
return -EINVAL;
for (i = 0; i < tp->pipeline_num; i++) {
pcm_dev = ipc_get_ppl_src_comp(ipc, tp->pipelines[i]);
if (!pcm_dev) {
fprintf(stderr, "error: pipeline %d has no source component\n",
tp->pipelines[i]);
return -EINVAL;
}
/* set up pipeline params */
p = pcm_dev->cd->pipeline;
/* input and output sample rate */
if (!ctx->fs_in)
ctx->fs_in = p->period * p->frames_per_sched;
if (!ctx->fs_out)
ctx->fs_out = p->period * p->frames_per_sched;
ret = tb_pipeline_params(ipc, p, ctx);
if (ret < 0) {
fprintf(stderr, "error: pipeline params failed: %s\n",
strerror(ret));
return ret;
}
}
p = pcm_dev->cd->pipeline;
/* input and output sample rate */
if (!ctx->fs_in)
ctx->fs_in = p->period * p->frames_per_sched;
return 0;
}
if (!ctx->fs_out)
ctx->fs_out = p->period * p->frames_per_sched;
static int test_pipeline_start(struct pipeline_thread_data *ptdata)
{
struct testbench_prm *tp = ptdata->tp;
struct pipeline *p;
struct ipc *ipc = sof_get()->ipc;
int i;
/* do we need to apply copy count limit ? */
if (tp->copy_check)
test_pipeline_set_test_limits(ctx->pipeline_id, tp->copy_iterations, 0);
/* Run pipeline until EOF from fileread */
for (i = 0; i < tp->pipeline_num; i++) {
p = get_pipeline_by_id(tp->pipelines[i]);
/* set pipeline params and trigger start */
if (tb_pipeline_start(sof_get()->ipc, p, ctx) < 0) {
fprintf(stderr, "error: pipeline params\n");
return -EINVAL;
/* do we need to apply copy count limit ? */
if (tp->copy_check)
test_pipeline_set_test_limits(tp->pipelines[i], tp->copy_iterations, 0);
/* set pipeline params and trigger start */
if (tb_pipeline_start(ipc, p) < 0) {
fprintf(stderr, "error: pipeline params\n");
return -EINVAL;
}
}
return 0;
}
static int test_pipeline_get_state(struct pipeline_thread_data *ptdata,
struct tplg_context *ctx)
static bool test_pipeline_check_state(struct pipeline_thread_data *ptdata, int state)
{
struct ipc_comp_dev *pcm_dev;
struct testbench_prm *tp = ptdata->tp;
struct pipeline *p;
int i;
/* Run pipeline until EOF from fileread */
pcm_dev = ipc_get_comp_by_id(sof_get()->ipc, ctx->sched_id);
p = pcm_dev->cd->pipeline;
return p->pipe_task->state;
for (i = 0; i < tp->pipeline_num; i++) {
p = get_pipeline_by_id(tp->pipelines[i]);
if (p->pipe_task->state == state)
return true;
}
return false;
}
static int test_pipeline_load(struct pipeline_thread_data *ptdata,
struct tplg_context *ctx, int pipeline_id)
static int test_pipeline_load(struct pipeline_thread_data *ptdata, struct tplg_context *ctx)
{
struct testbench_prm *tp = ptdata->tp;
int ret;
@ -543,7 +635,6 @@ static int test_pipeline_load(struct pipeline_thread_data *ptdata,
ctx->sof = sof_get();
ctx->tp = tp;
ctx->tplg_file = tp->tplg_file;
ctx->pipeline_id = pipeline_id;
ctx->fs_in = tp->cmd_fs_in;
ctx->fs_out = tp->cmd_fs_out;
ctx->channels_in = tp->cmd_channels_in;
@ -558,12 +649,6 @@ static int test_pipeline_load(struct pipeline_thread_data *ptdata,
return ret;
}
static void test_pipeline_free(struct pipeline_thread_data *ptdata,
struct tplg_context *ctx, int pipeline_id)
{
test_pipeline_free_comps(pipeline_id);
}
static void test_pipeline_stats(struct pipeline_thread_data *ptdata,
struct tplg_context *ctx, uint64_t delta)
{
@ -622,6 +707,10 @@ static void test_pipeline_stats(struct pipeline_thread_data *ptdata,
printf("Input bit format: %s\n", tp->bits_in);
printf("Input sample rate: %d\n", ctx->fs_in);
printf("Output sample rate: %d\n", ctx->fs_out);
for (i = 0; i < tp->input_file_num; i++) {
printf("Input[%d] read from file: \"%s\"\n",
i, tp->input_file[i]);
}
for (i = 0; i < tp->output_file_num; i++) {
printf("Output[%d] written to file: \"%s\"\n",
i, tp->output_file[i]);
@ -659,14 +748,21 @@ static void *pipline_test(void *data)
printf(" Test Start %d\n", dp_count);
printf("==========================================================\n");
err = test_pipeline_load(ptdata, &ctx, tp->pipelines[0]);
err = test_pipeline_load(ptdata, &ctx);
if (err < 0) {
fprintf(stderr, "error: pipeline load %d failed %d\n",
dp_count, err);
break;
}
err = test_pipeline_start(ptdata, &ctx);
err = test_pipeline_params(ptdata, &ctx);
if (err < 0) {
fprintf(stderr, "error: pipeline params %d failed %d\n",
dp_count, err);
break;
}
err = test_pipeline_start(ptdata);
if (err < 0) {
fprintf(stderr, "error: pipeline run %d failed %d\n",
dp_count, err);
@ -691,8 +787,7 @@ static void *pipline_test(void *data)
err = nanosleep(&ts, &ts);
if (err == 0) {
nsleep_time += tp->tick_period_us; /* sleep fully completed */
if (test_pipeline_get_state(ptdata, &ctx) ==
SOF_TASK_STATE_CANCEL) {
if (test_pipeline_check_state(ptdata, SOF_TASK_STATE_CANCEL)) {
fprintf(stdout, "pipeline cancelled !\n");
break;
}
@ -707,7 +802,7 @@ static void *pipline_test(void *data)
}
clock_gettime(CLOCK_MONOTONIC, &td1);
err = test_pipeline_stop(ptdata, &ctx);
err = test_pipeline_stop(ptdata);
if (err < 0) {
fprintf(stderr, "error: pipeline stop %d failed %d\n",
dp_count, err);
@ -718,14 +813,14 @@ static void *pipline_test(void *data)
delta += (td1.tv_nsec - td0.tv_nsec) / 1000;
test_pipeline_stats(ptdata, &ctx, delta);
err = test_pipeline_reset(ptdata, &ctx);
err = test_pipeline_reset(ptdata);
if (err < 0) {
fprintf(stderr, "error: pipeline stop %d failed %d\n",
dp_count, err);
break;
}
test_pipeline_free(ptdata, &ctx, tp->pipelines[0]);
test_pipeline_free(ptdata);
ptdata->count++;
dp_count++;
@ -746,12 +841,15 @@ int main(int argc, char **argv)
tp.cmd_fs_in = 0;
tp.cmd_fs_out = 0;
tp.bits_in = 0;
tp.input_file = NULL;
tp.tplg_file = NULL;
tp.input_file_num = 0;
tp.output_file_num = 0;
for (i = 0; i < MAX_OUTPUT_FILE_NUM; i++)
tp.output_file[i] = NULL;
for (i = 0; i < MAX_INPUT_FILE_NUM; i++)
tp.input_file[i] = NULL;
tp.cmd_channels_in = TESTBENCH_NCH;
tp.cmd_channels_out = 0;
tp.max_pipeline_id = 0;
@ -781,8 +879,8 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
if (!tp.input_file) {
fprintf(stderr, "input audio file not specified, use -i file\n");
if (!tp.input_file_num) {
fprintf(stderr, "input files not specified, use -i file1,file2\n");
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
@ -844,10 +942,13 @@ join:
out:
/* free all other data */
free(tp.bits_in);
free(tp.input_file);
free(tp.tplg_file);
for (i = 0; i < tp.output_file_num; i++)
free(tp.output_file[i]);
for (i = 0; i < tp.input_file_num; i++)
free(tp.input_file[i]);
free(tp.pipeline_string);
#ifdef TESTBENCH_CACHE_CHECK

View File

@ -245,16 +245,19 @@ static int load_fileread(struct tplg_context *ctx, int dir)
}
/* configure fileread */
fileread.fn = strdup(tp->input_file);
fileread.fn = strdup(tp->input_file[tp->input_file_index]);
if (tp->input_file_index == 0)
tp->fr_id = ctx->comp_id;
/* use fileread comp as scheduling comp */
tp->fr_id = ctx->comp_id;
ctx->sched_id = ctx->comp_id;
tp->input_file_index++;
/* Set format from testbench command line*/
fileread.rate = ctx->fs_in;
fileread.channels = ctx->channels_in;
fileread.frame_fmt = ctx->frame_fmt;
fileread.direction = dir;
/* Set type depending on direction */
fileread.comp.type = (dir == SOF_IPC_STREAM_PLAYBACK) ?
@ -304,6 +307,7 @@ static int load_filewrite(struct tplg_context *ctx, int dir)
filewrite.rate = ctx->fs_out;
filewrite.channels = ctx->channels_out;
filewrite.frame_fmt = ctx->frame_fmt;
filewrite.direction = dir;
/* Set type depending on direction */
filewrite.comp.type = (dir == SOF_IPC_STREAM_PLAYBACK) ?
@ -361,6 +365,7 @@ int parse_topology(struct tplg_context *ctx)
int ret = 0;
size_t file_size;
size_t size;
bool pipeline_match;
/* initialize output file index */
tp->output_file_index = 0;
@ -410,7 +415,17 @@ int parse_topology(struct tplg_context *ctx)
ctx->hdr = hdr;
if (hdr->index != ctx->pipeline_id) {
pipeline_match = false;
for (i = 0; i < tp->pipeline_num; i++) {
if (hdr->index == tp->pipelines[i]) {
pipeline_match = true;
break;
}
}
if (!pipeline_match) {
sprintf(message, "skipped pipeline %d\n", hdr->index);
debug_print(message);
next = get_next_hdr(ctx, hdr, file_size);
if (next < 0)
goto out;
@ -418,6 +433,7 @@ int parse_topology(struct tplg_context *ctx)
continue;
else
goto finish;
}
/* parse header and load the next block based on type */
@ -431,6 +447,7 @@ int parse_topology(struct tplg_context *ctx)
debug_print(message);
/* update max pipeline_id */
ctx->pipeline_id = hdr->index;
if (hdr->index > tp->max_pipeline_id)
tp->max_pipeline_id = hdr->index;