Logo Search packages:      
Sourcecode: alsa-utils version File versions  Download package

alsaloop.c

/*
 *  A simple PCM loopback utility with adaptive sample rate support
 *
 *     Author: Jaroslav Kysela <perex@perex.cz>
 *
 *
 *   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 2 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, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include <alsa/asoundlib.h>
#include <sys/time.h>
#include <math.h>
#include <pthread.h>
#include <syslog.h>
#include <sys/signal.h>
#include "alsaloop.h"

00037 struct loopback_thread {
      int threaded;
      pthread_t thread;
      int exitcode;
      struct loopback **loopbacks;
      int loopbacks_count;
      snd_output_t *output;
};

int quit = 0;
int verbose = 0;
int workarounds = 0;
int daemonize = 0;
int use_syslog = 0;
struct loopback **loopbacks = NULL;
int loopbacks_count = 0;
char **my_argv = NULL;
int my_argc = 0;
struct loopback_thread *threads;
int threads_count = 0;
pthread_t main_job;
int arg_default_xrun = 0;
int arg_default_wake = 0;

static void my_exit(struct loopback_thread *thread, int exitcode)
{
      int i;

      for (i = 0; i < thread->loopbacks_count; i++)
            pcmjob_done(thread->loopbacks[i]);
      if (thread->threaded) {
            thread->exitcode = exitcode;
            pthread_exit(0);
      }
      exit(exitcode);
}

static int create_loopback_handle(struct loopback_handle **_handle,
                          const char *device,
                          const char *ctldev,
                          const char *id)
{
      char idbuf[1024];
      struct loopback_handle *handle;

      handle = calloc(1, sizeof(*handle));
      if (handle == NULL)
            return -ENOMEM;
      if (device == NULL)
            device = "hw:0,0";
      handle->device = strdup(device);
      if (handle->device == NULL)
            return -ENOMEM;
      if (ctldev) {
            handle->ctldev = strdup(ctldev);
            if (handle->ctldev == NULL)
                  return -ENOMEM;
      } else {
            handle->ctldev = NULL;
      }
      snprintf(idbuf, sizeof(idbuf)-1, "%s %s", id, device);
      idbuf[sizeof(idbuf)-1] = '\0';
      handle->id = strdup(idbuf);
      handle->access = SND_PCM_ACCESS_RW_INTERLEAVED;
      handle->format = SND_PCM_FORMAT_S16_LE;
      handle->rate = handle->rate_req = 48000;
      handle->channels = 2;
      handle->resample = 0;
      *_handle = handle;
      return 0;
}

static int create_loopback(struct loopback **_handle,
                     struct loopback_handle *play,
                     struct loopback_handle *capt,
                     snd_output_t *output)
{
      struct loopback *handle;

      handle = calloc(1, sizeof(*handle));
      if (handle == NULL)
            return -ENOMEM;
      handle->play = play;
      handle->capt = capt;
      play->loopback = handle;
      capt->loopback = handle;
      handle->latency_req = 0;
      handle->latency_reqtime = 10000;
      handle->loop_time = ~0UL;
      handle->loop_limit = ~0ULL;
      handle->output = output;
      handle->state = output;
#ifdef USE_SAMPLERATE
      handle->src_enable = 1;
      handle->src_converter_type = SRC_SINC_BEST_QUALITY;
#endif
      *_handle = handle;
      return 0;
}

static void set_loop_time(struct loopback *loop, unsigned long loop_time)
{
      loop->loop_time = loop_time;
      loop->loop_limit = loop->capt->rate * loop_time;
}

static void setscheduler(void)
{
      struct sched_param sched_param;

      if (sched_getparam(0, &sched_param) < 0) {
            logit(LOG_WARNING, "Scheduler getparam failed.\n");
            return;
      }
      sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
      if (!sched_setscheduler(0, SCHED_RR, &sched_param)) {
            if (verbose)
                  logit(LOG_WARNING, "Scheduler set to Round Robin with priority %i\n", sched_param.sched_priority);
            return;
      }
      if (verbose)
            logit(LOG_INFO, "!!!Scheduler set to Round Robin with priority %i FAILED!\n", sched_param.sched_priority);
}

void help(void)
{
      int k;
      printf(
"Usage: alsaloop [OPTION]...\n\n"
"-h,--help      help\n"
"-g,--config    configuration file (one line = one job specified)\n"
"-d,--daemonize daemonize the main process and use syslog for errors\n"
"-P,--pdevice   playback device\n"
"-C,--cdevice   capture device\n"
"-X,--pctl      playback ctl device\n"
"-Y,--cctl      capture ctl device\n"
"-l,--latency   requested latency in frames\n"
"-t,--tlatency  requested latency in usec (1/1000000sec)\n"
"-f,--format    sample format\n"
"-c,--channels  channels\n"
"-r,--rate      rate\n"
"-n,--resample  resample in alsa-lib\n"
"-A,--samplerate use converter (0=sincbest,1=sincmedium,2=sincfastest,\n"
"                               3=zerohold,4=linear)\n"
"-B,--buffer    buffer size in frames\n"
"-E,--period    period size in frames\n"
"-s,--seconds   duration of loop in seconds\n"
"-b,--nblock    non-block mode (very early process wakeup)\n"
"-S,--sync      sync mode(0=none,1=simple,2=captshift,3=playshift,4=samplerate,\n"
"                         5=auto)\n"
"-a,--slave     stream parameters slave mode (0=auto, 1=on, 2=off)\n"
"-T,--thread    thread number (-1 = create unique)\n"
"-m,--mixer redirect mixer, argument is:\n"
"               SRC_SLAVE_ID(PLAYBACK)[@DST_SLAVE_ID(CAPTURE)]\n"
"-O,--ossmixer    rescan and redirect oss mixer, argument is:\n"
"               ALSA_ID@OSS_ID  (for example: \"Master@VOLUME\")\n"
"-e,--effect    apply an effect (bandpass filter sweep)\n"
"-v,--verbose   verbose mode (more -v means more verbose)\n"
"-w,--workaround use workaround (serialopen)\n"
"-U,--xrun      xrun profiling\n"
"-W,--wake      process wake timeout in ms\n"
);
      printf("\nRecognized sample formats are:");
      for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
            const char *s = snd_pcm_format_name(k);
            if (s)
                  printf(" %s", s);
      }
      printf("\n\n");
      printf(
"Tip #1 (usable 500ms latency, good CPU usage, superb xrun prevention):\n"
"  alsaloop -t 500000\n"
"Tip #2 (superb 1ms latency, but heavy CPU usage):\n"
"  alsaloop -t 1000\n"
);
}

static long timediff(struct timeval t1, struct timeval t2)
{
      signed long l;

      t1.tv_sec -= t2.tv_sec;
      l = (signed long) t1.tv_usec - (signed long) t2.tv_usec;
      if (l < 0) {
            t1.tv_sec--;
            l = 1000000 + l;
            l %= 1000000;
      }
      return (t1.tv_sec * 1000000) + l;
}

static void add_loop(struct loopback *loop)
{
      loopbacks = realloc(loopbacks, (loopbacks_count + 1) *
                                    sizeof(struct loopback *));
      if (loopbacks == NULL) {
            logit(LOG_CRIT, "No enough memory\n");
            exit(EXIT_FAILURE);
      }
      loopbacks[loopbacks_count++] = loop;
}

static int init_mixer_control(struct loopback_control *control,
                        char *id)
{
      int err;

      err = snd_ctl_elem_id_malloc(&control->id);
      if (err < 0)
            return err;
      err = snd_ctl_elem_info_malloc(&control->info);
      if (err < 0)
            return err;
      err = snd_ctl_elem_value_malloc(&control->value);
      if (err < 0)
            return err;
      err = control_parse_id(id, control->id);
      if (err < 0)
            return err;
      return 0;
}

static int add_mixers(struct loopback *loop,
                  char **mixers,
                  int mixers_count)
{
      struct loopback_mixer *mixer, *last = NULL;
      char *str1;
      int err;

      while (mixers_count > 0) {
            mixer = calloc(1, sizeof(*mixer));
            if (mixer == NULL)
                  return -ENOMEM;
            if (last)
                  last->next = mixer;
            else
                  loop->controls = mixer;
            last = mixer;
            str1 = strchr(*mixers, '@');
            if (str1)
                  *str1 = '\0';
            err = init_mixer_control(&mixer->src, *mixers);
            if (err < 0) {
                  logit(LOG_CRIT, "Wrong mixer control ID syntax '%s'\n", *mixers);
                  return -EINVAL;
            }
            err = init_mixer_control(&mixer->dst, str1 ? str1 + 1 : *mixers);
            if (err < 0) {
                  logit(LOG_CRIT, "Wrong mixer control ID syntax '%s'\n", str1 ? str1 + 1 : *mixers);
                  return -EINVAL;
            }
            if (str1)
                  *str1 = '@';
            mixers++;
            mixers_count--;
      }
      return 0;
}

static int add_oss_mixers(struct loopback *loop,
                    char **mixers,
                    int mixers_count)
{
      struct loopback_ossmixer *mixer, *last = NULL;
      char *str1, *str2;

      while (mixers_count > 0) {
            mixer = calloc(1, sizeof(*mixer));
            if (mixer == NULL)
                  return -ENOMEM;
            if (last)
                  last->next = mixer;
            else
                  loop->oss_controls = mixer;
            last = mixer;
            str1 = strchr(*mixers, ',');
            if (str1)
                  *str1 = '\0';
            str2 = strchr(str1 ? str1 + 1 : *mixers, '@');
            if (str2)
                  *str2 = '\0';
            mixer->alsa_id = strdup(*mixers);
            if (str1)
                  mixer->alsa_index = atoi(str1);
            mixer->oss_id = strdup(str2 ? str2 + 1 : *mixers);
            if (mixer->alsa_id == NULL || mixer->oss_id == NULL) {
                  logit(LOG_CRIT, "Not enough memory");
                  return -ENOMEM;
            }
            if (str1)
                  *str1 = ',';
            if (str2)
                  *str2 = ',';
            mixers++;
            mixers_count--;
      }
      return 0;
}

static int parse_config_file(const char *file, snd_output_t *output);

static int parse_config(int argc, char *argv[], snd_output_t *output,
                  int cmdline)
{
      struct option long_option[] =
      {
            {"help", 0, NULL, 'h'},
            {"config", 1, NULL, 'g'},
            {"daemonize", 0, NULL, 'd'},
            {"pdevice", 1, NULL, 'P'},
            {"cdevice", 1, NULL, 'C'},
            {"pctl", 1, NULL, 'X'},
            {"cctl", 1, NULL, 'Y'},
            {"latency", 1, NULL, 'l'},
            {"tlatency", 1, NULL, 't'},
            {"format", 1, NULL, 'f'},
            {"channels", 1, NULL, 'c'},
            {"rate", 1, NULL, 'r'},
            {"buffer", 1, NULL, 'B'},
            {"period", 1, NULL, 'E'},
            {"seconds", 1, NULL, 's'},
            {"nblock", 0, NULL, 'b'},
            {"effect", 0, NULL, 'e'},
            {"verbose", 0, NULL, 'v'},
            {"resample", 0, NULL, 'n'},
            {"samplerate", 1, NULL, 'A'},
            {"sync", 1, NULL, 'S'},
            {"slave", 1, NULL, 'a'},
            {"thread", 1, NULL, 'T'},
            {"mixer", 1, NULL, 'm'},
            {"ossmixer", 1, NULL, 'O'},
            {"workaround", 1, NULL, 'w'},
            {"xrun", 0, NULL, 'U'},
            {NULL, 0, NULL, 0},
      };
      int err, morehelp;
      char *arg_config = NULL;
      char *arg_pdevice = NULL;
      char *arg_cdevice = NULL;
      char *arg_pctl = NULL;
      char *arg_cctl = NULL;
      unsigned int arg_latency_req = 0;
      unsigned int arg_latency_reqtime = 10000;
      snd_pcm_format_t arg_format = SND_PCM_FORMAT_S16_LE;
      unsigned int arg_channels = 2;
      unsigned int arg_rate = 48000;
      snd_pcm_uframes_t arg_buffer_size = 0;
      snd_pcm_uframes_t arg_period_size = 0;
      unsigned long arg_loop_time = ~0UL;
      int arg_nblock = 0;
      int arg_effect = 0;
      int arg_resample = 0;
      int arg_samplerate = SRC_SINC_FASTEST + 1;
      int arg_sync = SYNC_TYPE_AUTO;
      int arg_slave = SLAVE_TYPE_AUTO;
      int arg_thread = 0;
      struct loopback *loop = NULL;
      char *arg_mixers[MAX_MIXERS];
      int arg_mixers_count = 0;
      char *arg_ossmixers[MAX_MIXERS];
      int arg_ossmixers_count = 0;
      int arg_xrun = arg_default_xrun;
      int arg_wake = arg_default_wake;

      morehelp = 0;
      while (1) {
            int c;
            if ((c = getopt_long(argc, argv,
                        "hdg:P:C:X:Y:l:t:F:f:c:r:s:benvA:S:a:m:T:O:w:UW:",
                        long_option, NULL)) < 0)
                  break;
            switch (c) {
            case 'h':
                  morehelp++;
                  break;
            case 'g':
                  arg_config = strdup(optarg);
                  break;
            case 'd':
                  daemonize = 1;
                  use_syslog = 1;
                  openlog("alsaloop", LOG_NDELAY|LOG_PID, LOG_DAEMON);
                  break;
            case 'P':
                  arg_pdevice = strdup(optarg);
                  break;
            case 'C':
                  arg_cdevice = strdup(optarg);
                  break;
            case 'X':
                  arg_pctl = strdup(optarg);
                  break;
            case 'Y':
                  arg_cctl = strdup(optarg);
                  break;
            case 'l':
                  err = atoi(optarg);
                  arg_latency_req = err >= 4 ? err : 4;
                  break;
            case 't':
                  err = atoi(optarg);
                  arg_latency_reqtime = err >= 500 ? err : 500;
                  break;
            case 'f':
                  arg_format = snd_pcm_format_value(optarg);
                  if (arg_format == SND_PCM_FORMAT_UNKNOWN) {
                        logit(LOG_WARNING, "Unknown format, setting to default S16_LE\n");
                        arg_format = SND_PCM_FORMAT_S16_LE;
                  }
                  break;
            case 'c':
                  err = atoi(optarg);
                  arg_channels = err >= 1 && err < 1024 ? err : 1;
                  break;
            case 'r':
                  err = atoi(optarg);
                  arg_rate = err >= 4000 && err < 200000 ? err : 44100;
                  break;
            case 'B':
                  err = atoi(optarg);
                  arg_buffer_size = err >= 32 && err < 200000 ? err : 0;
                  break;
            case 'E':
                  err = atoi(optarg);
                  arg_period_size = err >= 32 && err < 200000 ? err : 0;
                  break;
            case 's':
                  err = atoi(optarg);
                  arg_loop_time = err >= 1 && err <= 100000 ? err : 30;
                  break;
            case 'b':
                  arg_nblock = 1;
                  break;
            case 'e':
                  arg_effect = 1;
                  break;
            case 'n':
                  arg_resample = 1;
                  break;
            case 'A':
                  if (strcasecmp(optarg, "sincbest") == 0)
                        arg_samplerate = SRC_SINC_BEST_QUALITY;
                  else if (strcasecmp(optarg, "sincmedium") == 0)
                        arg_samplerate = SRC_SINC_MEDIUM_QUALITY;
                  else if (strcasecmp(optarg, "sincfastest") == 0)
                        arg_samplerate = SRC_SINC_FASTEST;
                  else if (strcasecmp(optarg, "zerohold") == 0)
                        arg_samplerate = SRC_ZERO_ORDER_HOLD;
                  else if (strcasecmp(optarg, "linear") == 0)
                        arg_samplerate = SRC_LINEAR;
                  else
                        arg_samplerate = atoi(optarg);
                  if (arg_samplerate < 0 || arg_samplerate > SRC_LINEAR)
                        arg_sync = SRC_SINC_FASTEST;
                  arg_samplerate += 1;
                  break;
            case 'S':
                  if (strcasecmp(optarg, "samplerate") == 0)
                        arg_sync = SYNC_TYPE_SAMPLERATE;
                  else if (optarg[0] == 'n')
                        arg_sync = SYNC_TYPE_NONE;
                  else if (optarg[0] == 's')
                        arg_sync = SYNC_TYPE_SIMPLE;
                  else if (optarg[0] == 'c')
                        arg_sync = SYNC_TYPE_CAPTRATESHIFT;
                  else if (optarg[0] == 'p')
                        arg_sync = SYNC_TYPE_PLAYRATESHIFT;
                  else if (optarg[0] == 'r')
                        arg_sync = SYNC_TYPE_SAMPLERATE;
                  else
                        arg_sync = atoi(optarg);
                  if (arg_sync < 0 || arg_sync > SYNC_TYPE_LAST)
                        arg_sync = SYNC_TYPE_AUTO;
                  break;
            case 'a':
                  if (optarg[0] == 'a')
                        arg_slave = SLAVE_TYPE_AUTO;
                  else if (strcasecmp(optarg, "on") == 0)
                        arg_slave = SLAVE_TYPE_ON;
                  else if (strcasecmp(optarg, "off") == 0)
                        arg_slave = SLAVE_TYPE_OFF;
                  else
                        arg_slave = atoi(optarg);
                  if (arg_slave < 0 || arg_slave > SLAVE_TYPE_LAST)
                        arg_slave = SLAVE_TYPE_AUTO;
                  break;
            case 'T':
                  arg_thread = atoi(optarg);
                  if (arg_thread < 0)
                        arg_thread = 10000000 + loopbacks_count;
                  break;
            case 'm':
                  if (arg_mixers_count >= MAX_MIXERS) {
                        logit(LOG_CRIT, "Maximum redirected mixer controls reached (max %i)\n", (int)MAX_MIXERS);
                        exit(EXIT_FAILURE);
                  }
                  arg_mixers[arg_mixers_count++] = optarg;
                  break;
            case 'O':
                  if (arg_ossmixers_count >= MAX_MIXERS) {
                        logit(LOG_CRIT, "Maximum redirected mixer controls reached (max %i)\n", (int)MAX_MIXERS);
                        exit(EXIT_FAILURE);
                  }
                  arg_ossmixers[arg_ossmixers_count++] = optarg;
                  break;
            case 'v':
                  verbose++;
                  break;
            case 'w':
                  if (strcasecmp(optarg, "serialopen") == 0)
                        workarounds |= WORKAROUND_SERIALOPEN;
                  break;
            case 'U':
                  arg_xrun = 1;
                  if (cmdline)
                        arg_default_xrun = 1;
                  break;
            case 'W':
                  arg_wake = atoi(optarg);
                  if (cmdline)
                        arg_default_wake = arg_wake;
                  break;
            }
      }

      if (morehelp) {
            help();
            exit(EXIT_SUCCESS);
      }
      if (arg_config == NULL) {
            struct loopback_handle *play;
            struct loopback_handle *capt;
            err = create_loopback_handle(&play, arg_pdevice, arg_pctl, "playback");
            if (err < 0) {
                  logit(LOG_CRIT, "Unable to create playback handle.\n");
                  exit(EXIT_FAILURE);
            }
            err = create_loopback_handle(&capt, arg_cdevice, arg_cctl, "capture");
            if (err < 0) {
                  logit(LOG_CRIT, "Unable to create capture handle.\n");
                  exit(EXIT_FAILURE);
            }
            err = create_loopback(&loop, play, capt, output);
            if (err < 0) {
                  logit(LOG_CRIT, "Unable to create loopback handle.\n");
                  exit(EXIT_FAILURE);
            }
            play->format = capt->format = arg_format;
            play->rate = play->rate_req = capt->rate = capt->rate_req = arg_rate;
            play->channels = capt->channels = arg_channels;
            play->buffer_size_req = capt->buffer_size_req = arg_buffer_size;
            play->period_size_req = capt->period_size_req = arg_period_size;
            play->resample = capt->resample = arg_resample;
            play->nblock = capt->nblock = arg_nblock ? 1 : 0;
            loop->latency_req = arg_latency_req;
            loop->latency_reqtime = arg_latency_reqtime;
            loop->sync = arg_sync;
            loop->slave = arg_slave;
            loop->thread = arg_thread;
            loop->xrun = arg_xrun;
            loop->wake = arg_wake;
            err = add_mixers(loop, arg_mixers, arg_mixers_count);
            if (err < 0) {
                  logit(LOG_CRIT, "Unable to add mixer controls.\n");
                  exit(EXIT_FAILURE);
            }
            err = add_oss_mixers(loop, arg_ossmixers, arg_ossmixers_count);
            if (err < 0) {
                  logit(LOG_CRIT, "Unable to add ossmixer controls.\n");
                  exit(EXIT_FAILURE);
            }
#ifdef USE_SAMPLERATE
            loop->src_enable = arg_samplerate > 0;
            if (loop->src_enable)
                  loop->src_converter_type = arg_samplerate - 1;
#else
            if (arg_samplerate > 0) {
                  logit(LOG_CRIT, "No libsamplerate support.\n");
                  exit(EXIT_FAILURE);
            }
#endif
            set_loop_time(loop, arg_loop_time);
            add_loop(loop);
            return 0;
      }

      return parse_config_file(arg_config, output);
}

static int parse_config_file(const char *file, snd_output_t *output)
{
      FILE *fp;
      char line[2048], word[2048];
      char *str, *ptr;
      int argc, c, err = 0;
      char **argv;

      fp = fopen(file, "r");
      if (fp == NULL) {
            logit(LOG_CRIT, "Unable to open file '%s': %s\n", file, strerror(errno));
            return -EIO;
      }
      while (!feof(fp)) {
            if (fgets(line, sizeof(line)-1, fp) == NULL)
                  break;
            line[sizeof(line)-1] = '\0';
            my_argv = realloc(my_argv, my_argc + MAX_ARGS * sizeof(char *));
            if (my_argv == NULL)
                  return -ENOMEM;
            argv = my_argv + my_argc;
            argc = 0;
            argv[argc++] = strdup("<prog>");
            my_argc++;
            str = line;
            while (*str) {
                  ptr = word;
                  while (*str && (*str == ' ' || *str < ' '))
                        str++;
                  if (*str == '#')
                        goto __next;
                  if (*str == '\'' || *str == '\"') {
                        c = *str++;
                        while (*str && *str != c)
                              *ptr++ = *str++;
                        if (*str == c)
                              str++;
                  } else {
                        while (*str && *str != ' ' && *str != '\t')
                              *ptr++ = *str++;
                  }
                  if (ptr != word) {
                        if (*(ptr-1) == '\n')
                              ptr--;
                        *ptr = '\0';
                        if (argc >= MAX_ARGS) {
                              logit(LOG_CRIT, "Too many arguments.");
                              goto __error;
                        }
                        argv[argc++] = strdup(word);
                        my_argc++;
                  }
            }
            /* erase runtime variables for getopt */
            optarg = NULL;
            optind = opterr = 1;
            optopt = '?';

            err = parse_config(argc, argv, output, 0);
            __next:
            if (err < 0)
                  break;
            err = 0;
      }
      __error:
      fclose(fp);

      return err;
}

