diff options
| -rw-r--r-- | src/modules/alsa/alsa-sink.c | 2 | ||||
| -rw-r--r-- | src/modules/alsa/alsa-source.c | 2 | ||||
| -rw-r--r-- | src/modules/alsa/alsa-util.c | 13 | ||||
| -rw-r--r-- | src/modules/alsa/alsa-util.h | 2 | ||||
| -rw-r--r-- | src/modules/alsa/module-alsa-sink.c | 4 | ||||
| -rw-r--r-- | src/modules/alsa/module-alsa-source.c | 4 | ||||
| -rw-r--r-- | src/modules/module-solaris.c | 101 | ||||
| -rw-r--r-- | src/pulse/introspect.h | 14 | ||||
| -rw-r--r-- | src/pulsecore/core-util.c | 34 | 
9 files changed, 107 insertions, 69 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 0296f64e..0dc0e2b3 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1644,7 +1644,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca      /* ALSA might tweak the sample spec, so recalculate the frame size */      frame_size = pa_frame_size(&ss); -    pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem); +    pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL));      pa_sink_new_data_init(&data);      data.driver = driver; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index ef365a21..348cd082 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1496,7 +1496,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p      /* ALSA might tweak the sample spec, so recalculate the frame size */      frame_size = pa_frame_size(&ss); -    pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem); +    pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL));      pa_source_new_data_init(&data);      data.driver = driver; diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 2d0ca107..5b5270b8 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -1094,7 +1094,8 @@ success:  int pa_alsa_find_mixer_and_elem(          snd_pcm_t *pcm,          snd_mixer_t **_m, -        snd_mixer_elem_t **_e) { +        snd_mixer_elem_t **_e, +        const char *control_name) {      int err;      snd_mixer_t *m; @@ -1146,11 +1147,17 @@ int pa_alsa_find_mixer_and_elem(      switch (snd_pcm_stream(pcm)) {          case SND_PCM_STREAM_PLAYBACK: -            e = pa_alsa_find_elem(m, "Master", "PCM", TRUE); +            if (control_name) +                e = pa_alsa_find_elem(m, control_name, NULL, TRUE); +            else +                e = pa_alsa_find_elem(m, "Master", "PCM", TRUE);              break;          case SND_PCM_STREAM_CAPTURE: -            e = pa_alsa_find_elem(m, "Capture", "Mic", FALSE); +            if (control_name) +                e = pa_alsa_find_elem(m, control_name, NULL, FALSE); +            else +                e = pa_alsa_find_elem(m, "Capture", "Mic", FALSE);              break;          default: diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 68496d51..5cad2958 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -54,7 +54,7 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);  int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);  snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback); -int pa_alsa_find_mixer_and_elem(snd_pcm_t *pcm, snd_mixer_t **_m, snd_mixer_elem_t **_e); +int pa_alsa_find_mixer_and_elem(snd_pcm_t *pcm, snd_mixer_t **_m, snd_mixer_elem_t **_e, const char *control_name);  typedef struct pa_alsa_profile_info {      pa_channel_map map; diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c index c728a446..8e600ab8 100644 --- a/src/modules/alsa/module-alsa-sink.c +++ b/src/modules/alsa/module-alsa-sink.c @@ -52,7 +52,8 @@ PA_MODULE_USAGE(          "tsched=<enable system timer based scheduling mode?> "          "tsched_buffer_size=<buffer size when using timer based scheduling> "          "tsched_buffer_watermark=<lower fill watermark> " -        "ignore_dB=<ignore dB information from the device?>"); +        "ignore_dB=<ignore dB information from the device?> " +        "control=<name of mixer control>");  static const char* const valid_modargs[] = {      "name", @@ -70,6 +71,7 @@ static const char* const valid_modargs[] = {      "tsched_buffer_size",      "tsched_buffer_watermark",      "ignore_dB", +    "control",      NULL  }; diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c index 6188019f..e6b27b3d 100644 --- a/src/modules/alsa/module-alsa-source.c +++ b/src/modules/alsa/module-alsa-source.c @@ -76,7 +76,8 @@ PA_MODULE_USAGE(          "tsched=<enable system timer based scheduling mode?> "          "tsched_buffer_size=<buffer size when using timer based scheduling> "          "tsched_buffer_watermark=<upper fill watermark> " -        "ignore_dB=<ignore dB information from the device?>"); +        "ignore_dB=<ignore dB information from the device?> " +        "control=<name of mixer control>");  static const char* const valid_modargs[] = {      "name", @@ -94,6 +95,7 @@ static const char* const valid_modargs[] = {      "tsched_buffer_size",      "tsched_buffer_watermark",      "ignore_dB", +    "control",      NULL  }; diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index e7dfc05e..ecd3ba32 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -75,7 +75,7 @@ PA_MODULE_USAGE(      "format=<sample format> "      "channels=<number of channels> "      "rate=<sample rate> " -    "buffer_size=<record buffer size> " +    "buffer_length=<milliseconds> "      "channel_map=<channel map>");  PA_MODULE_LOAD_ONCE(FALSE); @@ -94,8 +94,7 @@ struct userdata {      uint32_t frame_size;      int32_t buffer_size; -    volatile uint64_t written_bytes, read_bytes; -    pa_mutex *written_bytes_lock; +    uint64_t written_bytes, read_bytes;      char *device_name;      int mode; @@ -107,9 +106,8 @@ struct userdata {      uint32_t play_samples_msw, record_samples_msw;      uint32_t prev_playback_samples, prev_record_samples; -    pa_mutex *sample_counter_lock; -    size_t min_request; +    int32_t minimum_request;  };  static const char* const valid_modargs[] = { @@ -118,7 +116,7 @@ static const char* const valid_modargs[] = {      "device",      "record",      "playback", -    "buffer_size", +    "buffer_length",      "format",      "rate",      "channels", @@ -127,13 +125,9 @@ static const char* const valid_modargs[] = {  };  #define DEFAULT_DEVICE "/dev/audio" -#define MIN_BUFFER_SIZE (640) -#define MAX_RENDER_HZ   (300) -/* This render rate limit implies a minimum latency,  but without it we waste too much CPU time in the - * IO thread. The maximum render rate and minimum latency (or minimum buffer size) are unachievable on - * common hardware anyway. Note that MIN_BUFFER_SIZE * MAX_RENDER_HZ >= 4 * 48000 Bps. - */ +#define MAX_RENDER_HZ   (300) +/* This render rate limit imposes a minimum latency, but without it we waste too much CPU time. */  static uint64_t get_playback_buffered_bytes(struct userdata *u) {      audio_info_t info; @@ -142,8 +136,6 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {      pa_assert(u->sink); -    pa_mutex_lock(u->sample_counter_lock); -      err = ioctl(u->fd, AUDIO_GETINFO, &info);      pa_assert(err >= 0); @@ -159,8 +151,6 @@ static uint64_t get_playback_buffered_bytes(struct userdata *u) {      u->prev_playback_samples = info.play.samples;      played_bytes = (((uint64_t)u->play_samples_msw << 32) + info.play.samples) * u->frame_size; -    pa_mutex_unlock(u->sample_counter_lock); -      return u->written_bytes - played_bytes;  } @@ -171,11 +161,9 @@ static pa_usec_t sink_get_latency(struct userdata *u, pa_sample_spec *ss) {      pa_assert(ss);      if (u->fd >= 0) { -        pa_mutex_lock(u->written_bytes_lock);          r = pa_bytes_to_usec(get_playback_buffered_bytes(u), ss);          if (u->memchunk.memblock)              r += pa_bytes_to_usec(u->memchunk.length, ss); -        pa_mutex_unlock(u->written_bytes_lock);      }      return r;  } @@ -487,7 +475,7 @@ static void sink_set_volume(pa_sink *s) {      if (u->fd >= 0) {          AUDIO_INITINFO(&info); -        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; +        info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;          assert(info.play.gain <= AUDIO_MAX_GAIN);          if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) { @@ -523,7 +511,7 @@ static void source_set_volume(pa_source *s) {      if (u->fd >= 0) {          AUDIO_INITINFO(&info); -        info.play.gain = pa_cvolume_avg(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM; +        info.play.gain = pa_cvolume_max(&s->virtual_volume) * AUDIO_MAX_GAIN / PA_VOLUME_NORM;          assert(info.play.gain <= AUDIO_MAX_GAIN);          if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) { @@ -580,6 +568,25 @@ static void sink_get_mute(pa_sink *s) {      }  } +static void process_rewind(struct userdata *u) { +    size_t rewind_nbytes; + +    pa_assert(u); + +    /* Figure out how much we shall rewind and reset the counter */ +    rewind_nbytes = u->sink->thread_info.rewind_nbytes; +    u->sink->thread_info.rewind_nbytes = 0; + +    if (rewind_nbytes > 0) { +        pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); +        rewind_nbytes = PA_MIN(u->memchunk.length, rewind_nbytes); +        u->memchunk.length -= rewind_nbytes; +        pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); +    } + +    pa_sink_process_rewind(u->sink, rewind_nbytes); +} +  static void thread_func(void *userdata) {      struct userdata *u = userdata;      unsigned short revents = 0; @@ -604,10 +611,13 @@ static void thread_func(void *userdata) {              uint64_t buffered_bytes;              if (u->sink->thread_info.rewind_requested) -                pa_sink_process_rewind(u->sink, 0); +                process_rewind(u);              err = ioctl(u->fd, AUDIO_GETINFO, &info); -            pa_assert(err >= 0); +            if (err < 0) { +                pa_log("AUDIO_GETINFO ioctl failed: %s", pa_cstrerror(errno)); +                goto fail; +            }              if (info.play.error) {                  pa_log_debug("buffer under-run!"); @@ -635,7 +645,7 @@ static void thread_func(void *userdata) {                  len = u->buffer_size - buffered_bytes;                  len -= len % u->frame_size; -                if (len < u->min_request) +                if (len < (size_t) u->minimum_request)                      break;                  if (u->memchunk.length < len) @@ -648,12 +658,16 @@ static void thread_func(void *userdata) {                  if (w <= 0) {                      switch (errno) {                          case EINTR: -                            break; +                            continue;                          case EAGAIN: +                            /* If the buffer_size is too big, we get EAGAIN. Avoiding that limit by trial and error +                             * is not ideal, but I don't know how to get the system to tell me what the limit is. +                             */                              u->buffer_size = u->buffer_size * 18 / 25;                              u->buffer_size -= u->buffer_size % u->frame_size; -                            u->buffer_size = PA_MAX(u->buffer_size, (int32_t)MIN_BUFFER_SIZE); +                            u->buffer_size = PA_MAX(u->buffer_size, 2 * u->minimum_request);                              pa_sink_set_max_request_within_thread(u->sink, u->buffer_size); +                            pa_sink_set_max_rewind_within_thread(u->sink, u->buffer_size);                              pa_log("EAGAIN. Buffer size is now %u bytes (%llu buffered)", u->buffer_size, buffered_bytes);                              break;                          default: @@ -663,10 +677,8 @@ static void thread_func(void *userdata) {                  } else {                      pa_assert(w % u->frame_size == 0); -                    pa_mutex_lock(u->written_bytes_lock);                      u->written_bytes += w;                      u->memchunk.length -= w; -                    pa_mutex_unlock(u->written_bytes_lock);                      u->memchunk.index += w;                      if (u->memchunk.length <= 0) { @@ -677,9 +689,8 @@ static void thread_func(void *userdata) {              }              pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec)); -        } else { +        } else              pa_rtpoll_set_timer_disabled(u->rtpoll); -        }          /* Try to read some data and pass it on to the source driver */ @@ -797,6 +808,7 @@ int pa__init(pa_module *m) {      pa_sample_spec ss;      pa_channel_map map;      pa_modargs *ma = NULL; +    uint32_t buffer_length_msec;      int fd;      pa_sink_new_data sink_new_data;      pa_source_new_data source_new_data; @@ -822,8 +834,6 @@ int pa__init(pa_module *m) {      }      u = pa_xnew0(struct userdata, 1); -    u->sample_counter_lock = pa_mutex_new(FALSE, FALSE); -    u->written_bytes_lock = pa_mutex_new(FALSE, FALSE);      /*       * For a process (or several processes) to use the same audio device for both @@ -839,13 +849,15 @@ int pa__init(pa_module *m) {      }      u->frame_size = pa_frame_size(&ss); -    u->buffer_size = 16384; -    if (pa_modargs_get_value_s32(ma, "buffer_size", &u->buffer_size) < 0) { -        pa_log("failed to parse buffer size argument"); +    u->minimum_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, &ss); + +    buffer_length_msec = 100; +    if (pa_modargs_get_value_u32(ma, "buffer_length", &buffer_length_msec) < 0) { +        pa_log("failed to parse buffer_length argument");          goto fail;      } -    u->buffer_size -= u->buffer_size % u->frame_size; -    if (u->buffer_size < (int32_t)MIN_BUFFER_SIZE) { +    u->buffer_size = pa_usec_to_bytes(1000 * buffer_length_msec, &ss); +    if (u->buffer_size < 2 * u->minimum_request) {          pa_log("supplied buffer size argument is too small");          goto fail;      } @@ -947,15 +959,17 @@ int pa__init(pa_module *m) {          u->sink->refresh_volume = u->sink->refresh_muted = TRUE;          pa_sink_set_max_request(u->sink, u->buffer_size); -        u->min_request = pa_usec_to_bytes(PA_USEC_PER_SEC / MAX_RENDER_HZ, &ss); +        pa_sink_set_max_rewind(u->sink, u->buffer_size);      } else          u->sink = NULL;      pa_assert(u->source || u->sink);      u->sig = pa_signal_new(SIGPOLL, sig_callback, u); -    pa_assert(u->sig); -    ioctl(u->fd, I_SETSIG, S_MSG); +    if (u->sig) +        ioctl(u->fd, I_SETSIG, S_MSG); +    else +        pa_log_warn("Could not register SIGPOLL handler");      if (!(u->thread = pa_thread_new(thread_func, u))) {          pa_log("Failed to create thread."); @@ -1010,8 +1024,10 @@ void pa__done(pa_module *m) {      if (!(u = m->userdata))          return; -    ioctl(u->fd, I_SETSIG, 0); -    pa_signal_free(u->sig); +    if (u->sig) { +        ioctl(u->fd, I_SETSIG, 0); +        pa_signal_free(u->sig); +    }      if (u->sink)          pa_sink_unlink(u->sink); @@ -1044,9 +1060,6 @@ void pa__done(pa_module *m) {      if (u->fd >= 0)          close(u->fd); -    pa_mutex_free(u->written_bytes_lock); -    pa_mutex_free(u->sample_counter_lock); -      pa_xfree(u->device_name);      pa_xfree(u); diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index ec8a2c06..d7ef1c0c 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -344,7 +344,7 @@ typedef struct pa_module_info {      pa_proplist *proplist;              /**< Property list \since 0.9.15 */  } pa_module_info; -/** Callback prototype for pa_context_get_module_info() and firends*/ +/** Callback prototype for pa_context_get_module_info() and friends*/  typedef void (*pa_module_info_cb_t) (pa_context *c, const pa_module_info*i, int eol, void *userdata);  /** Get some information about a module by its index */ @@ -377,7 +377,7 @@ typedef struct pa_client_info {      pa_proplist *proplist;               /**< Property list \since 0.9.11 */  } pa_client_info; -/** Callback prototype for pa_context_get_client_info() and firends*/ +/** Callback prototype for pa_context_get_client_info() and friends*/  typedef void (*pa_client_info_cb_t) (pa_context *c, const pa_client_info*i, int eol, void *userdata);  /** Get information about a client by its index */ @@ -418,7 +418,7 @@ typedef struct pa_card_info {      pa_proplist *proplist;               /**< Property list */  } pa_card_info; -/** Callback prototype for pa_context_get_card_info() and firends \since 0.9.15 */ +/** Callback prototype for pa_context_get_card_info() and friends \since 0.9.15 */  typedef void (*pa_card_info_cb_t) (pa_context *c, const pa_card_info*i, int eol, void *userdata);  /** Get information about a card by its index \since 0.9.15 */ @@ -460,7 +460,7 @@ typedef struct pa_sink_input_info {      pa_proplist *proplist;               /**< Property list \since 0.9.11 */  } pa_sink_input_info; -/** Callback prototype for pa_context_get_sink_input_info() and firends*/ +/** Callback prototype for pa_context_get_sink_input_info() and friends*/  typedef void (*pa_sink_input_info_cb_t) (pa_context *c, const pa_sink_input_info *i, int eol, void *userdata);  /** Get some information about a sink input by its index */ @@ -506,7 +506,7 @@ typedef struct pa_source_output_info {      pa_proplist *proplist;               /**< Property list \since 0.9.11 */  } pa_source_output_info; -/** Callback prototype for pa_context_get_source_output_info() and firends*/ +/** Callback prototype for pa_context_get_source_output_info() and friends*/  typedef void (*pa_source_output_info_cb_t) (pa_context *c, const pa_source_output_info *i, int eol, void *userdata);  /** Get information about a source output by its index */ @@ -571,7 +571,7 @@ typedef struct pa_sample_info {      pa_proplist *proplist;                /**< Property list for this sample. \since 0.9.11 */  } pa_sample_info; -/** Callback prototype for pa_context_get_sample_info_by_name() and firends */ +/** Callback prototype for pa_context_get_sample_info_by_name() and friends */  typedef void (*pa_sample_info_cb_t)(pa_context *c, const pa_sample_info *i, int eol, void *userdata);  /** Get information about a sample by its name */ @@ -606,7 +606,7 @@ typedef struct pa_autoload_info {      const char *argument;         /**< Argument string for module */  } pa_autoload_info; -/** \deprecated Callback prototype for pa_context_get_autoload_info_by_name() and firends */ +/** \deprecated Callback prototype for pa_context_get_autoload_info_by_name() and friends */  typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);  /** \deprecated Get info about a specific autoload entry. */ diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index e5d8a2f4..4b093c0e 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -2608,7 +2608,7 @@ char *pa_unescape(char *p) {  }  char *pa_realpath(const char *path) { -    char *r, *t; +    char *t;      pa_assert(path);      /* We want only abolsute paths */ @@ -2617,17 +2617,31 @@ char *pa_realpath(const char *path) {          return NULL;      } -#if !defined(__GLIBC__) && !defined(__APPLE__) -#error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here." -#endif +#if defined(__GLIBC__) || defined(__APPLE__) +    { +        char *r; -    if (!(r = realpath(path, NULL))) -        return NULL; +        if (!(r = realpath(path, NULL))) +            return NULL; + +        /* We copy this here in case our pa_xmalloc() is not +         * implemented on top of libc malloc() */ +        t = pa_xstrdup(r); +        pa_xfree(r); +    } +#elif defined(PATH_MAX) +    { +        char *path_buf; +        path_buf = pa_xmalloc(PATH_MAX); -    /* We copy this here in case our pa_xmalloc() is not implemented -     * on top of libc malloc() */ -    t = pa_xstrdup(r); -    pa_xfree(r); +        if (!(t = realpath(path, path_buf))) { +            pa_xfree(path_buf); +            return NULL; +        } +    } +#else +#error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here." +#endif      return t;  }  | 
