Merge branch 'fixeria/lib' into master

The previous GAPK implementation was represented by a single
executable. So, all audio transcoding operations were available
only via calling the 'gapk' binary. This approach didn't allow
external applications to benefit from using GAPK API directly.

The following set of changes separates the common code into a
shared library called 'libosmogapk', linking the 'gapk' binary
against it:

  - 95e6664 Introduce a shared 'libosmogapk' library
  - 30209ce Install GAPK headers to '${includedir}/osmocom/gapk/'
  - a8d4657 Add an 'osmo_gapk' prefix to the exposed symbols
  - 40d59f1 Add a pkg-config manifest for libosmogapk
  - 4f0a47d Add the symbol export map for libosmogapk

All memory management operations are now based on talloc library:

  - 3c20dac libosmogapk: use talloc for memory management
  - 5cabe1e osmo-gapk: use talloc for memory management

Integrated Osmocom logging framework:

  - c35ba8a libosmogapk: use Osmocom logging framework
  - 4b7cd2c osmo-gapk: drop useless printf calls
  - 0fe18af osmo-gapk: use Osmocom logging framework
  - 11943bf osmo-gapk: adjust application verbosity

Integrated GNU Autotest environment and basic test coverage:

  - f069eb3 Init automake test environment
  - 1fe6a9b tests: add procqueue test
  - 3e9e57f tests: add pq_file test
  - 9d2b15d tests: add pq_rtp test
  - f59f3f1 tests: add format / codec transcoding tests

For more details, see commits history.

Change-Id: I3c6d4a9d326ee49153e4ad83823d094831c112da
This commit is contained in:
Vadim Yanitskiy 2018-01-17 20:46:06 +06:00
commit 7a79fc1169
84 changed files with 2513 additions and 592 deletions

14
.gitignore vendored
View File

@ -3,7 +3,7 @@ autoscan-2.*.log
configure.scan
# autoreconf by-products
*.in
Makefile.in
aclocal.m4
autom4te.cache/
@ -18,6 +18,7 @@ m4/
missing
# configure by-products
*.pc
.deps
Makefile
@ -35,8 +36,17 @@ stamp-h1
libgsmhr/refsrc
# GNU autotest
tests/package.m4
tests/atconfig
tests/atlocal
tests/testsuite
tests/testsuite.dir/
tests/testsuite.log
tests/*/*_test
# final executables
src/gapk
src/osmo-gapk
# temporary/backup files
*.*~

View File

@ -1,6 +1,9 @@
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
ACLOCAL_AMFLAGS = -I m4
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libosmogapk.pc
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
SUBDIRS = include
@ -9,4 +12,4 @@ if ENABLE_GSMHR
SUBDIRS += libgsmhr
endif
SUBDIRS += src
SUBDIRS += src tests

View File

@ -1,6 +1,7 @@
AC_PREREQ([2.65])
AC_INIT([gapk],
m4_esyscmd([./git-version-gen .tarball-version]), [main@lists.airprobe.org])
m4_esyscmd([./git-version-gen .tarball-version]),
[openbsc@lists.osmocom.org])
AM_INIT_AUTOMAKE([dist-bzip2 subdir-objects])
LT_INIT([disable-static])
@ -8,16 +9,18 @@ LT_INIT([disable-static])
# kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_SRCDIR([src/app_osmo_gapk.c])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_TESTDIR(tests)
AC_CONFIG_FILES([
Makefile
src/Makefile
libgsmhr/Makefile
include/Makefile
include/gapk/Makefile
include/gsmhr/Makefile
libosmogapk.pc
tests/Makefile
])
# Options
@ -45,6 +48,16 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
CFLAGS="$saved_CFLAGS"
AC_SUBST(SYMBOL_VISIBILITY)
dnl check os: some linker flags not available on osx
case $host in
*-darwin*)
;;
*)
LTLDFLAGS_OSMOGAPK='-Wl,--version-script=$(srcdir)/libosmogapk.map'
;;
esac
AC_SUBST(LTLDFLAGS_OSMOGAPK)
# Checks for programs.
AC_PROG_CC

View File

@ -1,6 +1,6 @@
#!/bin/sh
GAPK=./src/gapk
GAPK=./src/osmo-gapk
PCMFILE=$1
BASE=`basename $PCMFILE`

View File

@ -1,5 +1,18 @@
SUBDIRS = gapk
noinst_HEADERS = \
osmocom/gapk/logging.h \
osmocom/gapk/utils.h \
osmocom/gapk/bench.h \
$(NULL)
nobase_include_HEADERS = \
osmocom/gapk/get_cycles.h \
osmocom/gapk/benchmark.h \
osmocom/gapk/procqueue.h \
osmocom/gapk/formats.h \
osmocom/gapk/codecs.h \
osmocom/gapk/common.h \
$(NULL)
if ENABLE_GSMHR
SUBDIRS += gsmhr
SUBDIRS = gsmhr
endif

View File

@ -1,6 +0,0 @@
noinst_HEADERS = benchmark.h \
codecs.h \
formats.h \
get_cycles.h \
procqueue.h \
utils.h

View File

@ -1,60 +0,0 @@
#ifndef _BENCHMARK_H
#define _BENCHMARK_H
/*
* This file is part of gapk (GSM Audio Pocket Knife).
*
* gapk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gapk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*
* (C) 2014 Harald Welte <laforge@gnumonks.org>
*/
#include <gapk/get_cycles.h>
#include <gapk/codecs.h>
#define NUM_AVG 102400
struct benchmark_cycles {
cycles_t enc[NUM_AVG];
unsigned int enc_used;
cycles_t dec[NUM_AVG];
unsigned int dec_used;
};
extern struct benchmark_cycles codec_cycles[_CODEC_MAX];
static inline void benchmark_stop(enum codec_type codec, int encode, unsigned long cycles)
{
struct benchmark_cycles *bc = &codec_cycles[codec];
if (encode) {
bc->enc_used = (bc->enc_used + 1) % NUM_AVG;
bc->enc[bc->enc_used] = cycles;
} else {
bc->dec_used = (bc->dec_used + 1) % NUM_AVG;
bc->dec[bc->dec_used] = cycles;
}
}
#define BENCHMARK_START do { \
cycles_t _cycles_start, _cycles_stop; \
_cycles_start = get_cycles()
#define BENCHMARK_STOP(x,y) _cycles_stop = get_cycles(); \
benchmark_stop(x, y, _cycles_stop - _cycles_start); \
} while (0)
void benchmark_dump(void);
#endif

View File

@ -1,72 +0,0 @@
/* Processing Queue Management */
/*
* This file is part of gapk (GSM Audio Pocket Knife).
*
* gapk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gapk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GAPK_PROCQUEUE_H__
#define __GAPK_PROCQUEUE_H__
#include <stdint.h>
#include <stdio.h> /* for FILE */
struct pq;
struct pq_item {
/*! input frame size (in bytes). '0' in case of variable frames */
int len_in;
/*! output frame size (in bytes). '0' in case of variable frames */
int len_out;
/*! opaque state */
void *state;
/*! call-back for actual format conversion function
* \param[in] state opaque state pointer
* \param[out] out caller-allocated buffer for output data
* \param[in] in input data
* \param[in] in_len length of input data \a in
* \returns number of output bytes written to \a out; negative on error */
int (*proc)(void *state, uint8_t *out, const uint8_t *in, unsigned int in_len);
void (*exit)(void *state);
};
/* Management */
struct pq * pq_create(void);
void pq_destroy(struct pq *pq);
struct pq_item * pq_add_item(struct pq *pq);
int pq_prepare(struct pq *pq);
int pq_execute(struct pq *pq);
/* File */
int pq_queue_file_input(struct pq *pq, FILE *src, unsigned int block_len);
int pq_queue_file_output(struct pq *pq, FILE *dst, unsigned int block_len);
/* RTP */
int pq_queue_rtp_input(struct pq *pq, int rtp_fd, unsigned int block_len);
int pq_queue_rtp_output(struct pq *pq, int rtp_fd, unsigned int block_len);
/* ALSA */
int pq_queue_alsa_input(struct pq *pq, const char *hwdev, unsigned int blk_len);
int pq_queue_alsa_output(struct pq *pq, const char *hwdev, unsigned int blk_len);
/* Format */
struct format_desc;
int pq_queue_fmt_convert(struct pq *pq, const struct format_desc *fmt, int to_from_n);
/* Codec */
struct codec_desc;
int pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int encode);
#endif /* __GAPK_PROCQUEUE_H__ */

View File

@ -17,8 +17,7 @@
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GSM_HR_H__
#define __GSM_HR_H__
#pragma once
#include <stdint.h>
@ -36,5 +35,3 @@ int gsmhr_decode(struct gsmhr *state, int16_t *pcm, const int16_t *hr_
#ifdef __cplusplus
}
#endif
#endif /* __GSM_HR_H__ */

View File

@ -0,0 +1,49 @@
/*
* This file is part of gapk (GSM Audio Pocket Knife).
*
* (C) 2014 Harald Welte <laforge@gnumonks.org>
*
* gapk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gapk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/gapk/get_cycles.h>
#include <osmocom/gapk/benchmark.h>
#include <osmocom/gapk/codecs.h>
#define BENCHMARK_START \
do { \
cycles_t _cycles_start, _cycles_stop; \
struct osmo_gapk_bench_cycles *_bc; \
_cycles_start = get_cycles()
#define BENCHMARK_STOP(codec, encode) \
_cycles_stop = get_cycles(); \
_bc = osmo_gapk_bench_codec[codec]; \
if (!_bc) break; \
\
if (encode) { \
_bc->enc_used = (_bc->enc_used + 1) \
% OSMO_GAPK_CYCLES_NUM_AVG; \
_bc->enc[_bc->enc_used] = \
_cycles_stop - _cycles_start; \
} else { \
_bc->dec_used = (_bc->dec_used + 1) \
% OSMO_GAPK_CYCLES_NUM_AVG; \
_bc->dec[_bc->dec_used] = \
_cycles_stop - _cycles_start; \
} \
} while (0)

View File

@ -0,0 +1,42 @@
/*
* This file is part of gapk (GSM Audio Pocket Knife).
*
* gapk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gapk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*
* (C) 2014 Harald Welte <laforge@gnumonks.org>
*/
#pragma once
#include <osmocom/gapk/get_cycles.h>
#include <osmocom/gapk/codecs.h>
#define OSMO_GAPK_CYCLES_NUM_AVG 102400
struct osmo_gapk_bench_cycles {
cycles_t enc[OSMO_GAPK_CYCLES_NUM_AVG];
unsigned int enc_used;
cycles_t dec[OSMO_GAPK_CYCLES_NUM_AVG];
unsigned int dec_used;
};
extern struct osmo_gapk_bench_cycles *osmo_gapk_bench_codec[_CODEC_MAX];
int osmo_gapk_bench_enable(enum osmo_gapk_codec_type codec);
void osmo_gapk_bench_free(void);
unsigned long long
osmo_gapk_bench_get_cycles(enum osmo_gapk_codec_type codec, int enc);
unsigned int
osmo_gapk_bench_get_frames(enum osmo_gapk_codec_type codec, int enc);

View File