static void thread_job1(void *_data)
{
      struct loopback_thread *thread = _data;
      snd_output_t *output = thread->output;
      struct pollfd *pfds = NULL;
      int pfds_count = 0;
      int i, j, err, wake = 1000000;

      setscheduler();

      for (i = 0; i < thread->loopbacks_count; i++) {
            err = pcmjob_init(thread->loopbacks[i]);
            if (err < 0) {
                  logit(LOG_CRIT, "Loopback initialization failure.\n");
                  my_exit(thread, EXIT_FAILURE);
            }
      }
      for (i = 0; i < thread->loopbacks_count; i++) {
            err = pcmjob_start(thread->loopbacks[i]);
            if (err < 0) {
                  logit(LOG_CRIT, "Loopback start failure.\n");
                  my_exit(thread, EXIT_FAILURE);
            }
            pfds_count += thread->loopbacks[i]->pollfd_count;
            j = thread->loopbacks[i]->wake;
            if (j > 0 && j < wake)
                  wake = j;
      }
      if (wake >= 1000000)
            wake = -1;
      pfds = calloc(pfds_count, sizeof(struct pollfd));
      if (pfds == NULL || pfds_count <= 0) {
            logit(LOG_CRIT, "Poll FDs allocation failed.\n");
            my_exit(thread, EXIT_FAILURE);
      }
      while (!quit) {
            struct timeval tv1, tv2;
            for (i = j = 0; i < thread->loopbacks_count; i++) {
                  err = pcmjob_pollfds_init(thread->loopbacks[i], &pfds[j]);
                  if (err < 0) {
                        logit(LOG_CRIT, "Poll FD initialization failed.\n");
                        my_exit(thread, EXIT_FAILURE);
                  }
                  j += err;
            }
            if (verbose > 10)
                  gettimeofday(&tv1, NULL);
            err = poll(pfds, j, wake);
            if (err < 0)
                  err = -errno;
            if (verbose > 10) {
                  gettimeofday(&tv2, NULL);
                  snd_output_printf(output, "pool took %lius\n", timediff(tv2, tv1));
            }
            if (err < 0) {
                  if (err == -EINTR || err == -ERESTART)
                        continue;
                  logit(LOG_CRIT, "Poll failed: %s\n", strerror(-err));
                  my_exit(thread, EXIT_FAILURE);
            }
            for (i = j = 0; i < thread->loopbacks_count; i++) {
                  struct loopback *loop = thread->loopbacks[i];
                  if (j < loop->active_pollfd_count) {
                        err = pcmjob_pollfds_handle(loop, &pfds[j]);
                        if (err < 0) {
                              logit(LOG_CRIT, "pcmjob failed.\n");
                              exit(EXIT_FAILURE);
                        }
                  }
                  j += loop->active_pollfd_count;
            }
      }

      my_exit(thread, EXIT_SUCCESS);
}

