327 lines
8.4 KiB
C
327 lines
8.4 KiB
C
/* osmo-cc-alsa-endpoint main
|
|
*
|
|
* (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
|
|
* All Rights Reserved
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <termios.h>
|
|
#include <sched.h>
|
|
#include "../libdebug/debug.h"
|
|
#include "../liboptions/options.h"
|
|
#include "../libg711/g711.h"
|
|
#include "telephone.h"
|
|
|
|
telephone_t *telephone_ep = NULL;
|
|
int num_kanal = 1;
|
|
|
|
static const char *name = "alsa";
|
|
static const char *audiodev = NULL;
|
|
static int samplerate = 48000;
|
|
static int latency = 50;
|
|
static int rt_prio = 1;
|
|
static const char *caller_id = "";
|
|
static int early_audio = 0;
|
|
static int autoalert = 0, autoanswer = 1;
|
|
#define MAX_CC_ARGS 1024
|
|
static int cc_argc = 0;
|
|
static const char *cc_argv[MAX_CC_ARGS];
|
|
|
|
static void print_usage(const char *app)
|
|
{
|
|
printf("Usage: %s -a hw:<card>,<device> [<options>] [dialing]\n", app);
|
|
}
|
|
|
|
static void print_help()
|
|
{
|
|
/* - - */
|
|
printf(" -h --help\n");
|
|
printf(" This help\n");
|
|
printf(" --config [~/]<path to config file>\n");
|
|
printf(" Give a config file to use. If it starts with '~/', path is at home dir.\n");
|
|
printf(" Each line in config file is one option, '-' or '--' must not be given!\n");
|
|
printf(" -v --verbose <level> | <level>,<category>[,<category>[,...]] | list\n");
|
|
printf(" Use 'list' to get a list of all levels and categories\n");
|
|
printf(" Verbose level: digit of debug level (default = '%d')\n", debuglevel);
|
|
printf(" Verbose level+category: level digit followed by one or more categories\n");
|
|
printf(" -> If no category is specified, all categories are selected\n");
|
|
printf(" -n --name <interface name>\n");
|
|
printf(" Give name of this interface. It will be sent in each call towards\n");
|
|
printf(" -I --caller-id <caller id>\n");
|
|
printf(" What caller ID to send on calls made from terminal. (default = '%s')\n", caller_id);
|
|
printf(" -A --auto alerting | answer | off\n");
|
|
printf(" An incoming call can be responded automatically with an alerting or an\n");
|
|
printf(" answer, or not be responded automatically. (default = 'answer')\n");
|
|
printf(" -E --early-audio\n");
|
|
printf(" Send early audio when call is not yet connected. Must be used in\n");
|
|
printf(" conjunction with --auto\n");
|
|
printf(" -a --audio-device hw:<card>,<device>\n");
|
|
printf(" Sound card and device number (default = '%s')\n", audiodev);
|
|
printf(" -s --samplerate <rate>\n");
|
|
printf(" Sample rate of sound device (default = '%d')\n", samplerate);
|
|
printf(" -b --buffer <ms>\n");
|
|
printf(" How many milliseconds are processed in advance (default = '%d')\n", latency);
|
|
printf(" -r --realtime <prio>\n");
|
|
printf(" Set prio: 0 to disable, 99 for maximum (default = %d)\n", rt_prio);
|
|
printf(" -C --cc \"<osmo-cc arg>\" [--cc ...]\n");
|
|
printf(" Pass arguments to Osmo-CC endpoint. Use '-cc help' for description.\n");
|
|
}
|
|
|
|
static void add_options(void)
|
|
{
|
|
option_add('h', "help", 0);
|
|
option_add('v', "verbose", 1);
|
|
option_add('n', "name", 1);
|
|
option_add('I', "caller-id", 1);
|
|
option_add('A', "auto", 1);
|
|
option_add('E', "early-audio", 0);
|
|
option_add('a', "audio-device", 1);
|
|
option_add('s', "samplerate", 1);
|
|
option_add('b', "buffer", 1);
|
|
option_add('r', "realtime", 1);
|
|
option_add('C', "cc", 1);
|
|
}
|
|
|
|
static int handle_options(int short_option, int argi, char **argv)
|
|
{
|
|
int rc;
|
|
|
|
switch (short_option) {
|
|
case 'h':
|
|
print_usage(argv[0]);
|
|
print_help();
|
|
return 0;
|
|
case 'v':
|
|
if (!strcasecmp(argv[argi], "list")) {
|
|
debug_list_cat();
|
|
return 0;
|
|
}
|
|
rc = parse_debug_opt(argv[argi]);
|
|
if (rc < 0) {
|
|
fprintf(stderr, "Failed to parse debug option, please use -h for help.\n");
|
|
return rc;
|
|
}
|
|
break;
|
|
case 'n':
|
|
name = options_strdup(argv[argi]);
|
|
break;
|
|
case 'I':
|
|
caller_id = options_strdup(argv[argi]);
|
|
break;
|
|
case 'A':
|
|
if (!strcasecmp(argv[argi], "alerting")) {
|
|
autoalert = 1;
|
|
autoanswer = 0;
|
|
} else
|
|
if (!strcasecmp(argv[argi], "answer")) {
|
|
autoalert = 0;
|
|
autoanswer = 1;
|
|
} else
|
|
if (!strcasecmp(argv[argi], "off")) {
|
|
autoalert = 0;
|
|
autoanswer = 0;
|
|
} else {
|
|
fprintf(stderr, "Unknown parameter '%s', please use -h for help.\n", argv[argi]);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
case 'E':
|
|
early_audio = 1;
|
|
break;
|
|
case 'a':
|
|
audiodev = options_strdup(argv[argi]);
|
|
break;
|
|
case 's':
|
|
samplerate = atoi(argv[argi]);
|
|
break;
|
|
case 'b':
|
|
latency = atoi(argv[argi]);
|
|
break;
|
|
case 'r':
|
|
rt_prio = atoi(argv[argi]);
|
|
break;
|
|
case 'C':
|
|
if (!strcasecmp(argv[argi], "help")) {
|
|
osmo_cc_help();
|
|
return 0;
|
|
}
|
|
if (cc_argc == MAX_CC_ARGS) {
|
|
fprintf(stderr, "Too many osmo-cc args!\n");
|
|
break;
|
|
}
|
|
cc_argv[cc_argc++] = options_strdup(argv[argi]);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int quit = 0;
|
|
void sighandler(int sigset)
|
|
{
|
|
if (sigset == SIGHUP || sigset == SIGPIPE)
|
|
return;
|
|
|
|
fprintf(stderr, "\nSignal %d received.\n", sigset);
|
|
|
|
quit = 1;
|
|
}
|
|
|
|
static int get_char()
|
|
{
|
|
struct timeval tv = {0, 0};
|
|
fd_set fds;
|
|
char c = 0;
|
|
int __attribute__((__unused__)) rc;
|
|
|
|
FD_ZERO(&fds);
|
|
FD_SET(0, &fds);
|
|
select(0+1, &fds, NULL, NULL, &tv);
|
|
if (FD_ISSET(0, &fds)) {
|
|
rc = read(0, &c, 1);
|
|
return c;
|
|
} else
|
|
return -1;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int argi, rc;
|
|
const char *dialing = "";
|
|
struct termios term, term_orig;
|
|
char c;
|
|
|
|
g711_init();
|
|
|
|
telephone_ep = telephone_create();
|
|
if (!telephone_ep)
|
|
goto error;
|
|
|
|
cc_argv[cc_argc++] = options_strdup("remote auto");
|
|
|
|
/* handle options / config file */
|
|
add_options();
|
|
rc = options_config_file(argc, argv, "~/.osmocom/alsa/alsa.conf", handle_options);
|
|
if (rc < 0)
|
|
return 0;
|
|
argi = options_command_line(argc, argv, handle_options);
|
|
if (argi <= 0)
|
|
return argi;
|
|
|
|
if (argi < argc)
|
|
dialing = argv[argi];
|
|
|
|
rc = ui_init(dialing, autoalert, autoanswer);
|
|
if (rc) {
|
|
PDEBUG(DTEL, DEBUG_ERROR, "UI initializing failed!\n");
|
|
goto error;
|
|
}
|
|
|
|
rc = telephone_init(telephone_ep, name, caller_id, OSMO_CC_LOCATION_USER, early_audio, audiodev, samplerate, samplerate * latency / 1000);
|
|
if (rc) {
|
|
PDEBUG(DTEL, DEBUG_ERROR, "Endpoint initializing failed!\n");
|
|
goto error;
|
|
}
|
|
|
|
rc = osmo_cc_new(&telephone_ep->cc_ep, OSMO_CC_VERSION, name, OSMO_CC_LOCATION_USER, cc_message, NULL, telephone_ep, cc_argc, cc_argv);
|
|
if (rc < 0)
|
|
goto error;
|
|
|
|
/* real time priority */
|
|
if (rt_prio > 0) {
|
|
struct sched_param schedp;
|
|
int rc;
|
|
|
|
memset(&schedp, 0, sizeof(schedp));
|
|
schedp.sched_priority = rt_prio;
|
|
rc = sched_setscheduler(0, SCHED_RR, &schedp);
|
|
if (rc)
|
|
fprintf(stderr, "Error setting SCHED_RR with prio %d\n", rt_prio);
|
|
}
|
|
|
|
/* prepare terminal */
|
|
tcgetattr(0, &term_orig);
|
|
term = term_orig;
|
|
term.c_lflag &= ~(ISIG|ICANON|ECHO);
|
|
term.c_cc[VMIN]=1;
|
|
term.c_cc[VTIME]=2;
|
|
tcsetattr(0, TCSANOW, &term);
|
|
|
|
signal(SIGINT, sighandler);
|
|
signal(SIGHUP, sighandler);
|
|
signal(SIGTERM, sighandler);
|
|
signal(SIGPIPE, sighandler);
|
|
|
|
while (!quit) {
|
|
int w;
|
|
c = get_char();
|
|
if (c == 3) {
|
|
/* quit */
|
|
if (clear_console_text)
|
|
clear_console_text();
|
|
printf("CTRL+c received, quitting!\n");
|
|
quit = 1;
|
|
continue;
|
|
}
|
|
process_timer();
|
|
alsa_work(telephone_ep);
|
|
rtp_work(telephone_ep);
|
|
do {
|
|
w = 0;
|
|
w |= osmo_cc_handle();
|
|
w |= ui_work(telephone_ep, c);
|
|
c = 0;
|
|
} while (w);
|
|
usleep(1000);
|
|
}
|
|
|
|
signal(SIGINT, SIG_DFL);
|
|
signal(SIGTSTP, SIG_DFL);
|
|
signal(SIGHUP, SIG_DFL);
|
|
signal(SIGTERM, SIG_DFL);
|
|
signal(SIGPIPE, SIG_DFL);
|
|
|
|
/* reset terminal */
|
|
tcsetattr(0, TCSANOW, &term_orig);
|
|
|
|
/* reset real time prio */
|
|
if (rt_prio > 0) {
|
|
struct sched_param schedp;
|
|
|
|
memset(&schedp, 0, sizeof(schedp));
|
|
schedp.sched_priority = 0;
|
|
sched_setscheduler(0, SCHED_OTHER, &schedp);
|
|
}
|
|
|
|
error:
|
|
if (telephone_ep) {
|
|
osmo_cc_delete(&telephone_ep->cc_ep);
|
|
telephone_destroy(telephone_ep);
|
|
}
|
|
|
|
options_free();
|
|
|
|
return 0;
|
|
}
|
|
|