@ -17,8 +17,7 @@
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GAPK_CODECS_H__
#define __GAPK_CODECS_H__
#pragma once
#include <stdint.h>
@ -29,7 +28,7 @@
#define HR_REF_ENC_LEN (20 * sizeof(uint16_t))
#define HR_REF_DEC_LEN (22 * sizeof(uint16_t))
enum codec_type {
enum osmo_gapk_codec_type {
CODEC_INVALID = 0,
CODEC_PCM, /* 16 bits PCM samples */
CODEC_HR, /* GSM Half Rate codec GSM 06.20 */
@ -39,7 +38,8 @@ enum codec_type {
_CODEC_MAX,
};
#include <gapk/formats.h> /* need to import here because or enum interdep */
/* Need to import here because of enum interdep */
#include <osmocom/gapk/formats.h>
/*! call-back for actual codec conversion function
* \param[in] state opaque state pointer (returned by codec->init)
@ -47,25 +47,33 @@ enum codec_type {
* \param[in] src input data
* \param[in] src_len length of input data \a src
* \returns number of output bytes written to \a dst; negative on error */
typedef int (*codec_conv_cb_t)(void *state, uint8_t *dst, const uint8_t *src, unsigned int src_len);
typedef int (*osmo_gapk_codec_conv_cb_t)(void *state, uint8_t *dst,
const uint8_t *src, unsigned int src_len);
struct codec_desc {
enum codec_type type;
const char * name;
const char * description;
/*! canonical frame size (in bytes); 0 in case of variable length */
unsigned int canon_frame_len;
struct osmo_gapk_codec_desc {
enum osmo_gapk_codec_type type;
const char *description;
const char *name;
enum format_type codec_enc_format_type; /* what the encoder provides */
enum format_type codec_dec_format_type; /* what to give the decoder */
/*! codec initialization function pointer, returns opaque state */
void * (*codec_init)(void);
/*! codec exit function pointer, gets passed opaque state */
void (*codec_exit)(void *state);
codec_conv_cb_t codec_encode;
codec_conv_cb_t codec_decode;
/*!
* Canonical frame size (in bytes);
* 0 in case of variable length
*/
unsigned int canon_frame_len;
/*! What the encoder provides */
enum osmo_gapk_format_type codec_enc_format_type;
/*! What to give the decoder */
enum osmo_gapk_format_type codec_dec_format_type;
/* (De)initialization function pointers */
void *(*codec_init)(void);
void (*codec_exit)(void *state);
/* Encoding / decoding function pointers */
osmo_gapk_codec_conv_cb_t codec_encode;
osmo_gapk_codec_conv_cb_t codec_decode;
};
const struct codec_desc *codec_get_from_type(enum codec_type type);
#endif /* __GAPK_CODECS_H__ */
const struct osmo_gapk_codec_desc *
osmo_gapk_codec_get_from_type(enum osmo_gapk_codec_type type);

View File

@ -0,0 +1,22 @@
/*
* This file is part of gapk (GSM Audio Pocket Knife).
*
* gapk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gapk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
void osmo_gapk_set_talloc_ctx(void *ctx);
void osmo_gapk_log_init(int subsys);

View File

@ -17,12 +17,11 @@
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GAPK_FORMATS_H__
#define __GAPK_FORMATS_H__
#pragma once
#include <stdint.h>
enum format_type {
enum osmo_gapk_format_type {
FMT_INVALID = 0,
/* Classic .amr container */
@ -62,33 +61,37 @@ enum format_type {
_FMT_MAX,
};
#include <gapk/codecs.h> /* need to import here because or enum interdep */
/* Need to import here because of enum interdep */
#include <osmocom/gapk/codecs.h>
/*! call-back for actual format conversion function
* \param[out] dst caller-allocated buffer for output data
* \param[in] src input data
* \param[in] src_len length of input data \a src
* \returns number of output bytes written to \a dst; negative on error */
typedef int (*fmt_conv_cb_t)(uint8_t *dst, const uint8_t *src, unsigned int src_len);
typedef int (*osmo_gapk_fmt_conv_cb_t)(uint8_t *dst,
const uint8_t *src, unsigned int src_len);
struct format_desc {
enum format_type type;
enum codec_type codec_type;
const char * name;
const char * description;
struct osmo_gapk_format_desc {
enum osmo_gapk_format_type type;
enum osmo_gapk_codec_type codec_type;
const char *description;
const char *name;
/*! length of frames in this format (as opposed to canonical) */
unsigned int frame_len;
fmt_conv_cb_t conv_from_canon;
fmt_conv_cb_t conv_to_canon;
/*! Length of frames in this format (as opposed to canonical) */
unsigned int frame_len;
/*! length of a (global) header at start of file */
unsigned int header_len;
/*! exact match for (global) header at start of file */
const uint8_t * header;
/* Format conversation function pointers */
osmo_gapk_fmt_conv_cb_t conv_from_canon;
osmo_gapk_fmt_conv_cb_t conv_to_canon;
/*! Length of a (global) header at start of file */
unsigned int header_len;
/*! Exact match for (global) header at start of file */
const uint8_t *header;
};
const struct format_desc *fmt_get_from_type(enum format_type type);
const struct format_desc *fmt_get_from_name(const char *name);
#endif /* __GAPK_FORMATS_H__ */
const struct osmo_gapk_format_desc *
osmo_gapk_fmt_get_from_type(enum osmo_gapk_format_type type);
const struct osmo_gapk_format_desc *
osmo_gapk_fmt_get_from_name(const char *name);

View File

@ -33,8 +33,7 @@
* $Id$
*/
#ifndef GET_CLOCK_H
#define GET_CLOCK_H
#pragma once
#if 0
@ -134,5 +133,3 @@ static inline cycles_t get_cycles()
#endif
extern double get_cpu_mhz(void);
#endif /* GET_CLOCK_H */

View File

@ -0,0 +1,28 @@
/*
* This file is part of gapk (GSM Audio Pocket Knife).
*
* gapk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gapk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <osmocom/core/logging.h>
extern int gapk_log_init_complete;
extern int gapk_log_subsys;
#define LOGPGAPK(level, fmt, args...) \
if (gapk_log_init_complete) \
LOGP(gapk_log_subsys, level, fmt, ## args)

View File

@ -0,0 +1,105 @@
/* Processing Queue Management */
/*
* This file is part of gapk (GSM Audio Pocket Knife).
*
* gapk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gapk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdio.h> /* for FILE */
#include <osmocom/core/linuxlist.h>
enum osmo_gapk_pq_item_type {
OSMO_GAPK_ITEM_TYPE_SOURCE,
OSMO_GAPK_ITEM_TYPE_SINK,
OSMO_GAPK_ITEM_TYPE_PROC,
};
#define OSMO_GAPK_CAT_NAME_SOURCE "source"
#define OSMO_GAPK_CAT_NAME_SINK "sink"
#define OSMO_GAPK_CAT_NAME_PROC "proc"
struct osmo_gapk_pq_item {
/*! input frame size (in bytes). '0' in case of variable frames */
unsigned int len_in;
/*! output frame size (in bytes). '0' in case of variable frames */
unsigned int len_out;
/*! opaque state */
void *state;
/*! buffer for output data */
uint8_t *buf;
/*! call-back for actual format conversion function
* \param[in] state opaque state pointer
* \param[out] out caller-allocated buffer for output data
* \param[in] in input data
* \param[in] in_len length of input data \a in
* \returns number of output bytes written to \a out; negative on error */
int (*proc)(void *state, uint8_t *out, const uint8_t *in, unsigned int in_len);
int (*wait)(void *state);
void (*exit)(void *state);
/*! \brief link to a processing queue */
struct llist_head list;
/*! \brief type of item */
enum osmo_gapk_pq_item_type type;
/*! \brief category name (src, format, codec, sink) */
const char *cat_name;
/*! \brief sub-category name (file, rtp-amr, amr, alsa) */
const char *sub_name;
};
#define VAR_BUF_SIZE 320
struct osmo_gapk_pq {
struct llist_head items;
unsigned n_items;
/*! \brief human-readable name */
const char *name;
};
/* Processing queue management */
struct osmo_gapk_pq *osmo_gapk_pq_create(const char *name);
int osmo_gapk_pq_check(struct osmo_gapk_pq *pq, int strict);
int osmo_gapk_pq_prepare(struct osmo_gapk_pq *pq);
int osmo_gapk_pq_execute(struct osmo_gapk_pq *pq);
void osmo_gapk_pq_destroy(struct osmo_gapk_pq *pq);
char *osmo_gapk_pq_describe(struct osmo_gapk_pq *pq);
/* Processing queue item management */
struct osmo_gapk_pq_item *osmo_gapk_pq_add_item(struct osmo_gapk_pq *pq);
/* File */
int osmo_gapk_pq_queue_file_input(struct osmo_gapk_pq *pq, FILE *src, unsigned int block_len);
int osmo_gapk_pq_queue_file_output(struct osmo_gapk_pq *pq, FILE *dst, unsigned int block_len);
/* RTP */
int osmo_gapk_pq_queue_rtp_input(struct osmo_gapk_pq *pq, int rtp_fd, unsigned int block_len);
int osmo_gapk_pq_queue_rtp_output(struct osmo_gapk_pq *pq, int rtp_fd, unsigned int block_len);
/* ALSA */
int osmo_gapk_pq_queue_alsa_input(struct osmo_gapk_pq *pq, const char *hwdev, unsigned int blk_len);
int osmo_gapk_pq_queue_alsa_output(struct osmo_gapk_pq *pq, const char *hwdev, unsigned int blk_len);
/* Format */
struct osmo_gapk_format_desc;
int osmo_gapk_pq_queue_fmt_convert(struct osmo_gapk_pq *pq, const struct osmo_gapk_format_desc *fmt, int to_from_n);
/* Codec */
struct osmo_gapk_codec_desc;
int osmo_gapk_pq_queue_codec(struct osmo_gapk_pq *pq, const struct osmo_gapk_codec_desc *codec, int encode);

View File

@ -17,8 +17,7 @@
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GAPK_UTILS_H__
#define __GAPK_UTILS_H__
#pragma once
#include <stdint.h>
@ -100,5 +99,3 @@ lsb_clr_bit(uint8_t *buf, int bn)
buf[pos_byte] &= ~(1 << pos_bit);
}
#endif /* __GAPK_UTILS_H__ */

11
libosmogapk.pc.in Normal file
View File

@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: Osmocom GSM Audio Transcoding Library
Description: C Utility Library
Requires: libosmocore, libosmocodec
Version: @VERSION@
Libs: -L${libdir} -losmogapk
Cflags: -I${includedir}/

View File

@ -1,20 +1,97 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCODEC_CFLAGS) $(LIBOSMOCORE_CFLAGS) \
${OPENCORE_AMRNB_CFLAGS} $(LIBALSA_CFLAGS)
AM_LDFLAGS=$(LIBOSMOCODEC_LIBS) $(LIBOSMOCORE_LIBS) \
${OPENCORE_AMRNB_LIBS} ${LIBGSM_LIBS} $(LIBALSA_LIBS)
# This is _NOT_ the library release version, it's an API version.
# Please read Chapter 6 "Library interface versions" of the libtool
# documentation before making any modification
LIBVERSION = 0:0:0
COM_SOURCES = procqueue.c pq_file.c pq_format.c pq_codec.c pq_rtp.c pq_alsa.c \
formats.c fmt_amr.c fmt_gsm.c fmt_hr_ref.c fmt_racal.c \
fmt_amr_opencore.c \
fmt_rtp_amr.c fmt_rtp_efr.c fmt_rtp_hr_etsi.c fmt_rtp_hr_ietf.c \
fmt_rawpcm.c fmt_ti.c benchmark.c \
codecs.c codec_pcm.c codec_hr.c codec_fr.c codec_efr.c codec_amr.c
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_builddir) \
-I$(top_srcdir)/include \
$(NULL)
bin_PROGRAMS = gapk
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOCODEC_CFLAGS) \
${OPENCORE_AMRNB_CFLAGS} \
$(LIBALSA_CFLAGS) \
$(NULL)
gapk_SOURCES = main.c $(COM_SOURCES)
lib_LTLIBRARIES = libosmogapk.la
EXTRA_DIST = libosmogapk.map
libosmogapk_la_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODEC_LIBS) \
${OPENCORE_AMRNB_LIBS} \
${LIBGSM_LIBS} \
$(LIBALSA_LIBS) \
$(LTLDFLAGS_OSMOGAPK) \
-version-info $(LIBVERSION) \
-no-undefined \
$(NULL)
if ENABLE_GSMHR
gapk_LDADD = $(top_builddir)/libgsmhr/libgsmhr.la
libosmogapk_la_LIBADD = $(top_builddir)/libgsmhr/libgsmhr.la
endif
# Processing queue implementation
libosmogapk_la_SOURCES = \
procqueue.c \
pq_format.c \
pq_codec.c \
pq_file.c \
pq_alsa.c \
pq_rtp.c \
$(NULL)
# Formats implementation
libosmogapk_la_SOURCES += \
formats.c \
fmt_ti.c \
fmt_amr.c \
fmt_gsm.c \
fmt_hr_ref.c \
fmt_racal.c \
fmt_rawpcm.c \
fmt_rtp_amr.c \
fmt_rtp_efr.c \
fmt_rtp_hr_etsi.c \
fmt_rtp_hr_ietf.c \
fmt_amr_opencore.c \
$(NULL)
# Codecs implementation
libosmogapk_la_SOURCES += \
codecs.c \
codec_pcm.c \
codec_hr.c \
codec_fr.c \
codec_efr.c \
codec_amr.c \
$(NULL)
# Codec benchmarking
libosmogapk_la_SOURCES += \
benchmark.c \
$(NULL)
# Common routines
libosmogapk_la_SOURCES += \
common.c \
$(NULL)
# libosmogapk representative application
bin_PROGRAMS = osmo-gapk
osmo_gapk_SOURCES = \
app_osmo_gapk.c \
$(NULL)
osmo_gapk_LDFLAGS = \
$(LIBOSMOCORE_LIBS) \
$(NULL)
osmo_gapk_LDADD = \
$(top_builddir)/src/libosmogapk.la \
$(NULL)

View File