static void thread_job(struct loopback_thread *thread)
{
      if (!thread->threaded) {
            thread_job1(thread);
            return;
      }
      pthread_create(&thread->thread, NULL, (void *) &thread_job1,
                                    (void *) thread);
}

static void send_to_all(int sig)
{
      struct loopback_thread *thread;
      int i;

      for (i = 0; i < threads_count; i++) {
            thread = &threads[i];
            if (thread->threaded)
                  pthread_kill(thread->thread, sig);
      }
}

static void signal_handler(int sig)
{
      quit = 1;
      send_to_all(SIGUSR2);
}

static void signal_handler_state(int sig)
{
      pthread_t self = pthread_self();
      struct loopback_thread *thread;
      int i, j;

      if (pthread_equal(main_job, self))
            send_to_all(SIGUSR1);
      for (i = 0; i < threads_count; i++) {
            thread = &threads[i];
            if (thread->thread == self) {
                  for (j = 0; j < thread->loopbacks_count; j++)
                        pcmjob_state(thread->loopbacks[j]);
            }
      }
      signal(sig, signal_handler_state);
}

static void signal_handler_ignore(int sig)
{
      signal(sig, signal_handler_ignore);
}

int main(int argc, char *argv[])
{
      snd_output_t *output;
      int i, j, k, l, err;

      err = snd_output_stdio_attach(&output, stdout, 0);
      if (err < 0) {
            logit(LOG_CRIT, "Output failed: %s\n", snd_strerror(err));
            exit(EXIT_FAILURE);
      }
      err = parse_config(argc, argv, output, 1);
      if (err < 0) {
            logit(LOG_CRIT, "Unable to parse arguments or configuration...\n");
            exit(EXIT_FAILURE);
      }
      while (my_argc > 0)
            free(my_argv[--my_argc]);
      free(my_argv);

      if (loopbacks_count <= 0) {
            logit(LOG_CRIT, "No loopback defined...\n");
            exit(EXIT_FAILURE);
      }

      if (daemonize) {
            if (daemon(0, 0) < 0) {
                  logit(LOG_CRIT, "daemon() failed: %s\n", strerror(errno));
                  exit(EXIT_FAILURE);
            }
            i = fork();
            if (i < 0) {
                  logit(LOG_CRIT, "fork() failed: %s\n", strerror(errno));
                  exit(EXIT_FAILURE);
            }
            if (i > 0) {
                  /* wait(&i); */
                  exit(EXIT_SUCCESS);
            }
      }

      /* we must sort thread IDs */
      j = -1;
      do {
            k = 0x7fffffff;
            for (i = 0; i < loopbacks_count; i++) {
                  if (loopbacks[i]->thread < k &&
                      loopbacks[i]->thread > j)
                        k = loopbacks[i]->thread;
            }
            j++;
            for (i = 0; i < loopbacks_count; i++) {
                  if (loopbacks[i]->thread == k)
                        loopbacks[i]->thread = j;
            }
      } while (k != 0x7fffffff);
      /* fix maximum thread id */
      for (i = 0, j = -1; i < loopbacks_count; i++) {
            if (loopbacks[i]->thread > j)
                  j = loopbacks[i]->thread;
      }
      j += 1;
      threads = calloc(1, sizeof(struct loopback_thread) * j);
      if (threads == NULL) {
            logit(LOG_CRIT, "No enough memory\n");
            exit(EXIT_FAILURE);
      }
      /* sort all threads */
      for (k = 0; k < j; k++) {
            for (i = l = 0; i < loopbacks_count; i++)
                  if (loopbacks[i]->thread == k)
                        l++;
            threads[k].loopbacks = malloc(l * sizeof(struct loopback *));
            threads[k].loopbacks_count = l;
            threads[k].output = output;
            threads[k].threaded = j > 1;
            for (i = l = 0; i < loopbacks_count; i++)
                  if (loopbacks[i]->thread == k)
                        threads[k].loopbacks[l++] = loopbacks[i];
      }
      threads_count = j;
      main_job = pthread_self();
 
      signal(SIGINT, signal_handler);
      signal(SIGTERM, signal_handler);
      signal(SIGABRT, signal_handler);
      signal(SIGUSR1, signal_handler_state);
      signal(SIGUSR2, signal_handler_ignore);

      for (k = 0; k < threads_count; k++)
            thread_job(&threads[k]);

      if (j > 1) {
            for (k = 0; k < threads_count; k++)
                  pthread_join(threads[k].thread, NULL);
      }

      if (use_syslog)
            closelog();
      exit(EXIT_SUCCESS);
}

Generated by  Doxygen 1.6.0   Back to index