/* osmo-cc-alsa-endpoint main * * (C) 2020 by Andreas Eversberg * 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 . */ #include #include #include #include #include #include #include #include #include #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:, [] [dialing]\n", app); } static void print_help() { /* - - */ printf(" -h --help\n"); printf(" This help\n"); printf(" --config [~/]\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 | ,[,[,...]] | 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 \n"); printf(" Give name of this interface. It will be sent in each call towards\n"); printf(" -I --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:,\n"); printf(" Sound card and device number (default = '%s')\n", audiodev); printf(" -s --samplerate \n"); printf(" Sample rate of sound device (default = '%d')\n", samplerate); printf(" -b --buffer \n"); printf(" How many milliseconds are processed in advance (default = '%d')\n", latency); printf(" -r --realtime \n"); printf(" Set prio: 0 to disable, 99 for maximum (default = %d)\n", rt_prio); printf(" -C --cc \"\" [--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; }