aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore14
-rw-r--r--Makefile.am5
-rw-r--r--configure.ac19
-rwxr-xr-xcontrib/benchmark.sh2
-rw-r--r--include/Makefile.am17
-rw-r--r--include/gapk/Makefile.am6
-rw-r--r--include/gapk/benchmark.h60
-rw-r--r--include/gapk/procqueue.h72
-rw-r--r--include/gsmhr/gsmhr.h5
-rw-r--r--include/osmocom/gapk/bench.h49
-rw-r--r--include/osmocom/gapk/benchmark.h42
-rw-r--r--include/osmocom/gapk/codecs.h (renamed from include/gapk/codecs.h)52
-rw-r--r--include/osmocom/gapk/common.h22
-rw-r--r--include/osmocom/gapk/formats.h (renamed from include/gapk/formats.h)53
-rw-r--r--include/osmocom/gapk/get_cycles.h (renamed from include/gapk/get_cycles.h)5
-rw-r--r--include/osmocom/gapk/logging.h28
-rw-r--r--include/osmocom/gapk/procqueue.h105
-rw-r--r--include/osmocom/gapk/utils.h (renamed from include/gapk/utils.h)5
-rw-r--r--libosmogapk.pc.in11
-rw-r--r--src/Makefile.am105
-rw-r--r--src/app_osmo_gapk.c (renamed from src/main.c)353
-rw-r--r--src/benchmark.c92
-rw-r--r--src/codec_amr.c17
-rw-r--r--src/codec_efr.c15
-rw-r--r--src/codec_fr.c7
-rw-r--r--src/codec_hr.c7
-rw-r--r--src/codec_pcm.c4
-rw-r--r--src/codecs.c16
-rw-r--r--src/common.c38
-rw-r--r--src/fmt_amr.c8
-rw-r--r--src/fmt_amr_opencore.c8
-rw-r--r--src/fmt_gsm.c6
-rw-r--r--src/fmt_hr_ref.c10
-rw-r--r--src/fmt_racal.c12
-rw-r--r--src/fmt_rawpcm.c6
-rw-r--r--src/fmt_rtp_amr.c8
-rw-r--r--src/fmt_rtp_efr.c8
-rw-r--r--src/fmt_rtp_hr_etsi.c8
-rw-r--r--src/fmt_rtp_hr_ietf.c8
-rw-r--r--src/fmt_ti.c12
-rw-r--r--src/formats.c46
-rw-r--r--src/libosmogapk.map4
-rw-r--r--src/pq_alsa.c50
-rw-r--r--src/pq_codec.c34
-rw-r--r--src/pq_file.c42
-rw-r--r--src/pq_format.c35
-rw-r--r--src/pq_rtp.c45
-rw-r--r--src/procqueue.c230
-rw-r--r--tests/Makefile.am93
-rw-r--r--tests/common.sh (renamed from test/common.sh)10
-rw-r--r--tests/io/io_sample.txt1
-rw-r--r--tests/io/pq_file_test.c149
-rw-r--r--tests/io/pq_file_test.ok12
-rw-r--r--tests/io/pq_rtp_test.c349
-rw-r--r--tests/io/pq_rtp_test.ok10
-rwxr-xr-xtests/play_all_formats.sh (renamed from test/play_all_formats.sh)0
-rw-r--r--tests/procqueue/pq_test.c373
-rw-r--r--tests/procqueue/pq_test.ok60
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16 (renamed from test/ref-files/hhgttg_part1_5.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.amr-efr (renamed from test/ref-files/hhgttg_part1_5.s16.amr-efr)bin8006 -> 8006 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.amr-efr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.amr-efr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.gsm (renamed from test/ref-files/hhgttg_part1_5.s16.gsm)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.gsm.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.gsm.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-efr (renamed from test/ref-files/hhgttg_part1_5.s16.racal-efr)bin7750 -> 7750 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-efr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.racal-efr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-fr (renamed from test/ref-files/hhgttg_part1_5.s16.racal-fr)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-fr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.racal-fr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-hr (renamed from test/ref-files/hhgttg_part1_5.s16.racal-hr)bin3500 -> 3500 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.racal-hr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.racal-hr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-efr (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-efr)bin7750 -> 7750 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-efr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi)bin3500 -> 3500 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf)bin3750 -> 3750 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-efr (renamed from test/ref-files/hhgttg_part1_5.s16.ti-efr)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-efr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.ti-efr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-fr (renamed from test/ref-files/hhgttg_part1_5.s16.ti-fr)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-fr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.ti-fr.s16)bin80000 -> 80000 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-hr (renamed from test/ref-files/hhgttg_part1_5.s16.ti-hr)bin8250 -> 8250 bytes
-rw-r--r--tests/ref-files/hhgttg_part1_5.s16.ti-hr.s16 (renamed from test/ref-files/hhgttg_part1_5.s16.ti-hr.s16)bin80000 -> 80000 bytes
-rwxr-xr-xtests/test_all_formats.sh (renamed from test/test_all_formats.sh)0
-rw-r--r--tests/testsuite.at244
-rwxr-xr-xtests/update_ref_files.sh (renamed from test/update_ref_files.sh)0
84 files changed, 2514 insertions, 593 deletions
diff --git a/.gitignore b/.gitignore
index 93316ad..298604e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
*.*~
diff --git a/Makefile.am b/Makefile.am
index d1638ec..0093f0e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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
diff --git a/configure.ac b/configure.ac
index c24e8f0..180e380 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/contrib/benchmark.sh b/contrib/benchmark.sh
index 0148667..0e86876 100755
--- a/contrib/benchmark.sh
+++ b/contrib/benchmark.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-GAPK=./src/gapk
+GAPK=./src/osmo-gapk
PCMFILE=$1
BASE=`basename $PCMFILE`
diff --git a/include/Makefile.am b/include/Makefile.am
index ddb25df..c20d3ca 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -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
diff --git a/include/gapk/Makefile.am b/include/gapk/Makefile.am
deleted file mode 100644
index 5fcc3b9..0000000
--- a/include/gapk/Makefile.am
+++ /dev/null
@@ -1,6 +0,0 @@
-noinst_HEADERS = benchmark.h \
- codecs.h \
- formats.h \
- get_cycles.h \
- procqueue.h \
- utils.h
diff --git a/include/gapk/benchmark.h b/include/gapk/benchmark.h
deleted file mode 100644
index 49c2c36..0000000
--- a/include/gapk/benchmark.h
+++ /dev/null
@@ -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
diff --git a/include/gapk/procqueue.h b/include/gapk/procqueue.h
deleted file mode 100644
index d9a5546..0000000
--- a/include/gapk/procqueue.h
+++ /dev/null
@@ -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__ */
diff --git a/include/gsmhr/gsmhr.h b/include/gsmhr/gsmhr.h
index 17d42cb..b2fa0da 100644
--- a/include/gsmhr/gsmhr.h
+++ b/include/gsmhr/gsmhr.h
@@ -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__ */
diff --git a/include/osmocom/gapk/bench.h b/include/osmocom/gapk/bench.h
new file mode 100644
index 0000000..404760e
--- /dev/null
+++ b/include/osmocom/gapk/bench.h
@@ -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)
diff --git a/include/osmocom/gapk/benchmark.h b/include/osmocom/gapk/benchmark.h
new file mode 100644
index 0000000..f87899d
--- /dev/null
+++ b/include/osmocom/gapk/benchmark.h
@@ -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);
diff --git a/include/gapk/codecs.h b/include/osmocom/gapk/codecs.h
index aa1c829..253fb14 100644
--- a/include/gapk/codecs.h
+++ b/include/osmocom/gapk/codecs.h
@@ -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;
-const struct codec_desc *codec_get_from_type(enum codec_type 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;
+};
-#endif /* __GAPK_CODECS_H__ */
+const struct osmo_gapk_codec_desc *
+osmo_gapk_codec_get_from_type(enum osmo_gapk_codec_type type);
diff --git a/include/osmocom/gapk/common.h b/include/osmocom/gapk/common.h
new file mode 100644
index 0000000..920824e
--- /dev/null
+++ b/include/osmocom/gapk/common.h
@@ -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);
diff --git a/include/gapk/formats.h b/include/osmocom/gapk/formats.h
index 81670b8..d1521d0 100644
--- a/include/gapk/formats.h
+++ b/include/osmocom/gapk/formats.h
@@ -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);
-
-struct format_desc {
- enum format_type type;
- enum codec_type codec_type;
- const char * name;
- const char * description;
-
- /*! 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 a (global) header at start of file */
- unsigned int header_len;
- /*! exact match for (global) header at start of file */
- const uint8_t * header;
+typedef int (*osmo_gapk_fmt_conv_cb_t)(uint8_t *dst,
+ const uint8_t *src, unsigned int src_len);
+
+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;
+
+ /* 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);
diff --git a/include/gapk/get_cycles.h b/include/osmocom/gapk/get_cycles.h
index 9eb7bc3..40ac570 100644
--- a/include/gapk/get_cycles.h
+++ b/include/osmocom/gapk/get_cycles.h
@@ -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 */
diff --git a/include/osmocom/gapk/logging.h b/include/osmocom/gapk/logging.h
new file mode 100644
index 0000000..60376f1
--- /dev/null
+++ b/include/osmocom/gapk/logging.h
@@ -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)
diff --git a/include/osmocom/gapk/procqueue.h b/include/osmocom/gapk/procqueue.h
new file mode 100644
index 0000000..6b929d7
--- /dev/null
+++ b/include/osmocom/gapk/procqueue.h
@@ -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);
diff --git a/include/gapk/utils.h b/include/osmocom/gapk/utils.h
index 3b02049..0b8b75b 100644
--- a/include/gapk/utils.h
+++ b/include/osmocom/gapk/utils.h
@@ -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__ */
diff --git a/libosmogapk.pc.in b/libosmogapk.pc.in
new file mode 100644
index 0000000..df27402
--- /dev/null
+++ b/libosmogapk.pc.in
@@ -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}/
diff --git a/src/Makefile.am b/src/Makefile.am
index e0b622c..8efd165 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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)
diff --git a/src/main.c b/src/app_osmo_gapk.c
index 58e23c1..729ea25 100644
--- a/src/main.c
+++ b/src/app_osmo_gapk.c
@@ -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;
- }
-
- /* 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");
+ /* Check I/O combinations */
+ int src_count = 0;
+ int sink_count = 0;
+
+ 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;
+ }
+
+ LOGP(DAPP, LOGL_NOTICE, "Processed %d frames\n", frames);
- fprintf(stderr, "[+] 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;
}
diff --git a/src/benchmark.c b/src/benchmark.c
index 91e2ce5..b3bb60b 100644
--- a/src/benchmark.c
+++ b/src/benchmark.c
@@ -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;
- 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);
- }
+ /* 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++)
+ talloc_free(osmo_gapk_bench_codec[i]);
}
diff --git a/src/codec_amr.c b/src/codec_amr.c
index ca614ed..614b744 100644
--- a/src/codec_amr.c
+++ b/src/codec_amr.c
@@ -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",
diff --git a/src/codec_efr.c b/src/codec_efr.c
index 339172a..9804bd9 100644
--- a/src/codec_efr.c
+++ b/src/codec_efr.c
@@ -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",
diff --git a/src/codec_fr.c b/src/codec_fr.c
index 917f34b..eddbee6 100644
--- a/src/codec_fr.c
+++ b/src/codec_fr.c
@@ -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)",
diff --git a/src/codec_hr.c b/src/codec_hr.c
index b3247e0..ef66f57 100644
--- a/src/codec_hr.c
+++ b/src/codec_hr.c
@@ -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",
diff --git a/src/codec_pcm.c b/src/codec_pcm.c
index 9fa8c1b..76cab34 100644
--- a/src/codec_pcm.c
+++ b/src/codec_pcm.c
@@ -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",
diff --git a/src/codecs.c b/src/codecs.c
index 623e80c..be5a112 100644
--- a/src/codecs.c
+++ b/src/codecs.c
@@ -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;
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..c7c5c32
--- /dev/null
+++ b/src/common.c
@@ -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;
+}
diff --git a/src/fmt_amr.c b/src/fmt_amr.c
index e28024c..3694f8a 100644
--- a/src/fmt_amr.c
+++ b/src/fmt_amr.c
@@ -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",
diff --git a/src/fmt_amr_opencore.c b/src/fmt_amr_opencore.c
index 3fa547b..640595e 100644
--- a/src/fmt_amr_opencore.c
+++ b/src/fmt_amr_opencore.c
@@ -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",
diff --git a/src/fmt_gsm.c b/src/fmt_gsm.c
index 27701f0..e97ac9b 100644
--- a/src/fmt_gsm.c
+++ b/src/fmt_gsm.c
@@ -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",
diff --git a/src/fmt_hr_ref.c b/src/fmt_hr_ref.c
index d64918b..0394b76 100644
--- a/src/fmt_hr_ref.c
+++ b/src/fmt_hr_ref.c
@@ -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",
diff --git a/src/fmt_racal.c b/src/fmt_racal.c
index 1dbc61d..7bb53dd 100644
--- a/src/fmt_racal.c
+++ b/src/fmt_racal.c
@@ -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",
diff --git a/src/fmt_rawpcm.c b/src/fmt_rawpcm.c
index 207708c..7b45060 100644
--- a/src/fmt_rawpcm.c
+++ b/src/fmt_rawpcm.c
@@ -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",
diff --git a/src/fmt_rtp_amr.c b/src/fmt_rtp_amr.c
index 1d5357d..a6a3c9f 100644
--- a/src/fmt_rtp_amr.c
+++ b/src/fmt_rtp_amr.c
@@ -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",
diff --git a/src/fmt_rtp_efr.c b/src/fmt_rtp_efr.c
index 0132e32..accfd4f 100644
--- a/src/fmt_rtp_efr.c
+++ b/src/fmt_rtp_efr.c
@@ -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",
diff --git a/src/fmt_rtp_hr_etsi.c b/src/fmt_rtp_hr_etsi.c
index fe6728a..9ed9b22 100644
--- a/src/fmt_rtp_hr_etsi.c
+++ b/src/fmt_rtp_hr_etsi.c
@@ -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",
diff --git a/src/fmt_rtp_hr_ietf.c b/src/fmt_rtp_hr_ietf.c
index 3e8e6a3..4ee548f 100644
--- a/src/fmt_rtp_hr_ietf.c
+++ b/src/fmt_rtp_hr_ietf.c
@@ -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",
diff --git a/src/fmt_ti.c b/src/fmt_ti.c
index 582bff4..d7a1ec7 100644
--- a/src/fmt_ti.c
+++ b/src/fmt_ti.c
@@ -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",
diff --git a/src/formats.c b/src/formats.c
index 60182fe..835af6b 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -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))
diff --git a/src/libosmogapk.map b/src/libosmogapk.map
new file mode 100644
index 0000000..6cd817e
--- /dev/null
+++ b/src/libosmogapk.map
@@ -0,0 +1,4 @@
+LIBOSMOGAPK_1.0 {
+global: osmo_gapk_*;
+local: *;
+};
diff --git a/src/pq_alsa.c b/src/pq_alsa.c
index cad76ca..3550221 100644
--- a/src/pq_alsa.c
+++ b/src/pq_alsa.c
@@ -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);
}
diff --git a/src/pq_codec.c b/src/pq_codec.c
index f1884fb..87afd82 100644
--- a/src/pq_codec.c
+++ b/src/pq_codec.c
@@ -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;
diff --git a/src/pq_file.c b/src/pq_file.c
index 96f7b3f..22dc9cd 100644
--- a/src/pq_file.c
+++ b/src/pq_file.c
@@ -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);
}
diff --git a/src/pq_format.c b/src/pq_format.c
index d6bc8af..2c4a2fd 100644
--- a/src/pq_format.c
+++ b/src/pq_format.c
@@ -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;
}
diff --git a/src/pq_rtp.c b/src/pq_rtp.c
index f5603de..a50013a 100644
--- a/src/pq_rtp.c
+++ b/src/pq_rtp.c
@@ -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);
}
diff --git a/src/procqueue.c b/src/procqueue.c
index 01ad581..78dee4d 100644
--- a/src/procqueue.c
+++ b/src/procqueue.c
@@ -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);
- for (i=0; i<pq->n_items-1; i++)
- free(pq->buffers[i]); /* free is NULL safe */
+ /* Call exit handler if preset */
+ if (item->exit)
+ item->exit(item->state);
- free(pq);
+ /* Delete an item from list */
+ llist_del(&item->list);
+ talloc_free(item);
+ }
+
+ 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;
-
- if (pq->n_items == MAX_PQ_ITEMS) {
- fprintf(stderr, "[!] Processing Queue cannot handle more than %u items\n",
- MAX_PQ_ITEMS);
- return NULL;
- }
+ struct osmo_gapk_pq_item *item;
- 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
- * \param[in] pq Processing Queue to be prepared
+/*! 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
-pq_prepare(struct pq *pq)
+osmo_gapk_pq_check(struct osmo_gapk_pq *pq, int strict)
{
- int i;
- unsigned int len_prev;
+ 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;
+ }
+ }
- len_prev = 0;
+ /* Save pointer to the previous item */
+ item_prev = item;
+ }
- for (i=0; i<pq->n_items; i++) {
- struct pq_item *item = pq->items[i];
+ 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;
+ }
- 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;
- }
+ 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;
+}
- if (i < (pq->n_items-1)) {
+/*! prepare a processing queue; allocates buffers
+ * \param[in] pq Processing Queue to be prepared
+ * \returns 0 on succcess; negative on error */
+int
+osmo_gapk_pq_prepare(struct osmo_gapk_pq *pq)
+{
+ struct osmo_gapk_pq_item *item;
+
+ /* 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])
+
+ /* Allocate memory for an output buffer */
+ item->buf = talloc_named_const(item, buf_size, ".buffer");
+ if (!item->buf)
return -ENOMEM;
- } else{
- if (item->len_out)
- return -EINVAL;
}
-
- len_prev = item->len_out;
}
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;
-
- 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);
+ struct osmo_gapk_pq_item *item;
+ unsigned int len_prev = 0;
+ uint8_t *buf_prev = NULL;
+ int rv;
+
+ /* 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;
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..cda5c2d
--- /dev/null
+++ b/tests/Makefile.am
@@ -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 $@
diff --git a/test/common.sh b/tests/common.sh
index 591be3a..5fded94 100644
--- a/test/common.sh
+++ b/tests/common.sh
@@ -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"
diff --git a/tests/io/io_sample.txt b/tests/io/io_sample.txt
new file mode 100644
index 0000000..707e019
--- /dev/null
+++ b/tests/io/io_sample.txt
@@ -0,0 +1 @@
+8217C8FB7675A95008F9089D883gapk
diff --git a/tests/io/pq_file_test.c b/tests/io/pq_file_test.c
new file mode 100644
index 0000000..6691db3
--- /dev/null
+++ b/tests/io/pq_file_test.c
@@ -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;
+}
diff --git a/tests/io/pq_file_test.ok b/tests/io/pq_file_test.ok
new file mode 100644
index 0000000..8df71c3
--- /dev/null
+++ b/tests/io/pq_file_test.ok
@@ -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
diff --git a/tests/io/pq_rtp_test.c b/tests/io/pq_rtp_test.c
new file mode 100644
index 0000000..f4070b7
--- /dev/null
+++ b/tests/io/pq_rtp_test.c
@@ -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;
+}
diff --git a/tests/io/pq_rtp_test.ok b/tests/io/pq_rtp_test.ok
new file mode 100644
index 0000000..bae485f
--- /dev/null
+++ b/tests/io/pq_rtp_test.ok
@@ -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
diff --git a/test/play_all_formats.sh b/tests/play_all_formats.sh
index 5cebffb..5cebffb 100755
--- a/test/play_all_formats.sh
+++ b/tests/play_all_formats.sh
diff --git a/tests/procqueue/pq_test.c b/tests/procqueue/pq_test.c
new file mode 100644
index 0000000..198108d
--- /dev/null
+++ b/tests/procqueue/pq_test.c
@@ -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;
+}
diff --git a/tests/procqueue/pq_test.ok b/tests/procqueue/pq_test.ok
new file mode 100644
index 0000000..abdb666
--- /dev/null
+++ b/tests/procqueue/pq_test.ok
@@ -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
diff --git a/test/ref-files/hhgttg_part1_5.s16 b/tests/ref-files/hhgttg_part1_5.s16
index 80a594d..80a594d 100644
--- a/test/ref-files/hhgttg_part1_5.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.amr-efr b/tests/ref-files/hhgttg_part1_5.s16.amr-efr
index 00a11b0..00a11b0 100644
--- a/test/ref-files/hhgttg_part1_5.s16.amr-efr
+++ b/tests/ref-files/hhgttg_part1_5.s16.amr-efr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.amr-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.amr-efr.s16
index 829ed67..829ed67 100644
--- a/test/ref-files/hhgttg_part1_5.s16.amr-efr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.amr-efr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.gsm b/tests/ref-files/hhgttg_part1_5.s16.gsm
index ad3cf8f..ad3cf8f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.gsm
+++ b/tests/ref-files/hhgttg_part1_5.s16.gsm
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.gsm.s16 b/tests/ref-files/hhgttg_part1_5.s16.gsm.s16
index 28aa317..28aa317 100644
--- a/test/ref-files/hhgttg_part1_5.s16.gsm.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.gsm.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-efr b/tests/ref-files/hhgttg_part1_5.s16.racal-efr
index b96be2e..b96be2e 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-efr
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-efr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.racal-efr.s16
index 829ed67..829ed67 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-efr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-efr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-fr b/tests/ref-files/hhgttg_part1_5.s16.racal-fr
index e0a5397..e0a5397 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-fr
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-fr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-fr.s16 b/tests/ref-files/hhgttg_part1_5.s16.racal-fr.s16
index 28aa317..28aa317 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-fr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-fr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-hr b/tests/ref-files/hhgttg_part1_5.s16.racal-hr
index 95881af..95881af 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-hr
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-hr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.racal-hr.s16 b/tests/ref-files/hhgttg_part1_5.s16.racal-hr.s16
index 27dfd5f..27dfd5f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.racal-hr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.racal-hr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-efr b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr
index 9e4615e..9e4615e 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-efr
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr.s16
index 829ed67..829ed67 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-efr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-efr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi
index 9a145d3..9a145d3 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16 b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16
index 27dfd5f..27dfd5f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-etsi.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf
index e956e9d..e956e9d 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16 b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16
index 27dfd5f..27dfd5f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.rtp-hr-ietf.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-efr b/tests/ref-files/hhgttg_part1_5.s16.ti-efr
index 9dd01ae..9dd01ae 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-efr
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-efr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-efr.s16 b/tests/ref-files/hhgttg_part1_5.s16.ti-efr.s16
index e8f759f..e8f759f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-efr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-efr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-fr b/tests/ref-files/hhgttg_part1_5.s16.ti-fr
index 5bf4b83..5bf4b83 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-fr
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-fr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-fr.s16 b/tests/ref-files/hhgttg_part1_5.s16.ti-fr.s16
index 28aa317..28aa317 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-fr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-fr.s16
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-hr b/tests/ref-files/hhgttg_part1_5.s16.ti-hr
index 29a16bf..29a16bf 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-hr
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-hr
Binary files differ
diff --git a/test/ref-files/hhgttg_part1_5.s16.ti-hr.s16 b/tests/ref-files/hhgttg_part1_5.s16.ti-hr.s16
index 27dfd5f..27dfd5f 100644
--- a/test/ref-files/hhgttg_part1_5.s16.ti-hr.s16
+++ b/tests/ref-files/hhgttg_part1_5.s16.ti-hr.s16
Binary files differ
diff --git a/test/test_all_formats.sh b/tests/test_all_formats.sh
index 6da27af..6da27af 100755
--- a/test/test_all_formats.sh
+++ b/tests/test_all_formats.sh
diff --git a/tests/testsuite.at b/tests/testsuite.at
new file mode 100644
index 0000000..7367bc5
--- /dev/null
+++ b/tests/testsuite.at
@@ -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
diff --git a/test/update_ref_files.sh b/tests/update_ref_files.sh
index 1d095ef..1d095ef 100755
--- a/test/update_ref_files.sh
+++ b/tests/update_ref_files.sh