diff options
| author | Lennart Poettering <lennart@poettering.net> | 2009-08-24 03:26:56 +0200 | 
|---|---|---|
| committer | Lennart Poettering <lennart@poettering.net> | 2009-08-24 03:27:29 +0200 | 
| commit | 050a3a99e1d151b4f55c89f82073ef33f3399646 (patch) | |
| tree | 0c656cc005fd0f77e7b219ab334464e1026fc7ca /src | |
| parent | 80c693730365c1a375a5c0e781f38e7f165b37bf (diff) | |
alsa: automatically decrease watermark after a time of stability
Diffstat (limited to 'src')
| -rw-r--r-- | src/modules/alsa/alsa-sink.c | 146 | ||||
| -rw-r--r-- | src/modules/alsa/alsa-source.c | 145 | ||||
| -rw-r--r-- | src/pulsecore/rtpoll.c | 28 | ||||
| -rw-r--r-- | src/pulsecore/rtpoll.h | 4 | 
4 files changed, 240 insertions, 83 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index b99ed782..07d53880 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -62,14 +62,21 @@  /* #define DEBUG_TIMING */  #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)            /* 2s   -- Overall buffer size */ -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)       /* 20ms -- Fill up when only this much is left in the buffer */ -#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC)          /* 10ms -- On underrun, increase watermark by this */ -#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC)               /* 10ms -- Sleep at least 10ms on each iteration */ -#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC)               /* 4ms  -- Wakeup at least this long before the buffer runs empty*/ -#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC)                /* 2ms -- min smoother update interval */ -#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC)              /* 200ms -- max smoother update inteval */ +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)             /* 2s    -- Overall buffer size */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)        /* 20ms  -- Fill up when only this much is left in the buffer */ + +#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC)       /* 10ms  -- On underrun, increase watermark by this */ +#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC)        /* 5ms   -- When everything's great, decrease watermark by this */ +#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC)    /* 20s   -- How long after a drop out recheck if things are good now */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC)   /* 3ms   -- If the buffer level ever below this theshold, increase the watermark */ +#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms -- If the buffer level didn't drop below this theshold in the verification time, decrease the watermark */ + +#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC)                /* 10ms  -- Sleep at least 10ms on each iteration */ +#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC)                /* 4ms   -- Wakeup at least this long before the buffer runs empty*/ + +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC)                 /* 2ms   -- min smoother update interval */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC)               /* 200ms -- max smoother update inteval */  #define VOLUME_ACCURACY (PA_VOLUME_NORM/100)  /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */ @@ -99,7 +106,12 @@ struct userdata {          hwbuf_unused,          min_sleep,          min_wakeup, -        watermark_step; +        watermark_inc_step, +        watermark_dec_step, +        watermark_inc_threshold, +        watermark_dec_threshold; + +    pa_usec_t watermark_dec_not_before;      unsigned nfragments;      pa_memchunk memchunk; @@ -248,6 +260,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) {      size_t max_use, max_use_2;      pa_assert(u); +    pa_assert(u->use_tsched);      max_use = u->hwbuf_size - u->hwbuf_unused;      max_use_2 = pa_frame_align(max_use/2, &u->sink->sample_spec); @@ -262,6 +275,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) {  static void fix_tsched_watermark(struct userdata *u) {      size_t max_use;      pa_assert(u); +    pa_assert(u->use_tsched);      max_use = u->hwbuf_size - u->hwbuf_unused; @@ -272,7 +286,7 @@ static void fix_tsched_watermark(struct userdata *u) {          u->tsched_watermark = u->min_wakeup;  } -static void adjust_after_underrun(struct userdata *u) { +static void increase_watermark(struct userdata *u) {      size_t old_watermark;      pa_usec_t old_min_latency, new_min_latency; @@ -281,31 +295,64 @@ static void adjust_after_underrun(struct userdata *u) {      /* First, just try to increase the watermark */      old_watermark = u->tsched_watermark; -    u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_step); +    u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step);      fix_tsched_watermark(u);      if (old_watermark != u->tsched_watermark) { -        pa_log_notice("Increasing wakeup watermark to %0.2f ms", -                      (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); +        pa_log_info("Increasing wakeup watermark to %0.2f ms", +                    (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);          return;      }      /* Hmm, we cannot increase the watermark any further, hence let's raise the latency */      old_min_latency = u->sink->thread_info.min_latency; -    new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC); +    new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC);      new_min_latency = PA_MIN(new_min_latency, u->sink->thread_info.max_latency);      if (old_min_latency != new_min_latency) { -        pa_log_notice("Increasing minimal latency to %0.2f ms", -                      (double) new_min_latency / PA_USEC_PER_MSEC); +        pa_log_info("Increasing minimal latency to %0.2f ms", +                    (double) new_min_latency / PA_USEC_PER_MSEC);          pa_sink_set_latency_range_within_thread(u->sink, new_min_latency, u->sink->thread_info.max_latency); -        return;      }      /* When we reach this we're officialy fucked! */  } +static void decrease_watermark(struct userdata *u) { +    size_t old_watermark; +    pa_usec_t now; + +    pa_assert(u); +    pa_assert(u->use_tsched); + +    now = pa_rtclock_now(); + +    if (u->watermark_dec_not_before <= 0) +        goto restart; + +    if (u->watermark_dec_not_before > now) +        return; + +    old_watermark = u->tsched_watermark; + +    if (u->tsched_watermark < u->watermark_dec_step) +        u->tsched_watermark = u->tsched_watermark / 2; +    else +        u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step); + +    fix_tsched_watermark(u); + +    if (old_watermark != u->tsched_watermark) +        pa_log_info("Decreasing wakeup watermark to %0.2f ms", +                    (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + +    /* We don't change the latency range*/ + +restart: +    u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC; +} +  static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {      pa_usec_t usec, wm; @@ -313,6 +360,7 @@ static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*p      pa_assert(process_usec);      pa_assert(u); +    pa_assert(u->use_tsched);      usec = pa_sink_get_requested_latency_within_thread(u->sink); @@ -360,7 +408,7 @@ static int try_recover(struct userdata *u, const char *call, int err) {      return 0;  } -static size_t check_left_to_play(struct userdata *u, size_t n_bytes) { +static size_t check_left_to_play(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) {      size_t left_to_play;      /* We use <= instead of < for this check here because an underrun @@ -368,34 +416,55 @@ static size_t check_left_to_play(struct userdata *u, size_t n_bytes) {       * it is removed from the buffer. This is particularly important       * when block transfer is used. */ -    if (n_bytes <= u->hwbuf_size) { +    if (n_bytes <= u->hwbuf_size)          left_to_play = u->hwbuf_size - n_bytes; +    else { + +        /* We got a dropout. What a mess! */ +        left_to_play = 0;  #ifdef DEBUG_TIMING -        pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); +        PA_DEBUG_TRAP;  #endif -    } else { -        left_to_play = 0; +        if (!u->first && !u->after_rewind) +            if (pa_log_ratelimit()) +                pa_log_info("Underrun!"); +    }  #ifdef DEBUG_TIMING -        PA_DEBUG_TRAP; +    pa_log_debug("%0.2f ms left to play; inc threshold = %0.2f ms; dec threshold = %0.2f ms", +                 (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC, +                 (double) pa_bytes_to_usec(u->watermark_inc_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC, +                 (double) pa_bytes_to_usec(u->watermark_dec_threshold, &u->sink->sample_spec) / PA_USEC_PER_MSEC);  #endif +    if (u->use_tsched) { +        pa_bool_t reset_not_before = TRUE; +          if (!u->first && !u->after_rewind) { +            if (left_to_play < u->watermark_inc_threshold) +                increase_watermark(u); +            else if (left_to_play > u->watermark_dec_threshold) { +                reset_not_before = FALSE; -            if (pa_log_ratelimit()) -                pa_log_info("Underrun!"); +                /* We decrease the watermark only if have actually +                 * been woken up by a timeout. If something else woke +                 * us up it's too easy to fulfill the deadlines... */ -            if (u->use_tsched) -                adjust_after_underrun(u); +                if (on_timeout) +                    decrease_watermark(u); +            }          } + +        if (reset_not_before) +            u->watermark_dec_not_before = 0;      }      return left_to_play;  } -static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {      pa_bool_t work_done = TRUE;      pa_usec_t max_sleep_usec = 0, process_usec = 0;      size_t left_to_play; @@ -430,7 +499,8 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle          pa_log_debug("avail: %lu", (unsigned long) n_bytes);  #endif -        left_to_play = check_left_to_play(u, n_bytes); +        left_to_play = check_left_to_play(u, n_bytes, on_timeout); +        on_timeout = FALSE;          if (u->use_tsched) @@ -565,7 +635,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle      return work_done ? 1 : 0;  } -static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {      pa_bool_t work_done = FALSE;      pa_usec_t max_sleep_usec = 0, process_usec = 0;      size_t left_to_play; @@ -591,7 +661,8 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle          }          n_bytes = (size_t) n * u->frame_size; -        left_to_play = check_left_to_play(u, n_bytes); +        left_to_play = check_left_to_play(u, n_bytes, on_timeout); +        on_timeout = FALSE;          if (u->use_tsched) @@ -1278,15 +1349,16 @@ static void thread_func(void *userdata) {          if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {              int work_done;              pa_usec_t sleep_usec = 0; +            pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);              if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))                  if (process_rewind(u) < 0)                          goto fail;              if (u->use_mmap) -                work_done = mmap_write(u, &sleep_usec, revents & POLLOUT); +                work_done = mmap_write(u, &sleep_usec, revents & POLLOUT, on_timeout);              else -                work_done = unix_write(u, &sleep_usec, revents & POLLOUT); +                work_done = unix_write(u, &sleep_usec, revents & POLLOUT, on_timeout);              if (work_done < 0)                  goto fail; @@ -1787,7 +1859,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca      u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);      u->nfragments = nfrags;      u->hwbuf_size = u->fragment_size * nfrags; -    u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec);      pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels);      pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", @@ -1798,7 +1869,13 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca      pa_sink_set_max_rewind(u->sink, u->hwbuf_size);      if (u->use_tsched) { -        u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->sink->sample_spec); +        u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec); + +        u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->sink->sample_spec); +        u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->sink->sample_spec); + +        u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->sink->sample_spec); +        u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->sink->sample_spec);          fix_min_sleep_wakeup(u);          fix_tsched_watermark(u); @@ -1812,6 +1889,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca      } else          pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->hwbuf_size, &ss)); +      reserve_update(u);      if (update_sw_params(u) < 0) diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 336027a2..165b2e3b 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -59,14 +59,22 @@  /* #define DEBUG_TIMING */  #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)       /* 2s */ -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)  /* 20ms */ -#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC)     /* 10ms */ -#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC)          /* 10ms */ -#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC)          /* 4ms */ -#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC)           /* 2ms */ -#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC)         /* 200ms */ +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)             /* 2s */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)        /* 20ms */ + +#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC)       /* 10ms  */ +#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC)        /* 5ms */ +#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC)    /* 20s */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (1*PA_USEC_PER_MSEC)   /* 3ms */ +#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms */ +#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC)           /* 10ms */ + +#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC)                /* 10ms */ +#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC)                /* 4ms */ + +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC)                 /* 2ms */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC)               /* 200ms */  #define VOLUME_ACCURACY (PA_VOLUME_NORM/100) @@ -96,7 +104,12 @@ struct userdata {          hwbuf_unused,          min_sleep,          min_wakeup, -        watermark_step; +        watermark_inc_step, +        watermark_dec_step, +        watermark_inc_threshold, +        watermark_dec_threshold; + +    pa_usec_t watermark_dec_not_before;      unsigned nfragments; @@ -241,6 +254,7 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) {  static void fix_min_sleep_wakeup(struct userdata *u) {      size_t max_use, max_use_2;      pa_assert(u); +    pa_assert(u->use_tsched);      max_use = u->hwbuf_size - u->hwbuf_unused;      max_use_2 = pa_frame_align(max_use/2, &u->source->sample_spec); @@ -255,6 +269,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) {  static void fix_tsched_watermark(struct userdata *u) {      size_t max_use;      pa_assert(u); +    pa_assert(u->use_tsched);      max_use = u->hwbuf_size - u->hwbuf_unused; @@ -265,7 +280,7 @@ static void fix_tsched_watermark(struct userdata *u) {          u->tsched_watermark = u->min_wakeup;  } -static void adjust_after_overrun(struct userdata *u) { +static void increase_watermark(struct userdata *u) {      size_t old_watermark;      pa_usec_t old_min_latency, new_min_latency; @@ -274,36 +289,72 @@ static void adjust_after_overrun(struct userdata *u) {      /* First, just try to increase the watermark */      old_watermark = u->tsched_watermark; -    u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_step); - +    u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step);      fix_tsched_watermark(u);      if (old_watermark != u->tsched_watermark) { -        pa_log_notice("Increasing wakeup watermark to %0.2f ms", -                      (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); +        pa_log_info("Increasing wakeup watermark to %0.2f ms", +                    (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);          return;      }      /* Hmm, we cannot increase the watermark any further, hence let's raise the latency */      old_min_latency = u->source->thread_info.min_latency; -    new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC); +    new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC);      new_min_latency = PA_MIN(new_min_latency, u->source->thread_info.max_latency);      if (old_min_latency != new_min_latency) { -        pa_log_notice("Increasing minimal latency to %0.2f ms", -                      (double) new_min_latency / PA_USEC_PER_MSEC); +        pa_log_info("Increasing minimal latency to %0.2f ms", +                    (double) new_min_latency / PA_USEC_PER_MSEC);          pa_source_set_latency_range_within_thread(u->source, new_min_latency, u->source->thread_info.max_latency); -        return;      }      /* When we reach this we're officialy fucked! */  } +static void decrease_watermark(struct userdata *u) { +    size_t old_watermark; +    pa_usec_t now; + +    pa_assert(u); +    pa_assert(u->use_tsched); + +    now = pa_rtclock_now(); + +    if (u->watermark_dec_not_before <= 0) +        goto restart; + +    if (u->watermark_dec_not_before > now) +        return; + +    old_watermark = u->tsched_watermark; + +    if (u->tsched_watermark < u->watermark_dec_step) +        u->tsched_watermark = u->tsched_watermark / 2; +    else +        u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step); + +    fix_tsched_watermark(u); + +    if (old_watermark != u->tsched_watermark) +        pa_log_info("Decreasing wakeup watermark to %0.2f ms", +                    (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); + +    /* We don't change the latency range*/ + +restart: +    u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC; +} +  static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {      pa_usec_t wm, usec; +    pa_assert(sleep_usec); +    pa_assert(process_usec); +      pa_assert(u); +    pa_assert(u->use_tsched);      usec = pa_source_get_requested_latency_within_thread(u->source); @@ -352,7 +403,7 @@ static int try_recover(struct userdata *u, const char *call, int err) {      return 0;  } -static size_t check_left_to_record(struct userdata *u, size_t n_bytes) { +static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) {      size_t left_to_record;      size_t rec_space = u->hwbuf_size - u->hwbuf_unused; @@ -361,14 +412,11 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) {       * it is removed from the buffer. This is particularly important       * when block transfer is used. */ -    if (n_bytes <= rec_space) { +    if (n_bytes <= rec_space)          left_to_record = rec_space - n_bytes; +    else { -#ifdef DEBUG_TIMING -        pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); -#endif - -    } else { +        /* We got a dropout. What a mess! */          left_to_record = 0;  #ifdef DEBUG_TIMING @@ -377,15 +425,36 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) {          if (pa_log_ratelimit())              pa_log_info("Overrun!"); +    } -        if (u->use_tsched) -            adjust_after_overrun(u); +#ifdef DEBUG_TIMING +    pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); +#endif + +    if (u->use_tsched) { +        pa_bool_t reset_not_before = TRUE; + +        if (left_to_record < u->watermark_inc_threshold) +            increase_watermark(u); +        else if (left_to_record > u->watermark_dec_threshold) { +            reset_not_before = FALSE; + +            /* We decrease the watermark only if have actually been +             * woken up by a timeout. If something else woke us up +             * it's too easy to fulfill the deadlines... */ + +            if (on_timeout) +                decrease_watermark(u); +        } + +        if (reset_not_before) +            u->watermark_dec_not_before = 0;      }      return left_to_record;  } -static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {      pa_bool_t work_done = FALSE;      pa_usec_t max_sleep_usec = 0, process_usec = 0;      size_t left_to_record; @@ -417,7 +486,8 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled          pa_log_debug("avail: %lu", (unsigned long) n_bytes);  #endif -        left_to_record = check_left_to_record(u, n_bytes); +        left_to_record = check_left_to_record(u, n_bytes, on_timeout); +        on_timeout = FALSE;          if (u->use_tsched)              if (!polled && @@ -543,7 +613,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled      return work_done ? 1 : 0;  } -static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) {      int work_done = FALSE;      pa_usec_t max_sleep_usec = 0, process_usec = 0;      size_t left_to_record; @@ -570,7 +640,8 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled          }          n_bytes = (size_t) n * u->frame_size; -        left_to_record = check_left_to_record(u, n_bytes); +        left_to_record = check_left_to_record(u, n_bytes, on_timeout); +        on_timeout = FALSE;          if (u->use_tsched)              if (!polled && @@ -1158,11 +1229,12 @@ static void thread_func(void *userdata) {          if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {              int work_done;              pa_usec_t sleep_usec = 0; +            pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);              if (u->use_mmap) -                work_done = mmap_read(u, &sleep_usec, revents & POLLIN); +                work_done = mmap_read(u, &sleep_usec, revents & POLLIN, on_timeout);              else -                work_done = unix_read(u, &sleep_usec, revents & POLLIN); +                work_done = unix_read(u, &sleep_usec, revents & POLLIN, on_timeout);              if (work_done < 0)                  goto fail; @@ -1632,7 +1704,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p      u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);      u->nfragments = nfrags;      u->hwbuf_size = u->fragment_size * nfrags; -    u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec);      pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels);      pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", @@ -1640,7 +1711,13 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p                  (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);      if (u->use_tsched) { -        u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->source->sample_spec); +        u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec); + +        u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->source->sample_spec); +        u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->source->sample_spec); + +        u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->source->sample_spec); +        u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->source->sample_spec);          fix_min_sleep_wakeup(u);          fix_tsched_watermark(u); diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index 42708a8a..666cbc98 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -63,6 +63,7 @@ struct pa_rtpoll {      pa_bool_t running:1;      pa_bool_t rebuild_needed:1;      pa_bool_t quit:1; +    pa_bool_t timer_elapsed:1;  #ifdef DEBUG_TIMING      pa_usec_t timestamp; @@ -94,26 +95,14 @@ PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree);  pa_rtpoll *pa_rtpoll_new(void) {      pa_rtpoll *p; -    p = pa_xnew(pa_rtpoll, 1); +    p = pa_xnew0(pa_rtpoll, 1);      p->n_pollfd_alloc = 32;      p->pollfd = pa_xnew(struct pollfd, p->n_pollfd_alloc);      p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc); -    p->n_pollfd_used = 0; - -    pa_zero(p->next_elapse); -    p->timer_enabled = FALSE; - -    p->running = FALSE; -    p->scan_for_dead = FALSE; -    p->rebuild_needed = FALSE; -    p->quit = FALSE; - -    PA_LLIST_HEAD_INIT(pa_rtpoll_item, p->items);  #ifdef DEBUG_TIMING      p->timestamp = pa_rtclock_now(); -    p->slept = p->awake = 0;  #endif      return p; @@ -229,6 +218,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {      pa_assert(!p->running);      p->running = TRUE; +    p->timer_elapsed = FALSE;      /* First, let's do some work */      for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) { @@ -286,7 +276,7 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {      if (p->rebuild_needed)          rtpoll_rebuild(p); -    memset(&timeout, 0, sizeof(timeout)); +    pa_zero(timeout);      /* Calculate timeout */      if (wait_op && !p->quit && p->timer_enabled) { @@ -314,9 +304,11 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait_op) {          r = ppoll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? &ts : NULL, NULL);      }  #else -        r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1); +    r = poll(p->pollfd, p->n_pollfd_used, (!wait_op || p->quit || p->timer_enabled) ? (int) ((timeout.tv_sec*1000) + (timeout.tv_usec / 1000)) : -1);  #endif +    p->timer_elapsed = r == 0; +  #ifdef DEBUG_TIMING      {          pa_usec_t now = pa_rtclock_now(); @@ -628,3 +620,9 @@ void pa_rtpoll_quit(pa_rtpoll *p) {      p->quit = TRUE;  } + +pa_bool_t pa_rtpoll_timer_elapsed(pa_rtpoll *p) { +    pa_assert(p); + +    return p->timer_elapsed; +} diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h index d2d69cad..b2a87fca 100644 --- a/src/pulsecore/rtpoll.h +++ b/src/pulsecore/rtpoll.h @@ -73,6 +73,10 @@ void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec);  void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec);  void pa_rtpoll_set_timer_disabled(pa_rtpoll *p); +/* Return TRUE when the elapsed timer was the reason for + * the last pa_rtpoll_run() invocation to finish */ +pa_bool_t pa_rtpoll_timer_elapsed(pa_rtpoll *p); +  /* A new fd wakeup item for pa_rtpoll */  pa_rtpoll_item *pa_rtpoll_item_new(pa_rtpoll *p, pa_rtpoll_priority_t prio, unsigned n_fds);  void pa_rtpoll_item_free(pa_rtpoll_item *i);  | 