@ -26,18 +26,25 @@
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <talloc.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/utils.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/procqueue.h>
#include <gapk/benchmark.h>
#include <osmocom/gapk/common.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/procqueue.h>
#include <osmocom/gapk/benchmark.h>
/* The root talloc context of application */
TALLOC_CTX *app_root_ctx;
struct gapk_options
{
@ -47,7 +54,7 @@ struct gapk_options
uint16_t port;
} rtp_in;
const char *alsa_in;
const struct format_desc *fmt_in;
const struct osmo_gapk_format_desc *fmt_in;
const char *fname_out;
struct {
@ -55,14 +62,17 @@ struct gapk_options
uint16_t port;
} rtp_out;
const char *alsa_out;
const struct format_desc *fmt_out;
const struct osmo_gapk_format_desc *fmt_out;
int benchmark;
int verbose;
};
struct gapk_state
{
struct gapk_options opts;
struct pq *pq;
struct osmo_gapk_pq *pq;
int exit;
struct {
struct {
@ -83,10 +93,30 @@ struct gapk_state
} out;
};
/* Logging related routines */
enum {
DAPP,
};
static struct log_info_cat gapk_log_info_cat[] = {
[DAPP] = {
.name = "DAPP",
.description = "Application",
.color = "\033[0;36m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
static const struct log_info gapk_log_info = {
.cat = gapk_log_info_cat,
.num_cat = ARRAY_SIZE(gapk_log_info_cat),
};
static void
print_help(char *progname)
{
const struct osmo_gapk_codec_desc *codec;
int i;
/* Header */
@ -103,6 +133,8 @@ print_help(char *progname)
#endif
fprintf(stdout, " -f, --input-format=FMT\tInput format (see below)\n");
fprintf(stdout, " -g, --output-format=FMT\tOutput format (see below)\n");
fprintf(stdout, " -b, --enable-benchmark\tEnable codec benchmarking\n");
fprintf(stdout, " -v, --verbose\t\t\tEnable debug messages\n");
fprintf(stdout, "\n");
/* Print all codecs */
@ -110,7 +142,7 @@ print_help(char *progname)
fprintf(stdout, " name\tfmt enc dec\tdescription\n");
for (i=CODEC_INVALID+1; i<_CODEC_MAX; i++) {
const struct codec_desc *codec = codec_get_from_type(i);
codec = osmo_gapk_codec_get_from_type(i);
fprintf(stdout, " %4s %c %c %c \t%s\n",
codec->name,
'*',
@ -126,7 +158,7 @@ print_help(char *progname)
fprintf(stdout, "Supported formats:\n");
for (i=FMT_INVALID+1; i<_FMT_MAX; i++) {
const struct format_desc *fmt = fmt_get_from_type(i);
const struct osmo_gapk_format_desc *fmt = osmo_gapk_fmt_get_from_type(i);
fprintf(stdout, " %-19s %s\n",
fmt->name,
fmt->description
@ -171,9 +203,11 @@ parse_options(struct gapk_state *state, int argc, char *argv[])
#endif
{"input-format", 1, 0, 'f'},
{"output-format", 1, 0, 'g'},
{"enable-benchmark", 0, 0, 'b'},
{"verbose", 0, 0, 'v'},
{"help", 0, 0, 'h'},
};
const char *short_options = "i:o:I:O:f:g:h"
const char *short_options = "i:o:I:O:f:g:bvh"
#ifdef HAVE_ALSA
"a:A:"
#endif
@ -206,7 +240,7 @@ parse_options(struct gapk_state *state, int argc, char *argv[])
case 'I':
rv = parse_host_port(optarg, &opt->rtp_in.hostname);
if (rv < 0 || rv > 0xffff) {
fprintf(stderr, "[!] Invalid port: %d\n", rv);
LOGP(DAPP, LOGL_ERROR, "Invalid port: %d\n", rv);
return -EINVAL;
}
opt->rtp_in.port = rv;
@ -223,33 +257,42 @@ parse_options(struct gapk_state *state, int argc, char *argv[])
case 'O':
rv = parse_host_port(optarg, &opt->rtp_out.hostname);
if (rv < 0 || rv > 0xffff) {
fprintf(stderr, "[!] Invalid port: %d\n", rv);
LOGP(DAPP, LOGL_ERROR, "Invalid port: %d\n", rv);
return -EINVAL;
}
opt->rtp_out.port = rv;
break;
case 'f':
opt->fmt_in = fmt_get_from_name(optarg);
opt->fmt_in = osmo_gapk_fmt_get_from_name(optarg);
if (!opt->fmt_in) {
fprintf(stderr, "[!] Unsupported format: %s\n", optarg);
LOGP(DAPP, LOGL_ERROR, "Unsupported format: %s\n", optarg);
return -EINVAL;
}
break;
case 'g':
opt->fmt_out = fmt_get_from_name(optarg);
opt->fmt_out = osmo_gapk_fmt_get_from_name(optarg);
if (!opt->fmt_out) {
fprintf(stderr, "[!] Unsupported format: %s\n", optarg);
LOGP(DAPP, LOGL_ERROR, "Unsupported format: %s\n", optarg);
return -EINVAL;
}
break;
case 'b':
opt->benchmark = 1;
break;
case 'v':
log_parse_category_mask(osmo_stderr_target, "DAPP");
opt->verbose = 1;
break;
case 'h':
return 1;
default:
fprintf(stderr, "[+] Unknown option\n");
LOGP(DAPP, LOGL_ERROR, "Unknown option\n");
return -EINVAL;
}
@ -263,98 +306,157 @@ check_options(struct gapk_state *gs)
{
/* Required formats */
if (!gs->opts.fmt_in || !gs->opts.fmt_out) {
fprintf(stderr, "[!] Input and output formats are required arguments !\n");
LOGP(DAPP, LOGL_ERROR, "Input and output formats are required arguments\n");
return -EINVAL;
}
/* Transcoding */
if (gs->opts.fmt_in->codec_type != gs->opts.fmt_out->codec_type) {
const struct codec_desc *codec;
const struct osmo_gapk_codec_desc *codec;
/* Check source codec */
codec = codec_get_from_type(gs->opts.fmt_in->codec_type);
codec = osmo_gapk_codec_get_from_type(gs->opts.fmt_in->codec_type);
if (!codec) {
fprintf(stderr, "[!] Internal error: bad codec reference\n");
LOGP(DAPP, LOGL_ERROR, "Internal error: bad codec reference\n");
return -EINVAL;
}
if ((codec->type != CODEC_PCM) && !codec->codec_decode) {
fprintf(stderr, "[!] Decoding from '%s' codec is unsupported\n", codec->name);
LOGP(DAPP, LOGL_ERROR, "Decoding from '%s' codec is unsupported\n", codec->name);
return -ENOTSUP;
}
/* Check destination codec */
codec = codec_get_from_type(gs->opts.fmt_out->codec_type);
codec = osmo_gapk_codec_get_from_type(gs->opts.fmt_out->codec_type);
if (!codec) {
fprintf(stderr, "[!] Internal error: bad codec reference\n");
LOGP(DAPP, LOGL_ERROR, "Internal error: bad codec reference\n");
return -EINVAL;
}
if ((codec->type != CODEC_PCM) && !codec->codec_encode) {
fprintf(stderr, "[!] Encoding to '%s' codec is unsupported\n", codec->name);
LOGP(DAPP, LOGL_ERROR, "Encoding to '%s' codec is unsupported\n", codec->name);
return -ENOTSUP;
}
}
/* Input combinations */
if (gs->opts.fname_in && gs->opts.rtp_in.port) {
fprintf(stderr, "[!] You have to decide on either file or RTP input\n");
return -EINVAL;
}
/* Check I/O combinations */
int src_count = 0;
int sink_count = 0;
/* Output combinations */
if (gs->opts.fname_out && gs->opts.rtp_out.port) {
fprintf(stderr, "[!] You have to decide on either file or RTP output\n");
if (gs->opts.fname_in)
src_count++;
if (gs->opts.rtp_in.port)
src_count++;
if (gs->opts.alsa_in)
src_count++;
if (gs->opts.fname_out)
sink_count++;
if (gs->opts.rtp_out.port)
sink_count++;
if (gs->opts.alsa_out)
sink_count++;
if (src_count > 1 || sink_count > 1) {
LOGP(DAPP, LOGL_ERROR, "You have to decide on "
"a single input and a single output\n");
return -EINVAL;
}
return 0;
}
static void
benchmark_dump(void)
{
int i;
for (i = 0; i < _CODEC_MAX; i++) {
struct osmo_gapk_bench_cycles *bc;
unsigned long long cycles;
unsigned int frames;
/* Check if there are benchmark data */
bc = osmo_gapk_bench_codec[i];
if (!bc)
continue;
if (bc->enc_used) {
cycles = osmo_gapk_bench_get_cycles(i, 1);
frames = osmo_gapk_bench_get_frames(i, 1);
LOGP(DAPP, LOGL_NOTICE, "Codec %u (ENC): %llu cycles for %u frames"
" => %llu cycles/frame\n", i, cycles,
frames, cycles / frames);
}
if (bc->dec_used) {
cycles = osmo_gapk_bench_get_cycles(i, 0);
frames = osmo_gapk_bench_get_frames(i, 0);
LOGP(DAPP, LOGL_NOTICE, "Codec %u (DEC): %llu cycles for %u frames"
" => %llu cycles/frame\n", i, cycles,
frames, cycles / frames);
}
}
}
static int
files_open(struct gapk_state *gs)
{
LOGP(DAPP, LOGL_NOTICE, "Opening I/O streams\n");
if (gs->opts.fname_in) {
LOGP(DAPP, LOGL_NOTICE, "Using a file as source\n");
gs->in.file.fh = fopen(gs->opts.fname_in, "rb");
if (!gs->in.file.fh) {
fprintf(stderr, "[!] Error while opening input file for reading\n");
LOGP(DAPP, LOGL_ERROR, "Error while opening input file for reading\n");
perror("fopen");
return -errno;
}
} else if (gs->opts.rtp_in.port) {
LOGP(DAPP, LOGL_NOTICE, "Using RTP as source\n");
gs->in.rtp.fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM,
IPPROTO_UDP,
gs->opts.rtp_in.hostname,
gs->opts.rtp_in.port,
OSMO_SOCK_F_BIND);
if (gs->in.rtp.fd < 0) {
fprintf(stderr, "[!] Error while opening input socket\n");
LOGP(DAPP, LOGL_ERROR, "Error while opening input socket\n");
return gs->in.rtp.fd;
}
} else if (gs->opts.alsa_in) {
printf("alsa_in, not stdin\n");
} else
/* Do nothing, ALSA source does the initialization itself */
LOGP(DAPP, LOGL_NOTICE, "Using ALSA as source\n");
} else {
LOGP(DAPP, LOGL_NOTICE, "Using stdin as source\n");
gs->in.file.fh = stdin;
}
if (gs->opts.fname_out) {
LOGP(DAPP, LOGL_NOTICE, "Using a file as sink\n");
gs->out.file.fh = fopen(gs->opts.fname_out, "wb");
if (!gs->out.file.fh) {
fprintf(stderr, "[!] Error while opening output file for writing\n");
LOGP(DAPP, LOGL_ERROR, "Error while opening output file for writing\n");
perror("fopen");
return -errno;
}
} else if (gs->opts.rtp_out.port) {
LOGP(DAPP, LOGL_NOTICE, "Using RTP as sink\n");
gs->out.rtp.fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM,
IPPROTO_UDP,
gs->opts.rtp_out.hostname,
gs->opts.rtp_out.port,
OSMO_SOCK_F_CONNECT);
if (gs->out.rtp.fd < 0) {
fprintf(stderr, "[!] Error while opening output socket\n");
LOGP(DAPP, LOGL_ERROR, "Error while opening output socket\n");
return gs->out.rtp.fd;
}
} else if (gs->opts.alsa_out) {
printf("alsa_out, not stdout\n");
} else
/* Do nothing, ALSA sink does the initialization itself */
LOGP(DAPP, LOGL_NOTICE, "Using ALSA as sink\n");
} else {
LOGP(DAPP, LOGL_NOTICE, "Using stdout as sink\n");
gs->out.file.fh = stdout;
}
return 0;
}
@ -362,6 +464,8 @@ files_open(struct gapk_state *gs)
static void
files_close(struct gapk_state *gs)
{
LOGP(DAPP, LOGL_NOTICE, "Closing I/O streams\n");
if (gs->in.file.fh && gs->in.file.fh != stdin)
fclose(gs->in.file.fh);
if (gs->in.rtp.fd >= 0)
@ -383,7 +487,7 @@ handle_headers(struct gapk_state *gs)
if (len && gs->in.file.fh) {
uint8_t *buf;
buf = malloc(len);
buf = talloc_size(app_root_ctx, len);
if (!buf)
return -ENOMEM;
@ -391,12 +495,12 @@ handle_headers(struct gapk_state *gs)
if ((rv != 1) ||
memcmp(buf, gs->opts.fmt_in->header, len))
{
free(buf);
fprintf(stderr, "[!] Invalid header in input file");
LOGP(DAPP, LOGL_ERROR, "Invalid header in input file");
talloc_free(buf);
return -EINVAL;
}
free(buf);
talloc_free(buf);
}
/* Output file header (write it) */
@ -413,16 +517,19 @@ handle_headers(struct gapk_state *gs)
static int
make_processing_chain(struct gapk_state *gs)
{
const struct format_desc *fmt_in, *fmt_out;
const struct codec_desc *codec_in, *codec_out;
const struct osmo_gapk_format_desc *fmt_in, *fmt_out;
const struct osmo_gapk_codec_desc *codec_in, *codec_out;
int need_dec, need_enc;
int rc;
LOGP(DAPP, LOGL_NOTICE, "Creating a processing queue\n");
fmt_in = gs->opts.fmt_in;
fmt_out = gs->opts.fmt_out;
codec_in = codec_get_from_type(fmt_in->codec_type);
codec_out = codec_get_from_type(fmt_out->codec_type);
codec_in = osmo_gapk_codec_get_from_type(fmt_in->codec_type);
codec_out = osmo_gapk_codec_get_from_type(fmt_out->codec_type);
need_dec = (fmt_in->codec_type != CODEC_PCM) &&
(fmt_in->codec_type != fmt_out->codec_type);
@ -431,15 +538,15 @@ make_processing_chain(struct gapk_state *gs)
/* File read */
if (gs->in.file.fh)
pq_queue_file_input(gs->pq, gs->in.file.fh, fmt_in->frame_len);
osmo_gapk_pq_queue_file_input(gs->pq, gs->in.file.fh, fmt_in->frame_len);
else if (gs->in.rtp.fd != -1)
pq_queue_rtp_input(gs->pq, gs->in.rtp.fd, fmt_in->frame_len);
osmo_gapk_pq_queue_rtp_input(gs->pq, gs->in.rtp.fd, fmt_in->frame_len);
#ifdef HAVE_ALSA
else if (gs->opts.alsa_in)
pq_queue_alsa_input(gs->pq, gs->opts.alsa_in, fmt_in->frame_len);
osmo_gapk_pq_queue_alsa_input(gs->pq, gs->opts.alsa_in, fmt_in->frame_len);
#endif
else {
fprintf(stderr, "Unknown/invalid input\n");
LOGP(DAPP, LOGL_ERROR, "Unknown/invalid input\n");
return -1;
}
@ -449,85 +556,111 @@ make_processing_chain(struct gapk_state *gs)
/* Convert input to decoder input fmt */
if (fmt_in->type != codec_in->codec_dec_format_type)
{
const struct format_desc *fmt_dec;
const struct osmo_gapk_format_desc *fmt_dec;
fmt_dec = fmt_get_from_type(codec_in->codec_dec_format_type);
fmt_dec = osmo_gapk_fmt_get_from_type(codec_in->codec_dec_format_type);
if (!fmt_dec) {
fprintf(stderr, "Cannot determine decoder input format for codec %s\n",
codec_in->name);
LOGP(DAPP, LOGL_ERROR, "Cannot determine decoder input format "
"for codec %s\n", codec_in->name);
return -EINVAL;
}
pq_queue_fmt_convert(gs->pq, fmt_in, 0);
pq_queue_fmt_convert(gs->pq, fmt_dec, 1);
osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_in, 0);
osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_dec, 1);
}
/* Do decoding */
pq_queue_codec(gs->pq, codec_in, 0);
osmo_gapk_pq_queue_codec(gs->pq, codec_in, 0);
/* Allocate memory for benchmarking */
if (gs->opts.benchmark)
osmo_gapk_bench_enable(fmt_in->codec_type);
}
else if (fmt_in->type != fmt_out->type)
{
/* Convert input to canonical fmt */
pq_queue_fmt_convert(gs->pq, fmt_in, 0);
osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_in, 0);
}
/* Encoding from PCM ? */
if (need_enc)
{
/* Do encoding */
pq_queue_codec(gs->pq, codec_out, 1);
osmo_gapk_pq_queue_codec(gs->pq, codec_out, 1);
/* Allocate memory for benchmarking */
if (gs->opts.benchmark)
osmo_gapk_bench_enable(fmt_out->codec_type);
/* Convert encoder output to output fmt */
if (fmt_out->type != codec_out->codec_enc_format_type)
{
const struct format_desc *fmt_enc;
const struct osmo_gapk_format_desc *fmt_enc;
fmt_enc = fmt_get_from_type(codec_out->codec_enc_format_type);
fmt_enc = osmo_gapk_fmt_get_from_type(codec_out->codec_enc_format_type);
if (!fmt_enc) {
fprintf(stderr, "Cannot determine encoder output format for codec %s\n",
codec_out->name);
LOGP(DAPP, LOGL_ERROR, "Cannot determine encoder output format "
"for codec %s\n", codec_out->name);
return -EINVAL;
}
pq_queue_fmt_convert(gs->pq, fmt_enc, 0);
pq_queue_fmt_convert(gs->pq, fmt_out, 1);
osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_enc, 0);
osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_out, 1);
}
}
else if (fmt_in->type != fmt_out->type)
{
/* Convert canonical to output fmt */
pq_queue_fmt_convert(gs->pq, fmt_out, 1);
osmo_gapk_pq_queue_fmt_convert(gs->pq, fmt_out, 1);
}
/* File write */
if (gs->out.file.fh)
pq_queue_file_output(gs->pq, gs->out.file.fh, fmt_out->frame_len);
osmo_gapk_pq_queue_file_output(gs->pq, gs->out.file.fh, fmt_out->frame_len);
else if (gs->out.rtp.fd != -1)
pq_queue_rtp_output(gs->pq, gs->out.rtp.fd, fmt_out->frame_len);
osmo_gapk_pq_queue_rtp_output(gs->pq, gs->out.rtp.fd, fmt_out->frame_len);
#ifdef HAVE_ALSA
else if (gs->opts.alsa_out)
pq_queue_alsa_output(gs->pq, gs->opts.alsa_out, fmt_out->frame_len);
osmo_gapk_pq_queue_alsa_output(gs->pq, gs->opts.alsa_out, fmt_out->frame_len);
#endif
else {
fprintf(stderr, "Unknown/invalid output\n");
LOGP(DAPP, LOGL_ERROR, "Unknown/invalid output\n");
return -1;
}
/* Check the processing queue in strict mode */
rc = osmo_gapk_pq_check(gs->pq, 1);
if (rc)
return rc;
return 0;
}
static int
run(struct gapk_state *gs)
{
struct osmo_gapk_pq_item *item;
int rv, frames;
rv = pq_prepare(gs->pq);
rv = osmo_gapk_pq_prepare(gs->pq);
if (rv)
return rv;
for (frames=0; !(rv = pq_execute(gs->pq)); frames++);
for (frames = 0; !gs->exit; frames++) {
rv = osmo_gapk_pq_execute(gs->pq);
if (rv)
break;
}
fprintf(stderr, "[+] Processed %d frames\n", frames);
LOGP(DAPP, LOGL_NOTICE, "Processed %d frames\n", frames);
/* Wait for sink to process buffers */
item = llist_last_entry(&gs->pq->items, struct osmo_gapk_pq_item, list);
if (item->wait && !gs->exit) {
LOGP(DAPP, LOGL_NOTICE, "Waiting for sink to finish...\n");
while (item->wait(item->state))
continue;
}
return frames > 0 ? 0 : rv;
}
@ -535,15 +668,39 @@ run(struct gapk_state *gs)
static struct gapk_state _gs, *gs = &_gs;
static void app_shutdown(void)
{
/* Close source / destination files */
files_close(gs);
/* Release processing queue */
osmo_gapk_pq_destroy(gs->pq);
/* Print benchmarking results, if enabled */
benchmark_dump();
/* Free memory taken by benchmark data */
osmo_gapk_bench_free();
if (gs->opts.verbose)
talloc_report_full(app_root_ctx, stderr);
}
static void signal_handler(int signal)
{
fprintf(stderr, "signal %u received\n", signal);
switch (signal) {
case SIGINT:
fprintf(stderr, "catching sigint, closing files\n");
files_close(gs);
pq_destroy(gs->pq);
exit(0);
if (gs->exit++) {
app_shutdown();
exit(0);
}
break;
case SIGABRT:
case SIGUSR1:
case SIGUSR2:
talloc_report_full(app_root_ctx, stderr);
default:
break;
}
@ -554,6 +711,15 @@ int main(int argc, char *argv[])
{
int rv;
/* Init talloc memory management system */
app_root_ctx = talloc_init("osmo-gapk root context");
osmo_gapk_set_talloc_ctx(app_root_ctx);
/* Init Osmocom logging framework */
osmo_init_logging(&gapk_log_info);
/* and GAPK logging wrapper */
osmo_gapk_log_init(DAPP);
/* Clear state */
memset(gs, 0x00, sizeof(struct gapk_state));
gs->in.rtp.fd = -1;
@ -574,47 +740,44 @@ int main(int argc, char *argv[])
return rv;
/* Create processing queue */
gs->pq = pq_create();
gs->pq = osmo_gapk_pq_create("main");
if (!gs->pq) {
rv = -ENOMEM;
fprintf(stderr, "Error creating processing queue\n");
LOGP(DAPP, LOGL_ERROR, "Error creating processing queue\n");
goto error;
}
/* Open source / destination files */
rv = files_open(gs);
if (rv) {
fprintf(stderr, "Error opening file(s)\n");
LOGP(DAPP, LOGL_ERROR, "Error opening file(s)\n");
goto error;
}
/* Handle input/output headers */
rv = handle_headers(gs);
if (rv) {
fprintf(stderr, "Error handling header(s)\n");
LOGP(DAPP, LOGL_ERROR, "Error handling header(s)\n");
goto error;
}
/* Make processing chain */
rv = make_processing_chain(gs);
if (rv) {
fprintf(stderr, "Error making processing chain\n");
LOGP(DAPP, LOGL_ERROR, "Error making processing chain\n");
goto error;
}
signal(SIGINT, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
/* Run the processing queue */
LOGP(DAPP, LOGL_NOTICE, "Init complete, starting processing queue...\n");
rv = run(gs);
error:
/* Close source / destination files */
files_close(gs);
/* Release processing queue */
pq_destroy(gs->pq);
benchmark_dump();
app_shutdown();
return rv;
}

View File

@ -17,41 +17,73 @@
* (C) 2014 Harald Welte <laforge@gnumonks.org>
*/
#include <stdio.h>
#include <talloc.h>
#include <errno.h>
#include <gapk/benchmark.h>
#include <osmocom/gapk/benchmark.h>
#include <osmocom/gapk/codecs.h>
struct benchmark_cycles codec_cycles[_CODEC_MAX];
/* Internal root talloc context */
extern TALLOC_CTX *gapk_root_ctx;
void benchmark_dump(void)
struct osmo_gapk_bench_cycles *
osmo_gapk_bench_codec[_CODEC_MAX] = { NULL };
int osmo_gapk_bench_enable(enum osmo_gapk_codec_type codec)
{
struct osmo_gapk_bench_cycles *bench;
/* Allocate zero-initialized memory */
bench = talloc_zero(gapk_root_ctx, struct osmo_gapk_bench_cycles);
if (!bench)
return -ENOMEM;
/* Set up pointer */
osmo_gapk_bench_codec[codec] = bench;
return 0;
}
unsigned long long
osmo_gapk_bench_get_cycles(enum osmo_gapk_codec_type codec, int enc)
{
struct osmo_gapk_bench_cycles *bench;
unsigned long long cycles = 0;
int i;
/* Check if there are benchmark data */
bench = osmo_gapk_bench_codec[codec];
if (!bench)
return -EAGAIN;
if (enc) {
for (i = 0; i < bench->enc_used; i++)
cycles += bench->enc[i];
} else {
for (i = 0; i < bench->dec_used; i++)
cycles += bench->dec[i];
}
return cycles;
}
unsigned int
osmo_gapk_bench_get_frames(enum osmo_gapk_codec_type codec, int enc)
{
struct osmo_gapk_bench_cycles *bench;
/* Check if there are benchmark data */
bench = osmo_gapk_bench_codec[codec];
if (!bench)
return -EAGAIN;
return enc ? bench->enc_used : bench->dec_used;
}
void osmo_gapk_bench_free(void)
{
int i;
for (i = 0; i < _CODEC_MAX; i++) {
struct benchmark_cycles *bc = &codec_cycles[i];
unsigned long long total;
int j;
if (bc->enc_used) {
total = 0;
for (j = 0; j < bc->enc_used; j++)
total += bc->enc[j];
fprintf(stderr,
"Codec %u (ENC): %llu cycles for %u frames => "
"%llu cycles/frame\n", i, total, bc->enc_used,
total / bc->enc_used);
}
if (bc->dec_used) {
total = 0;
for (j = 0; j < bc->dec_used; j++)
total += bc->dec[j];
fprintf(stderr,
"Codec %u (DEC): %llu cycles for %u frames => "
"%llu cycles/frame\n", i, total, bc->dec_used,
total / bc->dec_used);
}
}
for (i = 0; i < _CODEC_MAX; i++)
talloc_free(osmo_gapk_bench_codec[i]);
}

View File

@ -18,20 +18,23 @@
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gapk/codecs.h>
#include <gapk/benchmark.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/benchmark.h>
#include <osmocom/gapk/bench.h>
#include "config.h"
#ifdef HAVE_OPENCORE_AMRNB
#include <stdlib.h>
#include <stdio.h>
#include <talloc.h>
#include <opencore-amrnb/interf_dec.h>
#include <opencore-amrnb/interf_enc.h>
/* Internal root talloc context */
extern TALLOC_CTX *gapk_root_ctx;
struct codec_amr_state {
void *encoder;
void *decoder;
@ -43,7 +46,7 @@ codec_amr_init(void)
{
struct codec_amr_state *st;
st = calloc(1, sizeof(*st));
st = talloc_zero(gapk_root_ctx, struct codec_amr_state);
if (!st)
return NULL;
@ -61,6 +64,8 @@ codec_amr_exit(void *state)
Decoder_Interface_exit(st->decoder);
Encoder_Interface_exit(st->encoder);
talloc_free(st);
return;
}
@ -103,7 +108,7 @@ codec_amr_decode(void *state, uint8_t *pcm, const uint8_t *cod, unsigned int cod
#endif /* HAVE_OPENCORE_AMRNB */
const struct codec_desc codec_amr_desc = {
const struct osmo_gapk_codec_desc codec_amr_desc = {
.type = CODEC_AMR,
.name = "amr",
.description = "GSM 26.071 Adaptive Multi Rate codec",

View File

@ -17,20 +17,23 @@
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gapk/codecs.h>
#include <gapk/benchmark.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/benchmark.h>
#include <osmocom/gapk/bench.h>
#include "config.h"
#ifdef HAVE_OPENCORE_AMRNB
#include <stdlib.h>
#include <talloc.h>
#include <assert.h>
#include <opencore-amrnb/interf_dec.h>
#include <opencore-amrnb/interf_enc.h>
/* Internal root talloc context */
extern TALLOC_CTX *gapk_root_ctx;
struct codec_efr_state {
void *encoder;
@ -43,7 +46,7 @@ codec_efr_init(void)
{
struct codec_efr_state *st;
st = calloc(1, sizeof(*st));
st = talloc_zero(gapk_root_ctx, struct codec_efr_state);
if (!st)
return NULL;
@ -61,6 +64,8 @@ codec_efr_exit(void *state)
Decoder_Interface_exit(st->decoder);
Encoder_Interface_exit(st->encoder);
talloc_free(st);
return;
}
@ -108,7 +113,7 @@ codec_efr_decode(void *state, uint8_t *pcm, const uint8_t *cod, unsigned int cod
#endif /* HAVE_OPENCORE_AMRNB */
const struct codec_desc codec_efr_desc = {
const struct osmo_gapk_codec_desc codec_efr_desc = {
.type = CODEC_EFR,
.name = "efr",
.description = "GSM 06.60 Enhanced Full Rate codec",

View File

@ -19,8 +19,9 @@
#include <assert.h>
#include <gapk/codecs.h>
#include <gapk/benchmark.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/benchmark.h>
#include <osmocom/gapk/bench.h>
#include "config.h"
@ -83,7 +84,7 @@ codec_fr_decode(void *state, uint8_t *pcm, const uint8_t *cod, unsigned int cod_
#endif /* HAVE_LIBGSM */
const struct codec_desc codec_fr_desc = {
const struct osmo_gapk_codec_desc codec_fr_desc = {
.type = CODEC_FR,
.name = "fr",
.description = "GSM 06.10 Full Rate codec (classic gsm codec)",

View File

@ -19,8 +19,9 @@
#include <assert.h>
#include <gapk/codecs.h>
#include <gapk/benchmark.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/benchmark.h>
#include <osmocom/gapk/bench.h>
#include "config.h"
@ -73,7 +74,7 @@ codec_hr_decode(void *_state, uint8_t *pcm, const uint8_t *cod, unsigned int cod
#endif /* HAVE_LIBGSMHR */
const struct codec_desc codec_hr_desc = {
const struct osmo_gapk_codec_desc codec_hr_desc = {
.type = CODEC_HR,
.name = "hr",
.description = "GSM 06.20 Half Rate codec",

View File

@ -17,9 +17,9 @@
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gapk/codecs.h>
#include <osmocom/gapk/codecs.h>
const struct codec_desc codec_pcm_desc = {
const struct osmo_gapk_codec_desc codec_pcm_desc = {
.type = CODEC_PCM,
.name = "pcm",
.description = "Raw PCM signed 16 bits samples",

View File

@ -19,18 +19,18 @@
#include <stdio.h> /* for NULL */
#include <gapk/codecs.h>
#include <osmocom/gapk/codecs.h>
/* Extern codec descriptors */
extern const struct codec_desc codec_pcm_desc;
extern const struct codec_desc codec_hr_desc;
extern const struct codec_desc codec_fr_desc;
extern const struct codec_desc codec_efr_desc;
extern const struct codec_desc codec_amr_desc;
extern const struct osmo_gapk_codec_desc codec_pcm_desc;
extern const struct osmo_gapk_codec_desc codec_hr_desc;
extern const struct osmo_gapk_codec_desc codec_fr_desc;
extern const struct osmo_gapk_codec_desc codec_efr_desc;
extern const struct osmo_gapk_codec_desc codec_amr_desc;
const struct codec_desc *
codec_get_from_type(enum codec_type type)
const struct osmo_gapk_codec_desc *
osmo_gapk_codec_get_from_type(enum osmo_gapk_codec_type type)
{
switch (type) {
case CODEC_PCM: return &codec_pcm_desc;

38
src/common.c Normal file
View File

@ -0,0 +1,38 @@
/*
* This file is part of gapk (GSM Audio Pocket Knife).
*
* (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
*
* gapk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* gapk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gapk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <talloc.h>
/* Internal root talloc context */
TALLOC_CTX *gapk_root_ctx = NULL;
void osmo_gapk_set_talloc_ctx(void *ctx)
{
gapk_root_ctx = ctx;
}
/* Internal GAPK logging */
int gapk_log_init_complete = 0;
int gapk_log_subsys;
void osmo_gapk_log_init(int subsys)
{
gapk_log_subsys = subsys;
gapk_log_init_complete = 1;
}

View File

@ -22,9 +22,9 @@
#include <osmocom/codec/codec.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/utils.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/utils.h>
#define EFR_LEN 32
@ -70,7 +70,7 @@ amr_efr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return EFR_CANON_LEN;
}
const struct format_desc fmt_amr_efr = {
const struct osmo_gapk_format_desc fmt_amr_efr = {
.type = FMT_AMR_EFR,
.codec_type = CODEC_EFR,
.name = "amr-efr",

View File

@ -21,9 +21,9 @@
#include <stdint.h>
#include <string.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/utils.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/utils.h>
static int
amr_opencore_from_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
@ -39,7 +39,7 @@ amr_opencore_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return src_len;
}
const struct format_desc fmt_amr_opencore = {
const struct osmo_gapk_format_desc fmt_amr_opencore = {
.type = FMT_AMR_OPENCORE,
.codec_type = CODEC_AMR,
.name = "amr-opencore",

View File

@ -18,8 +18,8 @@
*/
#include <assert.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#define GSM_LEN 33
#define GSM_MAGIC 0xd
@ -54,7 +54,7 @@ gsm_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return FR_CANON_LEN;
}
const struct format_desc fmt_gsm = {
const struct osmo_gapk_format_desc fmt_gsm = {
.type = FMT_GSM,
.codec_type = CODEC_FR,
.name = "gsm",

View File

@ -22,9 +22,9 @@
#include <assert.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/utils.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/utils.h>
static const int params_unvoiced[] = {
5, /* R0 */
@ -191,7 +191,7 @@ hr_ref_enc_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
}
const struct format_desc fmt_hr_ref_dec = {
const struct osmo_gapk_format_desc fmt_hr_ref_dec = {
.type = FMT_HR_REF_DEC,
.codec_type = CODEC_HR,
.name = "hr-ref-dec",
@ -202,7 +202,7 @@ const struct format_desc fmt_hr_ref_dec = {
.conv_to_canon = hr_ref_dec_to_canon,
};
const struct format_desc fmt_hr_ref_enc = {
const struct osmo_gapk_format_desc fmt_hr_ref_enc = {
.type = FMT_HR_REF_ENC,
.codec_type = CODEC_HR,
.name = "hr-ref-enc",

View File

@ -22,9 +22,9 @@
#include <osmocom/codec/codec.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/utils.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/utils.h>
#define RACAL_HR_LEN 14
#define RACAL_FR_LEN 33
@ -76,7 +76,7 @@ racal_hr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return RACAL_HR_LEN;
}
const struct format_desc fmt_racal_hr = {
const struct osmo_gapk_format_desc fmt_racal_hr = {
.type = FMT_RACAL_HR,
.codec_type = CODEC_HR,
.name = "racal-hr",
@ -124,7 +124,7 @@ racal_fr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return FR_CANON_LEN;
}
const struct format_desc fmt_racal_fr = {
const struct osmo_gapk_format_desc fmt_racal_fr = {
.type = FMT_RACAL_FR,
.codec_type = CODEC_FR,
.name = "racal-fr",
@ -166,7 +166,7 @@ racal_efr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return EFR_CANON_LEN;
}
const struct format_desc fmt_racal_efr = {
const struct osmo_gapk_format_desc fmt_racal_efr = {
.type = FMT_RACAL_EFR,
.codec_type = CODEC_EFR,
.name = "racal-efr",

View File

@ -19,8 +19,8 @@
#include <assert.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
static int
rawpcm_s16le_from_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
@ -53,7 +53,7 @@ rawpcm_s16le_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return PCM_CANON_LEN;
}
const struct format_desc fmt_rawpcm_s16le = {
const struct osmo_gapk_format_desc fmt_rawpcm_s16le = {
.type = FMT_RAWPCM_S16LE,
.codec_type = CODEC_PCM,
.name = "rawpcm-s16le",

View File

@ -23,9 +23,9 @@
#include <osmocom/codec/codec.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/utils.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/utils.h>
/* conversion function: RTP payload -> canonical format */
static int
@ -48,7 +48,7 @@ rtp_amr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return src_len-1;
}
const struct format_desc fmt_rtp_amr = {
const struct osmo_gapk_format_desc fmt_rtp_amr = {
.type = FMT_RTP_AMR,
.codec_type = CODEC_AMR,
.name = "rtp-amr",

View File

@ -23,9 +23,9 @@
#include <osmocom/codec/codec.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/utils.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/utils.h>
#define EFR_LEN 31
#define EFR_MAGIC 0xc
@ -61,7 +61,7 @@ rtp_efr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return EFR_CANON_LEN;
}
const struct format_desc fmt_rtp_efr = {
const struct osmo_gapk_format_desc fmt_rtp_efr = {
.type = FMT_RTP_EFR,
.codec_type = CODEC_EFR,
.name = "rtp-efr",

View File

@ -22,9 +22,9 @@
#include <assert.h>
#include <string.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/utils.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/utils.h>
/* conversion function: RTP payload -> canonical format */
static int
@ -48,7 +48,7 @@ rtp_hr_etsi_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return HR_CANON_LEN;
}
const struct format_desc fmt_rtp_hr_etsi = {
const struct osmo_gapk_format_desc fmt_rtp_hr_etsi = {
.type = FMT_RTP_HR_ETSI,
.codec_type = CODEC_HR,
.name = "rtp-hr-etsi",

View File

@ -22,9 +22,9 @@
#include <assert.h>
#include <string.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/utils.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/utils.h>
#define HR_LEN (HR_CANON_LEN+1)
@ -68,7 +68,7 @@ rtp_hr_ietf_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return HR_CANON_LEN;
}
const struct format_desc fmt_rtp_hr_ietf = {
const struct osmo_gapk_format_desc fmt_rtp_hr_ietf = {
.type = FMT_RTP_HR_IETF,
.codec_type = CODEC_HR,
.name = "rtp-hr-ietf",

View File

@ -30,9 +30,9 @@
#include <osmocom/codec/codec.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/utils.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/utils.h>
#define TI_LEN 33
@ -84,7 +84,7 @@ ti_hr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return HR_CANON_LEN;
}
const struct format_desc fmt_ti_hr = {
const struct osmo_gapk_format_desc fmt_ti_hr = {
.type = FMT_TI_HR,
.codec_type = CODEC_HR,
.name = "ti-hr",
@ -132,7 +132,7 @@ ti_fr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return FR_CANON_LEN;
}
const struct format_desc fmt_ti_fr = {
const struct osmo_gapk_format_desc fmt_ti_fr = {
.type = FMT_TI_FR,
.codec_type = CODEC_FR,
.name = "ti-fr",
@ -224,7 +224,7 @@ ti_efr_to_canon(uint8_t *dst, const uint8_t *src, unsigned int src_len)
return EFR_CANON_LEN;
}
const struct format_desc fmt_ti_efr = {
const struct osmo_gapk_format_desc fmt_ti_efr = {
.type = FMT_TI_EFR,
.codec_type = CODEC_EFR,
.name = "ti-efr",

View File

@ -20,27 +20,27 @@
#include <stdio.h> /* for NULL */
#include <string.h>
#include <gapk/formats.h>
#include <osmocom/gapk/formats.h>
/* Extern format descriptors */
extern const struct format_desc fmt_amr_efr;
extern const struct format_desc fmt_gsm;
extern const struct format_desc fmt_hr_ref_dec;
extern const struct format_desc fmt_hr_ref_enc;
extern const struct format_desc fmt_racal_hr;
extern const struct format_desc fmt_racal_fr;
extern const struct format_desc fmt_racal_efr;
extern const struct format_desc fmt_rawpcm_s16le;
extern const struct format_desc fmt_ti_hr;
extern const struct format_desc fmt_ti_fr;
extern const struct format_desc fmt_ti_efr;
extern const struct format_desc fmt_amr_opencore;
extern const struct format_desc fmt_rtp_amr;
extern const struct format_desc fmt_rtp_efr;
extern const struct format_desc fmt_rtp_hr_etsi;
extern const struct format_desc fmt_rtp_hr_ietf;
extern const struct osmo_gapk_format_desc fmt_amr_efr;
extern const struct osmo_gapk_format_desc fmt_gsm;
extern const struct osmo_gapk_format_desc fmt_hr_ref_dec;
extern const struct osmo_gapk_format_desc fmt_hr_ref_enc;
extern const struct osmo_gapk_format_desc fmt_racal_hr;
extern const struct osmo_gapk_format_desc fmt_racal_fr;
extern const struct osmo_gapk_format_desc fmt_racal_efr;
extern const struct osmo_gapk_format_desc fmt_rawpcm_s16le;
extern const struct osmo_gapk_format_desc fmt_ti_hr;
extern const struct osmo_gapk_format_desc fmt_ti_fr;
extern const struct osmo_gapk_format_desc fmt_ti_efr;
extern const struct osmo_gapk_format_desc fmt_amr_opencore;
extern const struct osmo_gapk_format_desc fmt_rtp_amr;
extern const struct osmo_gapk_format_desc fmt_rtp_efr;
extern const struct osmo_gapk_format_desc fmt_rtp_hr_etsi;
extern const struct osmo_gapk_format_desc fmt_rtp_hr_ietf;
static const struct format_desc *supported_formats[_FMT_MAX] = {
static const struct osmo_gapk_format_desc *supported_formats[_FMT_MAX] = {
[FMT_INVALID] = NULL,
[FMT_AMR_EFR] = &fmt_amr_efr,
[FMT_GSM] = &fmt_gsm,
@ -61,20 +61,20 @@ static const struct format_desc *supported_formats[_FMT_MAX] = {
};
const struct format_desc *
fmt_get_from_type(enum format_type type)
const struct osmo_gapk_format_desc *
osmo_gapk_fmt_get_from_type(enum osmo_gapk_format_type type)
{
if (type <= FMT_INVALID || type >= _FMT_MAX)
return NULL;
return supported_formats[type];
}
const struct format_desc *
fmt_get_from_name(const char *name)
const struct osmo_gapk_format_desc *
osmo_gapk_fmt_get_from_name(const char *name)
{
int i;
for (i=FMT_INVALID+1; i<_FMT_MAX; i++) {
const struct format_desc *fmt = supported_formats[i];
const struct osmo_gapk_format_desc *fmt = supported_formats[i];
if (!fmt)
continue;
if (!strcmp(fmt->name, name))

4
src/libosmogapk.map Normal file
View File

@ -0,0 +1,4 @@
LIBOSMOGAPK_1.0 {
global: osmo_gapk_*;
local: *;
};

View File

@ -20,12 +20,12 @@
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <talloc.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/procqueue.h>
#include <osmocom/gapk/logging.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/procqueue.h>
#include "config.h"
@ -69,22 +69,30 @@ pq_cb_alsa_output(void *_state, uint8_t *out, const uint8_t *in, unsigned int in
return rv == num_samples ? 0 : -1;
}
static int
pq_cb_alsa_wait(void *_state)
{
struct pq_state_alsa *state = _state;
return snd_pcm_avail_update(state->pcm_handle) > 0;
}
static void
pq_cb_alsa_exit(void *_state)
{
struct pq_state_alsa *state = _state;
snd_pcm_close(state->pcm_handle);
talloc_free(state);
}
static int
pq_queue_alsa_op(struct pq *pq, const char *alsa_dev, unsigned int blk_len, int in_out_n)
pq_queue_alsa_op(struct osmo_gapk_pq *pq, const char *alsa_dev, unsigned int blk_len, int in_out_n)
{
struct pq_item *item;
struct osmo_gapk_pq_item *item;
struct pq_state_alsa *state;
snd_pcm_hw_params_t *hw_params;
int rc = -1;
state = calloc(1, sizeof(struct pq_state_alsa));
state = talloc_zero(pq, struct pq_state_alsa);
if (!state) {
rc = -ENOMEM;
goto out_print;
@ -127,27 +135,37 @@ pq_queue_alsa_op(struct pq *pq, const char *alsa_dev, unsigned int blk_len, int
snd_pcm_hw_params_free(hw_params);
item = pq_add_item(pq);
item = osmo_gapk_pq_add_item(pq);
if (!item) {
rc = -ENOMEM;
goto out_close;
}
item->type = in_out_n ?
OSMO_GAPK_ITEM_TYPE_SOURCE : OSMO_GAPK_ITEM_TYPE_SINK;
item->cat_name = in_out_n ?
OSMO_GAPK_CAT_NAME_SOURCE : OSMO_GAPK_CAT_NAME_SINK;
item->sub_name = "alsa";
item->len_in = in_out_n ? 0 : blk_len;
item->len_out = in_out_n ? blk_len : 0;
item->state = state;
item->proc = in_out_n ? pq_cb_alsa_input : pq_cb_alsa_output;
item->wait = pq_cb_alsa_wait;
item->exit = pq_cb_alsa_exit;
/* Change state's talloc context from pq to item */
talloc_steal(item, state);
return 0;
out_free_par:
snd_pcm_hw_params_free(hw_params);
out_close:
snd_pcm_close(state->pcm_handle);
free(state);
talloc_free(state);
out_print:
fprintf(stderr, "[!] Couldn't init ALSA device '%s': %s\n",
LOGPGAPK(LOGL_ERROR, "Couldn't init ALSA device '%s': %s\n",
alsa_dev, snd_strerror(rc));
return rc;
}
@ -160,9 +178,10 @@ out_print:
* \param[in] blk_len block length to be read from device
* \returns 0 on sucess; negative on error */
int
pq_queue_alsa_input(struct pq *pq, const char *hwdev, unsigned int blk_len)
osmo_gapk_pq_queue_alsa_input(struct osmo_gapk_pq *pq, const char *hwdev, unsigned int blk_len)
{
fprintf(stderr, "[+] PQ: Adding ALSA input (dev='%s', blk_len=%u)\n", hwdev, blk_len);
LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding ALSA input "
"(dev='%s', blk_len=%u)\n", pq->name, hwdev, blk_len);
return pq_queue_alsa_op(pq, hwdev, blk_len, 1);
}
@ -173,9 +192,10 @@ pq_queue_alsa_input(struct pq *pq, const char *hwdev, unsigned int blk_len)
* \param[in] blk_len block length to be written to device
* \returns 0 on sucess; negative on error */
int
pq_queue_alsa_output(struct pq *pq, const char *hwdev, unsigned int blk_len)
osmo_gapk_pq_queue_alsa_output(struct osmo_gapk_pq *pq, const char *hwdev, unsigned int blk_len)
{
fprintf(stderr, "[+] PQ: Adding ALSA output (dev='%s', blk_len=%u)\n", hwdev, blk_len);
LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding ALSA output "
"(dev='%s', blk_len=%u)\n", pq->name, hwdev, blk_len);
return pq_queue_alsa_op(pq, hwdev, blk_len, 0);
}

View File

@ -20,9 +20,10 @@
#include <errno.h>
#include <stdint.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/procqueue.h>
#include <osmocom/gapk/logging.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/procqueue.h>
/*! Add a codecl to the processing queue
@ -31,14 +32,14 @@
* \param[in] encode (1) or decode (0)
* \returns 0 on success; negative on error */
int
pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int enc_dec_n)
osmo_gapk_pq_queue_codec(struct osmo_gapk_pq *pq, const struct osmo_gapk_codec_desc *codec, int enc_dec_n)
{
const struct codec_desc *codec_pcm = codec_get_from_type(CODEC_PCM);
const struct format_desc *fmt;
struct pq_item *item;
const struct osmo_gapk_codec_desc *codec_pcm;
const struct osmo_gapk_format_desc *fmt;
struct osmo_gapk_pq_item *item;
/* allocate a new item to the processing queue */
item = pq_add_item(pq);
item = osmo_gapk_pq_add_item(pq);
if (!item)
return -ENOMEM;
@ -50,7 +51,8 @@ pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int enc_dec_n)
}
if (enc_dec_n) {
fmt = fmt_get_from_type(codec->codec_enc_format_type);
codec_pcm = osmo_gapk_codec_get_from_type(CODEC_PCM);
fmt = osmo_gapk_fmt_get_from_type(codec->codec_enc_format_type);
if (!fmt)
return -EINVAL;
@ -58,7 +60,8 @@ pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int enc_dec_n)
item->len_out = fmt->frame_len;
item->proc = codec->codec_encode;
} else {
fmt = fmt_get_from_type(codec->codec_dec_format_type);
codec_pcm = osmo_gapk_codec_get_from_type(CODEC_PCM);
fmt = osmo_gapk_fmt_get_from_type(codec->codec_dec_format_type);
if (!fmt)
return -EINVAL;
@ -67,10 +70,17 @@ pq_queue_codec(struct pq *pq, const struct codec_desc *codec, int enc_dec_n)
item->proc = codec->codec_decode;
}
item->type = OSMO_GAPK_ITEM_TYPE_PROC;
item->exit = codec->codec_exit;
item->wait = NULL;
fprintf(stderr, "[+] PQ: Adding Codec %s, %s format %s\n", codec->name,
enc_dec_n ? "encoding to" : "decoding from", fmt->name);
/* Meta information */
item->cat_name = "codec";
item->sub_name = codec->name;
LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding codec %s, %s format %s\n",
pq->name, codec->name, enc_dec_n ?
"encoding to" : "decoding from", fmt->name);
if (!item->proc)
return -ENOTSUP;

View File

@ -19,12 +19,12 @@
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <talloc.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/procqueue.h>
#include <osmocom/gapk/logging.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/procqueue.h>
struct pq_state_file {
@ -56,34 +56,44 @@ pq_cb_file_output(void *_state, uint8_t *out, const uint8_t *in, unsigned int in
static void
pq_cb_file_exit(void *_state)
{
free(_state);
talloc_free(_state);
}
static int
pq_queue_file_op(struct pq *pq, FILE *fh, unsigned int blk_len, int in_out_n)
pq_queue_file_op(struct osmo_gapk_pq *pq, FILE *fh, unsigned int blk_len, int in_out_n)
{
struct pq_item *item;
struct osmo_gapk_pq_item *item;
struct pq_state_file *state;
state = calloc(1, sizeof(struct pq_state_file));
state = talloc_zero(pq, struct pq_state_file);
if (!state)
return -ENOMEM;
state->fh = fh;
state->blk_len = blk_len;
item = pq_add_item(pq);
item = osmo_gapk_pq_add_item(pq);
if (!item) {
free(state);
talloc_free(state);
return -ENOMEM;
}
item->type = in_out_n ?
OSMO_GAPK_ITEM_TYPE_SOURCE : OSMO_GAPK_ITEM_TYPE_SINK;
item->cat_name = in_out_n ?
OSMO_GAPK_CAT_NAME_SOURCE : OSMO_GAPK_CAT_NAME_SINK;
item->sub_name = "file";
item->len_in = in_out_n ? 0 : blk_len;
item->len_out = in_out_n ? blk_len : 0;
item->state = state;
item->proc = in_out_n ? pq_cb_file_input : pq_cb_file_output;
item->wait = NULL;
item->exit = pq_cb_file_exit;
/* Change state's talloc context from pq to item */
talloc_steal(item, state);
return 0;
}
@ -95,9 +105,10 @@ pq_queue_file_op(struct pq *pq, FILE *fh, unsigned int blk_len, int in_out_n)
* \param[in] blk_len block length to be read from file
* \returns 0 on sucess; negative on error */
int
pq_queue_file_input(struct pq *pq, FILE *src, unsigned int blk_len)
osmo_gapk_pq_queue_file_input(struct osmo_gapk_pq *pq, FILE *src, unsigned int blk_len)
{
fprintf(stderr, "[+] PQ: Adding file input (blk_len=%u)\n", blk_len);
LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding file input (blk_len=%u)\n",
pq->name, blk_len);
return pq_queue_file_op(pq, src, blk_len, 1);
}
@ -108,8 +119,9 @@ pq_queue_file_input(struct pq *pq, FILE *src, unsigned int blk_len)
* \param[in] blk_len block length to be written to file
* \returns 0 on sucess; negative on error */
int
pq_queue_file_output(struct pq *pq, FILE *dst, unsigned int blk_len)
osmo_gapk_pq_queue_file_output(struct osmo_gapk_pq *pq, FILE *dst, unsigned int blk_len)
{
fprintf(stderr, "[+] PQ: Adding file output (blk_len=%u)\n", blk_len);
LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding file output (blk_len=%u)\n",
pq->name, blk_len);
return pq_queue_file_op(pq, dst, blk_len, 0);
}

View File

@ -20,15 +20,16 @@
#include <errno.h>
#include <stdint.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/procqueue.h>
#include <osmocom/gapk/logging.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/procqueue.h>
static int
pq_cb_fmt_convert(void *_state, uint8_t *out, const uint8_t *in, unsigned int in_len)
{
fmt_conv_cb_t f = _state;
osmo_gapk_fmt_conv_cb_t f = _state;
return f(out, in, in_len);
}
@ -37,36 +38,44 @@ pq_cb_fmt_convert(void *_state, uint8_t *out, const uint8_t *in, unsigned int in
* \param[in] fmt Format description for conversion
* \param[in] to_from_n convert to (0) or from (1) specified format */
int
pq_queue_fmt_convert(struct pq *pq, const struct format_desc *fmt, int to_from_n)
osmo_gapk_pq_queue_fmt_convert(struct osmo_gapk_pq *pq, const struct osmo_gapk_format_desc *fmt, int to_from_n)
{
struct pq_item *item;
const struct codec_desc *codec = codec_get_from_type(fmt->codec_type);
const struct osmo_gapk_codec_desc *codec;
struct osmo_gapk_pq_item *item;
codec = osmo_gapk_codec_get_from_type(fmt->codec_type);
if (!codec) {
fprintf(stderr, "[!] Cannot determine codec from format %s\n", fmt->name);
LOGPGAPK(LOGL_ERROR, "Cannot determine codec from "
"format %s\n", fmt->name);
return -EINVAL;
}
item = pq_add_item(pq);
item = osmo_gapk_pq_add_item(pq);
if (!item)
return -ENOMEM;
if (to_from_n) {
fprintf(stderr, "[+] PQ: Adding conversion from canon to %s (for codec %s)\n",
fmt->name, codec->name);
LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding conversion from canon "
"to %s (for codec %s)\n", pq->name, fmt->name, codec->name);
item->len_in = codec->canon_frame_len;
item->len_out = fmt->frame_len;
item->state = fmt->conv_from_canon;
} else {
fprintf(stderr, "[+] PQ: Adding conversion from %s to canon (for codec %s)\n",
fmt->name, codec->name);
LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding conversion from %s "
"to canon (for codec %s)\n", pq->name, fmt->name, codec->name);
item->len_in = fmt->frame_len;
item->len_out = codec->canon_frame_len;
item->state = fmt->conv_to_canon;
}
item->type = OSMO_GAPK_ITEM_TYPE_PROC;
item->proc = pq_cb_fmt_convert;
item->wait = NULL;
/* Meta information */
item->cat_name = "format";
item->sub_name = fmt->name;
return 0;
}

View File

@ -20,16 +20,16 @@
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <talloc.h>
#include <arpa/inet.h>
#include <gapk/codecs.h>
#include <gapk/formats.h>
#include <gapk/procqueue.h>
#include <osmocom/gapk/logging.h>
#include <osmocom/gapk/codecs.h>
#include <osmocom/gapk/formats.h>
#include <osmocom/gapk/procqueue.h>
#ifndef __BYTE_ORDER
# ifdef __APPLE__
@ -86,7 +86,8 @@ struct pq_state_rtp {
uint32_t ssrc;
};
#define rtp_err(x, args...) fprintf(stderr, "[!] %s():" x, __func__, ## args)
#define rtp_err(err_msg, args...) \
LOGPGAPK(LOGL_ERROR, "%s():" err_msg, __func__, ## args)
static int
pq_cb_rtp_input(void *_state, uint8_t *out, const uint8_t *in, unsigned int in_len)
@ -188,16 +189,16 @@ pq_cb_rtp_output(void *_state, uint8_t *out, const uint8_t *in, unsigned int in_
static void
pq_cb_rtp_exit(void *_state)
{
free(_state);
talloc_free(_state);
}
static int
pq_queue_rtp_op(struct pq *pq, int udp_fd, unsigned int blk_len, int in_out_n)
pq_queue_rtp_op(struct osmo_gapk_pq *pq, int udp_fd, unsigned int blk_len, int in_out_n)
{
struct pq_item *item;
struct osmo_gapk_pq_item *item;
struct pq_state_rtp *state;
state = calloc(1, sizeof(struct pq_state_rtp));
state = talloc_zero(pq, struct pq_state_rtp);
if (!state)
return -ENOMEM;
@ -217,18 +218,28 @@ pq_queue_rtp_op(struct pq *pq, int udp_fd, unsigned int blk_len, int in_out_n)
state->payload_type = RTP_PT_GSM_FULL;
}
item = pq_add_item(pq);
item = osmo_gapk_pq_add_item(pq);
if (!item) {
free(state);
talloc_free(state);
return -ENOMEM;
}
item->type = in_out_n ?
OSMO_GAPK_ITEM_TYPE_SOURCE : OSMO_GAPK_ITEM_TYPE_SINK;
item->cat_name = in_out_n ?
OSMO_GAPK_CAT_NAME_SOURCE : OSMO_GAPK_CAT_NAME_SINK;
item->sub_name = "rtp";
item->len_in = in_out_n ? 0 : blk_len;
item->len_out = in_out_n ? blk_len : 0;
item->state = state;
item->proc = in_out_n ? pq_cb_rtp_input : pq_cb_rtp_output;
item->wait = NULL;
item->exit = pq_cb_rtp_exit;
/* Change state's talloc context from pq to item */
talloc_steal(item, state);
return 0;
}
@ -239,9 +250,10 @@ pq_queue_rtp_op(struct pq *pq, int udp_fd, unsigned int blk_len, int in_out_n)
* \param[in] udp_fd UDP file descriptor for the RTP input
* \param[in] blk_len Block Length to read from RTP */
int
pq_queue_rtp_input(struct pq *pq, int udp_fd, unsigned int blk_len)
osmo_gapk_pq_queue_rtp_input(struct osmo_gapk_pq *pq, int udp_fd, unsigned int blk_len)
{
fprintf(stderr, "[+] PQ: Adding RTP input (blk_len=%u)\n", blk_len);
LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding RTP input (blk_len=%u)\n",
pq->name, blk_len);
return pq_queue_rtp_op(pq, udp_fd, blk_len, 1);
}
@ -251,8 +263,9 @@ pq_queue_rtp_input(struct pq *pq, int udp_fd, unsigned int blk_len)
* \param[in] udp_fd UDP file descriptor for the RTP output
* \param[in] blk_len Block Length to read from RTP */
int
pq_queue_rtp_output(struct pq *pq, int udp_fd, unsigned int blk_len)
osmo_gapk_pq_queue_rtp_output(struct osmo_gapk_pq *pq, int udp_fd, unsigned int blk_len)
{
fprintf(stderr, "[+] PQ: Adding RTP output (blk_len=%u)\n", blk_len);
LOGPGAPK(LOGL_DEBUG, "PQ '%s': Adding RTP output (blk_len=%u)\n",
pq->name, blk_len);
return pq_queue_rtp_op(pq, udp_fd, blk_len, 0);
}

View File

@ -20,108 +20,164 @@
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <talloc.h>
#include <gapk/procqueue.h>
#include <osmocom/core/linuxlist.h>
#define VAR_BUF_SIZE 320
#define MAX_PQ_ITEMS 8
struct pq {
int n_items;
struct pq_item* items[MAX_PQ_ITEMS];
void * buffers[MAX_PQ_ITEMS+1];
};
#include <osmocom/gapk/procqueue.h>
#include <osmocom/gapk/logging.h>
/* Internal root talloc context */
extern TALLOC_CTX *gapk_root_ctx;
/* crate a new (empty) processing queue */
struct pq *
pq_create(void)
struct osmo_gapk_pq *
osmo_gapk_pq_create(const char *name)
{
return (struct pq *) calloc(1, sizeof(struct pq));
struct osmo_gapk_pq *pq;
/* Allocate memory for a new processing queue */
pq = talloc_zero(gapk_root_ctx, struct osmo_gapk_pq);
if (!pq)
return NULL;
if (name != NULL) {
/* Rename talloc context */
talloc_set_name(pq, "struct osmo_gapk_pq '%s'", name);
/* Set queue name */
pq->name = name;
}
/* Init its list of items */
INIT_LLIST_HEAD(&pq->items);
return pq;
}
/*! destroy a processing queue, calls exit() callback of each item
* \param[in] pq Processing Queue to be destroyed */
void
pq_destroy(struct pq *pq)
osmo_gapk_pq_destroy(struct osmo_gapk_pq *pq)
{
int i;
struct osmo_gapk_pq_item *item, *item_next;
if (!pq)
return;
for (i=0; i<pq->n_items; i++) {
if (!pq->items[i])
continue;
if (pq->items[i]->exit)
pq->items[i]->exit(pq->items[i]->state);
free(pq->items[i]);
/* Iterate over all items in queue */
llist_for_each_entry_safe(item, item_next, &pq->items, list) {
/* Free output buffer memory */
talloc_free(item->buf);
/* Call exit handler if preset */
if (item->exit)
item->exit(item->state);
/* Delete an item from list */
llist_del(&item->list);
talloc_free(item);
}
for (i=0; i<pq->n_items-1; i++)
free(pq->buffers[i]); /* free is NULL safe */
free(pq);
talloc_free(pq);
}
/*! allocate + add an item to a processing queue; return new item
* \param[in] pq Processing Queue to which item is added
* \returns new PQ item; NULL on error */
struct pq_item *
pq_add_item(struct pq *pq)
struct osmo_gapk_pq_item *
osmo_gapk_pq_add_item(struct osmo_gapk_pq *pq)
{
struct pq_item *item;
struct osmo_gapk_pq_item *item;
if (pq->n_items == MAX_PQ_ITEMS) {
fprintf(stderr, "[!] Processing Queue cannot handle more than %u items\n",
MAX_PQ_ITEMS);
return NULL;
}
item = calloc(1, sizeof(struct pq_item));
/* Allocate memory for a new item */
item = talloc_zero(pq, struct osmo_gapk_pq_item);
if (!item)
return NULL;
pq->items[pq->n_items++] = item;
/* Add one to the end of a queue */
llist_add_tail(&item->list, &pq->items);
/* Increase the items count */
pq->n_items++;
return item;
}
/*! prepare a processing queue; allocates buffers; checks lengths
/*! check a processing queue; make sure I/O data lengths are equal
* \param[in] pq Make sure both source and sink are preset
* \param[in] strict Processing Queue to be checked
* \returns 0 on succcess; negative on error */
int
osmo_gapk_pq_check(struct osmo_gapk_pq *pq, int strict)
{
struct osmo_gapk_pq_item *item_prev = NULL;
struct osmo_gapk_pq_item *item;
/* Make sure I/O data lengths are equal */
llist_for_each_entry(item, &pq->items, list) {
if (item_prev && item->len_in) {
if (item->len_in != item_prev->len_out) {
LOGPGAPK(LOGL_ERROR, "PQ '%s': item '%s/%s' requires "
"input size %u, but previous '%s/%s' has %u\n",
pq->name, item->cat_name, item->sub_name,
item->len_in, item_prev->cat_name,
item_prev->sub_name, item_prev->len_out);
return -EINVAL;
}
}
/* Save pointer to the previous item */
item_prev = item;
}
if (strict) {
/* Make sure the first item is a source */
item = llist_first_entry(&pq->items,
struct osmo_gapk_pq_item, list);
if (item->type != OSMO_GAPK_ITEM_TYPE_SOURCE)
goto src_sink_err;
/* Make sure the last item is a sink */
item = llist_last_entry(&pq->items,
struct osmo_gapk_pq_item, list);
if (item->type != OSMO_GAPK_ITEM_TYPE_SINK)
goto src_sink_err;
}
return 0;
src_sink_err:
LOGPGAPK(LOGL_ERROR, "PQ '%s': the first item should be a source, "
"and the last one should be a sink\n", pq->name);
return -EINVAL;
}
/*! prepare a processing queue; allocates buffers
* \param[in] pq Processing Queue to be prepared
* \returns 0 on succcess; negative on error */
int
pq_prepare(struct pq *pq)
osmo_gapk_pq_prepare(struct osmo_gapk_pq *pq)
{
int i;
unsigned int len_prev;
struct osmo_gapk_pq_item *item;
len_prev = 0;
for (i=0; i<pq->n_items; i++) {
struct pq_item *item = pq->items[i];
if (item->len_in && item->len_in != len_prev) {
fprintf(stderr, "[!] PQ item requires input size %u, but previous output is %u\n",
item->len_in, len_prev);
return -EINVAL;
}
if (i < (pq->n_items-1)) {
/* Iterate over all items in queue */
llist_for_each_entry(item, &pq->items, list) {
/* The sink item doesn't require an output buffer */
if (item->list.next != &pq->items) {
unsigned int buf_size = item->len_out;
/* variable-length codec output, use maximum
* known buffer size */
/**
* Use maximum known buffer size
* for variable-length codec output
*/
if (!buf_size)
buf_size = VAR_BUF_SIZE;
pq->buffers[i] = malloc(buf_size);
if (!pq->buffers[i])
return -ENOMEM;
} else{
if (item->len_out)
return -EINVAL;
}
len_prev = item->len_out;
/* Allocate memory for an output buffer */
item->buf = talloc_named_const(item, buf_size, ".buffer");
if (!item->buf)
return -ENOMEM;
}
}
return 0;
@ -131,30 +187,54 @@ pq_prepare(struct pq *pq)
* \param[in] pq Processing Queue to be executed
* \returns 0 on success; negative on error (if any item returns negative) */
int
pq_execute(struct pq *pq)
osmo_gapk_pq_execute(struct osmo_gapk_pq *pq)
{
int i;
void *buf_prev, *buf;
unsigned int len_prev;
struct osmo_gapk_pq_item *item;
unsigned int len_prev = 0;
uint8_t *buf_prev = NULL;
int rv;
buf_prev = NULL;
len_prev = 0;
for (i=0; i<pq->n_items; i++) {
int rv;
struct pq_item *item = pq->items[i];
buf = i < (pq->n_items-1) ? pq->buffers[i] : NULL;
rv = item->proc(item->state, buf, buf_prev, len_prev);
/* Iterate over all items in queue */
llist_for_each_entry(item, &pq->items, list) {
/* Call item's processing handler */
rv = item->proc(item->state, item->buf, buf_prev, len_prev);
if (rv < 0) {
fprintf(stderr, "[!] pq_execute(): abort, item returned %d\n", rv);
LOGPGAPK(LOGL_ERROR, "PQ '%s': execution aborted: "
"item '%s/%s' returned %d\n", pq->name,
item->cat_name, item->sub_name, rv);
return rv;
}
buf_prev = buf;
buf_prev = item->buf;
len_prev = rv;
}
return 0;
}
char *
osmo_gapk_pq_describe(struct osmo_gapk_pq *pq)
{
struct osmo_gapk_pq_item *item;
char *result = NULL;
int i = 0;
/* Nothing to describe */
if (!pq->n_items)
return NULL;
/* Iterate over all items in queue */
llist_for_each_entry(item, &pq->items, list) {
result = talloc_asprintf_append(result, "%s/%s%s",
item->cat_name, item->sub_name,
++i < pq->n_items ? " -> " : "");
}
/* Change talloc context name */
talloc_set_name_const(result, ".description");
/* Change parent talloc context to pq */
talloc_steal(pq, result);
return result;
}

93
tests/Makefile.am Normal file
View File

@ -0,0 +1,93 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_builddir) \
-I$(top_srcdir)/include \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOCODEC_CFLAGS) \
$(NULL)
check_PROGRAMS = \
procqueue/pq_test \
io/pq_file_test \
io/pq_rtp_test \
$(NULL)
procqueue_pq_test_SOURCES = procqueue/pq_test.c
procqueue_pq_test_LDADD = \
$(top_builddir)/src/libosmogapk.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODEC_LIBS) \
$(TALLOC_LIBS) \
$(NULL)
io_pq_file_test_SOURCES = io/pq_file_test.c
io_pq_file_test_LDADD = \
$(top_builddir)/src/libosmogapk.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODEC_LIBS) \
$(TALLOC_LIBS) \
$(NULL)
io_pq_rtp_test_SOURCES = io/pq_rtp_test.c
io_pq_rtp_test_LDADD = \
$(top_builddir)/src/libosmogapk.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODEC_LIBS) \
$(TALLOC_LIBS) \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
echo '# Signature of the current package.' && \
echo 'm4_define([AT_PACKAGE_NAME],' && \
echo ' [$(PACKAGE_NAME)])' && \
echo 'm4_define([AT_PACKAGE_TARNAME],' && \
echo ' [$(PACKAGE_TARNAME)])' && \
echo 'm4_define([AT_PACKAGE_VERSION],' && \
echo ' [$(PACKAGE_VERSION)])' && \
echo 'm4_define([AT_PACKAGE_STRING],' && \
echo ' [$(PACKAGE_STRING)])' && \
echo 'm4_define([AT_PACKAGE_BUGREPORT],' && \
echo ' [$(PACKAGE_BUGREPORT)])'; \
echo 'm4_define([AT_PACKAGE_URL],' && \
echo ' [$(PACKAGE_URL)])'; \
} >'$(srcdir)/package.m4'
EXTRA_DIST = \
testsuite.at \
$(TESTSUITE) \
$(srcdir)/package.m4 \
$(NULL)
EXTRA_DIST += \
procqueue/pq_test.ok \
io/pq_file_test.ok \
io/pq_rtp_test.ok \
io/io_sample.txt \
ref-files/* \
$(NULL)
DISTCLEANFILES = atconfig
TESTSUITE = $(srcdir)/testsuite
check-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
installcheck-local: atconfig $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \
$(TESTSUITEFLAGS)
clean-local:
test ! -f '$(TESTSUITE)' || \
$(SHELL) '$(TESTSUITE)' --clean
AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te
AUTOTEST = $(AUTOM4TE) --language=autotest
$(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4
$(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
mv $@.tmp $@

View File

@ -1,13 +1,13 @@
# directory containing the reference files for comparing against
REFDIR=./ref-files
if [ -f ../src/gapk ]; then
GAPK=../src/gapk
elif [ -f `which gapk` ]; then
GAPK=`which gapk`
if [ -f ../src/osmo-gapk ]; then
GAPK=../src/osmo-gapk
elif [ -f `which osmo-gapk` ]; then
GAPK=`which osmo-gapk`
else
exit 1
fi
echo Using gapk found at $GAPK
echo Using osmo-gapk found at $GAPK
FORMATS="amr-efr gsm racal-hr racal-fr racal-efr ti-hr ti-fr ti-efr rtp-efr rtp-hr-etsi rtp-hr-ietf"

1
tests/io/io_sample.txt Normal file
View File

@ -0,0 +1 @@
8217C8FB7675A95008F9089D883gapk

149
tests/io/pq_file_test.c Normal file
View File

@ -0,0 +1,149 @@
/*
* This file is part of GAPK (GSM Audio Pocket Knife).
*
* (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
*
* GAPK is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GAPK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAPK. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <errno.h>
#include <talloc.h>
#include <assert.h>
#include <osmocom/gapk/procqueue.h>
#include <osmocom/gapk/common.h>
/**
* This test is intended to check the file source / sink
* operability. To do that, the following processing chain
* is being composed:
*
* source/file -> proc/dummy -> sink/file (stdout)
*
* The source item opens the sample file named 'io_sample.txt'
* for reading. The next processing item simply converts all
* uppercase latters to the lowercase. The last one writes
* the result to stdout.
*
* This processing cycle is being repeated several times
* with different block length values.
*/
static void talloc_ctx_walk_cb(const void *chunk, int depth,
int max_depth, int is_ref, void *data)
{
const char *chunk_name = talloc_get_name(chunk);
int spaces_cnt;
/* Hierarchical spacing */
for (spaces_cnt = 0; spaces_cnt < depth; spaces_cnt++)
printf(" ");
/* Chunk info */
printf("chunk %s: depth=%d\n", chunk_name, depth);
}
static int pq_file_proc(void *state, uint8_t *out, const uint8_t *in,
unsigned int in_len)
{
int i;
/* Change upper case to lower */
for (i = 0; i < in_len; i++)
out[i] = (in[i] >= 65 && in[i] <= 90) ?
in[i] + 32 : in[i];
return in_len;
}
static int pq_file_check(FILE *src_file, unsigned int blk_len)
{
struct osmo_gapk_pq_item *proc_item;
struct osmo_gapk_pq *pq;
int rc;
printf("Processing sample file with blk_len=%u:\n", blk_len);
/* Allocate a processing queue */
pq = osmo_gapk_pq_create("pq_file_check");
assert(pq != NULL);
/* Add a file sink */
rc = osmo_gapk_pq_queue_file_input(pq, src_file, blk_len);
assert(rc == 0);
/* Add a processing item */
proc_item = osmo_gapk_pq_add_item(pq);
assert(proc_item != NULL);
/* Set up I/O parameters */
proc_item->type = OSMO_GAPK_ITEM_TYPE_PROC;
proc_item->len_in = blk_len;
proc_item->len_out = blk_len;
/* Set the processing callback */
proc_item->proc = &pq_file_proc;
/* Add a sink item */
rc = osmo_gapk_pq_queue_file_output(pq, stdout, blk_len);
assert(rc == 0);
/* Check a queue in strict mode */
rc = osmo_gapk_pq_check(pq, 1);
assert(rc == 0);
/* Prepare a queue */
rc = osmo_gapk_pq_prepare(pq);
assert(rc == 0);
while (1) {
rc = osmo_gapk_pq_execute(pq);
if (rc)
break;
}
/* Destroy processing queue */
osmo_gapk_pq_destroy(pq);
return 0;
}
int main(int argc, char **argv)
{
int i;
/* Enable tracking the use of NULL memory contexts */
talloc_enable_null_tracking();
/* Open sample file */
FILE *sample_file = fopen(argv[1], "rb");
assert(sample_file != NULL);
/* Process sample file with different blk_len values */
for (i = 2; i <= 32; i *= 2) {
pq_file_check(sample_file, i);
rewind(sample_file);
}
printf("\n");
/* Close sample file */
fclose(sample_file);
/* Print talloc memory hierarchy */
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
return 0;
}

12
tests/io/pq_file_test.ok Normal file
View File

@ -0,0 +1,12 @@
Processing sample file with blk_len=2:
8217c8fb7675a95008f9089d883gapk
Processing sample file with blk_len=4:
8217c8fb7675a95008f9089d883gapk
Processing sample file with blk_len=8:
8217c8fb7675a95008f9089d883gapk
Processing sample file with blk_len=16:
8217c8fb7675a95008f9089d883gapk
Processing sample file with blk_len=32:
8217c8fb7675a95008f9089d883gapk
chunk null_context: depth=0

349
tests/io/pq_rtp_test.c Normal file
View File

@ -0,0 +1,349 @@
/*
* This file is part of GAPK (GSM Audio Pocket Knife).
*
* (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
*
* GAPK is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GAPK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAPK. If not, see <http://www.gnu.org/licenses/>.
*/
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <talloc.h>
#include <assert.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/socket.h>
#include <osmocom/gapk/procqueue.h>
#include <osmocom/gapk/common.h>
/**
* This test is intended to check the RTP source / sink operability.
* To do this, two processing queues are being allocated:
*
* "generator": source/random -> sink/rtp
* "checker": source/rtp -> sink/checker
*
* The first one generates some amount of random bytes (payload),
* and stores them inside a buffer that is shared between both
* queues.
*
* After generation, a payload is being sent from the first
* queue via an RTP sink, and then being received by the second
* via an RTP source.
*
* As both queues do use a shared buffer, the last item of the
* second queue (named 'sink/checker') is able to compare a
* received payload with expected.
*/
static void talloc_ctx_walk_cb(const void *chunk, int depth,
int max_depth, int is_ref, void *data)
{
const char *chunk_name = talloc_get_name(chunk);
int spaces_cnt;
/* Hierarchical spacing */
for (spaces_cnt = 0; spaces_cnt < depth; spaces_cnt++)
printf(" ");
/* Chunk info */
printf("chunk %s: depth=%d\n", chunk_name, depth);
}
#define RTP_TEST_BUF_LEN 128
struct rtp_test_state {
unsigned int payload_len;
unsigned int rtp_port;
int rtp_src_fd;
int rtp_dst_fd;
uint8_t data[RTP_TEST_BUF_LEN];
uint8_t *ptr;
};
static int src_rand_proc(void *data, uint8_t *out, const uint8_t *in,
unsigned int in_len)
{
struct rtp_test_state *state = (struct rtp_test_state *) data;
unsigned int i;
/* Generate a random payload */
for (i = 0; i < state->payload_len; i++) {
uint8_t byte = rand() % 0xff;
*(state->ptr + i) = byte;
out[i] = byte;
}
return state->payload_len;
}
static void src_rand_exit(void *data)
{
struct rtp_test_state *state = (struct rtp_test_state *) data;
if (state->rtp_src_fd >= 0) {
close(state->rtp_src_fd);
state->rtp_src_fd = -1;
}
}
static int sink_chk_proc(void *data, uint8_t *out, const uint8_t *in,
unsigned int in_len)
{
struct rtp_test_state *state = (struct rtp_test_state *) data;
unsigned int i;
/* Make sure we have all bytes transferred */
if (in_len != state->payload_len) {
printf("Data length mismatch!\n");
return -EINVAL;
}
for (i = 0; i < in_len; i++) {
if (in[i] != *(state->ptr + i)) {
printf("Data mismatch!\n");
return -EINVAL;
}
}
return in_len;
}
static void sink_chk_exit(void *data)
{
struct rtp_test_state *state = (struct rtp_test_state *) data;
if (state->rtp_dst_fd >= 0) {
close(state->rtp_dst_fd);
state->rtp_dst_fd = -1;
}
}
/* Allocates: source/random -> sink/rtp */
static int init_gen_queue(struct osmo_gapk_pq *pq,
struct rtp_test_state *state, unsigned int payload_len)
{
int rc;
/* Allocate memory for the 'source/random' */
struct osmo_gapk_pq_item *src_rand = osmo_gapk_pq_add_item(pq);
if (!src_rand)
return -ENOMEM;
/* Fill in meta information */
src_rand->type = OSMO_GAPK_ITEM_TYPE_SOURCE;
src_rand->cat_name = OSMO_GAPK_CAT_NAME_SOURCE;
src_rand->sub_name = "random";
/* Set I/O buffer lengths */
state->payload_len = payload_len;
src_rand->len_out = payload_len;
/* Set proc / exit callbacks and state */
src_rand->proc = &src_rand_proc;
src_rand->exit = &src_rand_exit;
src_rand->state = state;
/* Init connection socket */
state->rtp_dst_fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM,
IPPROTO_UDP, "127.0.0.1", state->rtp_port, OSMO_SOCK_F_CONNECT);
if (state->rtp_dst_fd < 0) {
printf("Could not init connection socket\n");
return -EINVAL;
}
/* Init an RTP sink */
rc = osmo_gapk_pq_queue_rtp_output(pq, state->rtp_dst_fd, payload_len);
if (rc) {
printf("Could not init an RTP sink\n");
return rc;
}
/* Check and prepare */
rc = osmo_gapk_pq_check(pq, 1);
if (rc) {
printf("Queue check failed\n");
return rc;
}
rc = osmo_gapk_pq_prepare(pq);
if (rc) {
printf("Queue preparation failed\n");
return rc;
}
return 0;
}
/* Allocates: source/rtp -> sink/checker */
static int init_chk_queue(struct osmo_gapk_pq *pq,
struct rtp_test_state *state, unsigned int payload_len)
{
int rc;
/* Init listening socket */
state->rtp_src_fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM,
IPPROTO_UDP, "127.0.0.1", 0, OSMO_SOCK_F_BIND);
if (state->rtp_src_fd < 0) {
printf("Could not init listening socket\n");
return -EINVAL;
}
/* Init an RTP source on any available port */
rc = osmo_gapk_pq_queue_rtp_input(pq, state->rtp_src_fd, payload_len);
if (rc) {
printf("Could not init an RTP sink\n");
return rc;
}
/* Determine on which port are we listening */
struct sockaddr_in adr_inet;
socklen_t len_inet;
len_inet = sizeof(adr_inet);
rc = getsockname(state->rtp_src_fd,
(struct sockaddr *) &adr_inet, &len_inet);
if (rc)
return -EINVAL;
/* Save assigned port to shared state */
state->rtp_port = (unsigned int) ntohs(adr_inet.sin_port);
/* Allocate memory for the 'sink/checker' */
struct osmo_gapk_pq_item *sink_chk = osmo_gapk_pq_add_item(pq);
if (!sink_chk)
return -ENOMEM;
/* Fill in meta information */
sink_chk->type = OSMO_GAPK_ITEM_TYPE_SINK;
sink_chk->cat_name = OSMO_GAPK_CAT_NAME_SINK;
sink_chk->sub_name = "checker";
/* Set I/O buffer lengths */
sink_chk->len_in = payload_len;
/* Set proc / exit callbacks and state */
sink_chk->proc = &sink_chk_proc;
sink_chk->exit = &sink_chk_exit;
sink_chk->state = state;
/* Check and prepare */
rc = osmo_gapk_pq_check(pq, 1);
if (rc) {
printf("Queue check failed\n");
return rc;
}
rc = osmo_gapk_pq_prepare(pq);
if (rc) {
printf("Queue preparation failed\n");
return rc;
}
return 0;
}
static int rtp_test(struct rtp_test_state *state, unsigned int payload_len)
{
struct osmo_gapk_pq *q_gen, *q_chk;
unsigned int i, chunks;
int rc;
/* Allocate two queues */
q_gen = osmo_gapk_pq_create("generator");
q_chk = osmo_gapk_pq_create("checker");
/* Make sure both queues are allocated */
if (!q_gen || !q_chk) {
rc = -ENOMEM;
goto exit;
}
/* Init both queues: generator and checker */
rc = init_chk_queue(q_chk, state, payload_len);
if (rc)
goto exit;
rc = init_gen_queue(q_gen, state, payload_len);
if (rc)
goto exit;
/* Calculate how much chunks do we have */
chunks = RTP_TEST_BUF_LEN / payload_len;
/* Execute both queues */
for (i = 0; i < chunks; i++) {
/* Move data pointer */
state->ptr = state->data + i * payload_len;
/* Generate and send a payload */
rc = osmo_gapk_pq_execute(q_gen);
if (rc) {
printf("Queue '%s' execution aborted on chunk %u/%u\n",
q_gen->name, i + 1, chunks);
goto exit;
}
/* TODO: prevent test hang if nothing was being sent */
/* Receive and check a payload */
rc = osmo_gapk_pq_execute(q_chk);
if (rc) {
printf("Queue '%s' execution aborted on chunk %u/%u\n",
q_gen->name, i + 1, chunks);
goto exit;
}
}
printf("Payload len=%u check ok\n", payload_len);
exit:
/* Deallocate both queues and data */
osmo_gapk_pq_destroy(q_gen);
osmo_gapk_pq_destroy(q_chk);
return rc;
}
int main(int argc, char **argv)
{
struct rtp_test_state state;
unsigned int len;
/* Enable tracking the use of NULL memory contexts */
talloc_enable_null_tracking();
/* Init pseudo-random generator */
srand(time(NULL));
/* Perform testing with different payload size values */
for (len = 1; len <= RTP_TEST_BUF_LEN; len *= 2)
assert(rtp_test(&state, len) == 0);
printf("\n");
/* Memory leak detection test */
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
return 0;
}

10
tests/io/pq_rtp_test.ok Normal file
View File

@ -0,0 +1,10 @@
Payload len=1 check ok
Payload len=2 check ok
Payload len=4 check ok
Payload len=8 check ok
Payload len=16 check ok
Payload len=32 check ok
Payload len=64 check ok
Payload len=128 check ok
chunk null_context: depth=0

373
tests/procqueue/pq_test.c Normal file
View File

@ -0,0 +1,373 @@
/*
* This file is part of GAPK (GSM Audio Pocket Knife).
*
* (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
*
* GAPK is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GAPK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAPK. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <talloc.h>
#include <assert.h>
#include <osmocom/gapk/procqueue.h>
#include <osmocom/gapk/common.h>
/**
* This test is intended to validate the processing queue
* management API. Moreover, the talloc debugging API is
* used to ensure that there are no memory leaks.
*
* First, four processing queues are being allocated. One
* of them is empty, while others have different count of
* items. Then the human-readable description is being
* generated for all of them. And finally, the processing
* and exit callback are being tested.
*
* During the test execution, the talloc NULL-context
* tracking feature is enabled, allowing to observe every
* memory allocation within the libosmogapk, and to detect
* memory leaks.
*/
static void talloc_ctx_walk_cb(const void *chunk, int depth,
int max_depth, int is_ref, void *data)
{
const char *chunk_name = talloc_get_name(chunk);
int spaces_cnt;
/* Hierarchical spacing */
for (spaces_cnt = 0; spaces_cnt < depth; spaces_cnt++)
printf(" ");
/* Chunk info */
printf("chunk %s: depth=%d\n", chunk_name, depth);
}
static int q3_i0_proc(void *state, uint8_t *out, const uint8_t *in,
unsigned int in_len)
{
int i;
printf("Incoming data: ");
for (i = 0; i < 10; i++) {
printf("%d ", i);
out[i] = i;
}
printf("\n");
return 0;
}
static int q3_i1_proc(void *state, uint8_t *out, const uint8_t *in,
unsigned int in_len)
{
int i;
for (i = 0; i < 10; i++)
out[i] = in[i] % 2;
return 0;
}
static int q3_i2_proc(void *state, uint8_t *out, const uint8_t *in,
unsigned int in_len)
{
int i;
printf("Outgoing data: ");
for (i = 0; i < 10; i++)
printf("%d ", in[i]);
printf("\n");
return 0;
}
static void q3_exit(void *state)
{
struct osmo_gapk_pq_item *item;
/* Make sure the item's state is passed correctly */
assert(state != NULL);
item = (struct osmo_gapk_pq_item *) state;
printf("Calling exit callback for '%s/%s'\n",
item->cat_name, item->sub_name);
}
int main(int argc, char **argv)
{
/* Enable tracking the use of NULL memory contexts */
talloc_enable_null_tracking();
/**
* 1. Processing queue allocation test
*/
struct osmo_gapk_pq *q0, *q1, *q2, *q3;
printf("Processing queue allocation test:\n");
/* Allocate four queues */
q0 = osmo_gapk_pq_create(NULL);
q1 = osmo_gapk_pq_create("q1");
q2 = osmo_gapk_pq_create("q2");
q3 = osmo_gapk_pq_create("q3");
/* Make sure all queues are allocated */
assert(q0 != NULL);
assert(q1 != NULL);
assert(q2 != NULL);
assert(q3 != NULL);
/* Print talloc memory hierarchy */
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
printf("\n");
/**
* 2. Item allocation test
*/
struct osmo_gapk_pq_item *q3_i0, *q3_i1, *q3_i2;
struct osmo_gapk_pq_item *q2_i0, *q2_i1;
struct osmo_gapk_pq_item *q1_i0;
printf("Item allocation test:\n");
/* Make sure there are no items */
assert(q0->n_items == 0);
assert(q1->n_items == 0);
assert(q2->n_items == 0);
assert(q3->n_items == 0);
/* Allocate items */
q3_i0 = osmo_gapk_pq_add_item(q3);
q3_i1 = osmo_gapk_pq_add_item(q3);
q3_i2 = osmo_gapk_pq_add_item(q3);
q2_i0 = osmo_gapk_pq_add_item(q2);
q2_i1 = osmo_gapk_pq_add_item(q2);
q1_i0 = osmo_gapk_pq_add_item(q1);
/* Make sure all items are allocated */
assert(q3_i0 != NULL);
assert(q3_i1 != NULL);
assert(q3_i2 != NULL);
assert(q2_i0 != NULL);
assert(q2_i1 != NULL);
assert(q1_i0 != NULL);
/* Check item count */
assert(q0->n_items == 0);
assert(q1->n_items == 1);
assert(q2->n_items == 2);
assert(q3->n_items == 3);
/* Print talloc memory hierarchy */
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
printf("\n");
/**
* 3. Items persistence test
*/
struct osmo_gapk_pq_item *item;
/* Make sure that q0 is empty, others are not */
assert(llist_empty(&q0->items) == 1);
assert(llist_empty(&q1->items) == 0);
assert(llist_empty(&q2->items) == 0);
assert(llist_empty(&q3->items) == 0);
/* A single item must be the first and the last in a queue */
item = llist_first_entry(&q1->items, struct osmo_gapk_pq_item, list);
assert(item == q1_i0);
item = llist_last_entry(&q1->items, struct osmo_gapk_pq_item, list);
assert(item == q1_i0);
/* Two items: one is the first, second is the last */
item = llist_first_entry(&q2->items, struct osmo_gapk_pq_item, list);
assert(item == q2_i0);
item = llist_last_entry(&q2->items, struct osmo_gapk_pq_item, list);
assert(item == q2_i1);
/* Three items: one is the first, third is the last */
item = llist_first_entry(&q3->items, struct osmo_gapk_pq_item, list);
assert(item == q3_i0);
assert(item != q3_i1);
item = llist_last_entry(&q3->items, struct osmo_gapk_pq_item, list);
assert(item == q3_i2);
assert(item != q3_i1);
/**
* 4. Queue I/O data lengths check
*/
q3_i0->len_in = 10;
q3_i0->len_out = 20;
q3_i1->len_in = 20;
q3_i1->len_out = 30;
q3_i2->len_in = 30;
q3_i2->len_out = 10;
/* Normal case */
assert(osmo_gapk_pq_check(q3, 0) == 0);
/* Abnormal case (I/O length mismatch) */
q3_i0->len_out = 10;
assert(osmo_gapk_pq_check(q3, 0) != 0);
q3_i0->len_out = 20;
/* Check queue in strict mode (requires src -> ... -> sink) */
q3_i0->type = OSMO_GAPK_ITEM_TYPE_SOURCE;
q3_i1->type = OSMO_GAPK_ITEM_TYPE_PROC;
q3_i2->type = OSMO_GAPK_ITEM_TYPE_SINK;
/* Normal case (src -> proc -> sink) */
assert(osmo_gapk_pq_check(q3, 1) == 0);
/* Abnormal case (proc -> proc -> sink) */
q3_i0->type = OSMO_GAPK_ITEM_TYPE_PROC;
assert(osmo_gapk_pq_check(q3, 1) != 0);
q3_i0->type = OSMO_GAPK_ITEM_TYPE_SOURCE;
/**
* 5. Buffer allocation test
*/
printf("Queue preparation test:\n");
/* Prepare the queue */
assert(osmo_gapk_pq_prepare(q3) == 0);
/* Make sure buffers were allocated */
assert(q3_i0->buf != NULL);
assert(q3_i1->buf != NULL);
/* Currently, we don't allocate buffers for sinks */
assert(q3_i2->buf == NULL);
/* Compare required vs allocated buffer sizes */
assert(talloc_total_size(q3_i0->buf) == q3_i0->len_out);
assert(talloc_total_size(q3_i1->buf) == q3_i1->len_out);
/* Print talloc memory hierarchy */
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
printf("\n");
/**
* 6. Queue description test
*/
char *queue_desc;
printf("Queue description test:\n");
/* An empty queue doesn't have description */
queue_desc = osmo_gapk_pq_describe(q0);
assert(queue_desc == NULL);
/* Fill in some data */
q3_i0->cat_name = OSMO_GAPK_CAT_NAME_SOURCE;
q3_i0->sub_name = "file";
q3_i1->cat_name = OSMO_GAPK_CAT_NAME_PROC;
q3_i1->sub_name = "dummy";
q3_i2->cat_name = OSMO_GAPK_CAT_NAME_SINK;
q3_i2->sub_name = "alsa";
q2_i0->cat_name = OSMO_GAPK_CAT_NAME_SOURCE;
q2_i0->sub_name = "dummy";
q2_i1->cat_name = OSMO_GAPK_CAT_NAME_SINK;
q2_i1->sub_name = "dummy";
q1_i0->cat_name = "dummy";
q1_i0->sub_name = "dummy";
queue_desc = osmo_gapk_pq_describe(q3);
assert(queue_desc != NULL);
printf("Queue q3 description: %s\n", queue_desc);
talloc_free(queue_desc);
queue_desc = osmo_gapk_pq_describe(q2);
assert(queue_desc != NULL);
printf("Queue q2 description: %s\n", queue_desc);
talloc_free(queue_desc);
queue_desc = osmo_gapk_pq_describe(q1);
assert(queue_desc != NULL);
printf("Queue q1 description: %s\n\n", queue_desc);
talloc_free(queue_desc);
/**
* 7. Queue execution test
*/
printf("Processing queue execution test:\n");
/* Make sure there are no callbacks by default */
assert(q3_i0->proc == NULL);
assert(q3_i0->wait == NULL);
assert(q3_i0->exit == NULL);
assert(q3_i1->proc == NULL);
assert(q3_i1->wait == NULL);
assert(q3_i1->exit == NULL);
assert(q3_i2->proc == NULL);
assert(q3_i2->wait == NULL);
assert(q3_i2->exit == NULL);
q3_i0->proc = &q3_i0_proc;
q3_i1->proc = &q3_i1_proc;
q3_i2->proc = &q3_i2_proc;
assert(osmo_gapk_pq_execute(q3) == 0);
printf("\n");
/**
* 8. Exit callback & deallocation tests
*/
printf("Processing queue exit callback test:\n");
q3_i0->state = q3_i0;
q3_i1->state = q3_i1;
q3_i2->state = q3_i2;
q3_i0->exit = &q3_exit;
q3_i1->exit = &q3_exit;
q3_i2->exit = &q3_exit;
osmo_gapk_pq_destroy(q0);
osmo_gapk_pq_destroy(q1);
osmo_gapk_pq_destroy(q2);
osmo_gapk_pq_destroy(q3);
printf("\n");
/**
* 9. Memory leak detection test
*/
printf("Processing queue deallocation test:\n");
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
return 0;
}

View File

@ -0,0 +1,60 @@
Processing queue allocation test:
chunk null_context: depth=0
chunk struct osmo_gapk_pq 'q3': depth=1
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q2': depth=1
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q1': depth=1
chunk .name: depth=2
chunk struct osmo_gapk_pq: depth=1
Item allocation test:
chunk null_context: depth=0
chunk struct osmo_gapk_pq 'q3': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q2': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q1': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq: depth=1
Queue preparation test:
chunk null_context: depth=0
chunk struct osmo_gapk_pq 'q3': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk .buffer: depth=3
chunk struct osmo_gapk_pq_item: depth=2
chunk .buffer: depth=3
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q2': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q1': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq: depth=1
Queue description test:
Queue q3 description: source/file -> proc/dummy -> sink/alsa
Queue q2 description: source/dummy -> sink/dummy
Queue q1 description: dummy/dummy
Processing queue execution test:
Incoming data: 0 1 2 3 4 5 6 7 8 9
Outgoing data: 0 1 0 1 0 1 0 1 0 1
Processing queue exit callback test:
Calling exit callback for 'source/file'
Calling exit callback for 'proc/dummy'
Calling exit callback for 'sink/alsa'
Processing queue deallocation test:
chunk null_context: depth=0

244
tests/testsuite.at Normal file
View File

@ -0,0 +1,244 @@
AT_INIT
AT_BANNER([Regression tests.])
AT_SETUP([procqueue])
AT_KEYWORDS([procqueue])
cat $abs_srcdir/procqueue/pq_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/procqueue/pq_test], [0], [expout])
AT_CLEANUP
AT_SETUP([io/pq_file])
AT_KEYWORDS([pq_file])
cat $abs_srcdir/io/pq_file_test.ok > expout
AT_CHECK([
$abs_top_builddir/tests/io/pq_file_test \
$abs_top_builddir/tests/io/io_sample.txt],
[0], [expout])
AT_CLEANUP
AT_SETUP([io/pq_rtp])
AT_KEYWORDS([pq_rtp])
cat $abs_srcdir/io/pq_rtp_test.ok > expout
AT_CHECK([
$abs_top_builddir/tests/io/pq_rtp_test], [0], [expout])
AT_CLEANUP
AT_SETUP([conv/enc/amr_efr])
AT_KEYWORDS([amr_efr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.amr-efr > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g amr-efr
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/gsm])
AT_KEYWORDS([gsm])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.gsm > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g gsm
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/racal_hr])
AT_KEYWORDS([racal_hr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-hr > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g racal-hr
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/racal_fr])
AT_KEYWORDS([racal_fr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-fr > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g racal-fr
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/racal_efr])
AT_KEYWORDS([racal_efr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-efr > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g racal-efr
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/ti_hr])
AT_KEYWORDS([ti_hr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-hr > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g ti-hr
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/ti_fr])
AT_KEYWORDS([ti_fr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-fr > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g ti-fr
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/ti_efr])
AT_KEYWORDS([ti_efr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-efr > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g ti-efr
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/rtp_efr])
AT_KEYWORDS([rtp_efr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-efr > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g rtp-efr
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/rtp_hr_etsi])
AT_KEYWORDS([rtp_hr_etsi])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g rtp-hr-etsi
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/enc/rtp_hr_ietf])
AT_KEYWORDS([rtp_hr_ietf])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16 \
-f rawpcm-s16le -g rtp-hr-ietf
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/amr_efr])
AT_KEYWORDS([amr_efr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.amr-efr.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.amr-efr \
-f amr-efr -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/gsm])
AT_KEYWORDS([gsm])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.gsm.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.gsm \
-f gsm -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/racal_hr])
AT_KEYWORDS([racal_hr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-hr.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-hr \
-f racal-hr -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/racal_fr])
AT_KEYWORDS([racal_fr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-fr.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-fr \
-f racal-fr -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/racal_efr])
AT_KEYWORDS([racal_efr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-efr.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.racal-efr \
-f racal-efr -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/ti_hr])
AT_KEYWORDS([ti_hr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-hr.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-hr \
-f ti-hr -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/ti_fr])
AT_KEYWORDS([ti_fr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-fr.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-fr \
-f ti-fr -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/ti_efr])
AT_KEYWORDS([ti_efr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-efr.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.ti-efr \
-f ti-efr -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/rtp_efr])
AT_KEYWORDS([rtp_efr])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-efr \
-f rtp-efr -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/rtp_hr_etsi])
AT_KEYWORDS([rtp_hr_etsi])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi \
-f rtp-hr-etsi -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([conv/dec/rtp_hr_ietf])
AT_KEYWORDS([rtp_hr_ietf])
cat $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16 > expout
AT_CHECK([
$abs_top_builddir/src/osmo-gapk \
-i $abs_srcdir/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf \
-f rtp-hr-ietf -g rawpcm-s16le
], [0], [expout], [ignore])
AT_CLEANUP