aboutsummaryrefslogtreecommitdiffstats
path: root/res
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2009-05-29 20:11:00 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2009-05-29 20:11:00 +0000
commit10e1be3284dac3881e85ef6583d58aeb21f8e68a (patch)
tree05789e53556f0d771da09fd4ac71c361d6a121a4 /res
parent3438e76a970882f627b453124f5dd21d0ba60bfa (diff)
Merged revisions 198146 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk ........ r198146 | russell | 2009-05-29 15:06:59 -0500 (Fri, 29 May 2009) | 38 lines Resolve issues with choppy sound when using res_timing_pthread. The situation that caused this problem was when continuous mode was being turned on and off while a rate was set for a timing interface. A very easy way to replicate this bug was to do a Playback() from behind a Local channel. In this scenario, a rate gets set on the channel for doing file playback. At the same time, continuous mode gets turned on and off about every 20 ms as frames get queued on to the PBX side channel from the other side of the Local channel. Essentially, this module treated continuous mode and a set rate as mutually exclusive states for the timer to be in. When I dug deep enough, I observed the following pattern: 1) Set timer to tick every 20 ms. 2) Wait almost 20 ms ... 3) Continuous mode gets turned on for a queued up frame 4) Continuous mode gets turned off 5) The timer goes back to its tick per 20 ms. state but starts counting at 0 ms. 6) Goto step 2. Sometimes, res_timing_pthread would make it 20 ms and produce a timer tick, but not most of the time. This is what produced the choppy sound (or sometimes no sound at all). Now, the module treats continuous mode and a set rate as completely independent timer modes. They can be enabled and disabled independently of each other and things work as expected. (closes issue #14412) Reported by: dome Patches: issue14412.diff.txt uploaded by russell (license 2) issue14412-1.6.1.0.diff.txt uploaded by russell (license 2) Tested by: DennisD, russell ........ git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.6.1@198147 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'res')
-rw-r--r--res/res_timing_pthread.c124
1 files changed, 62 insertions, 62 deletions
diff --git a/res/res_timing_pthread.c b/res/res_timing_pthread.c
index 8eea1a0f9..5e3c31364 100644
--- a/res/res_timing_pthread.c
+++ b/res/res_timing_pthread.c
@@ -75,7 +75,6 @@ enum {
enum pthread_timer_state {
TIMER_STATE_IDLE,
TIMER_STATE_TICKING,
- TIMER_STATE_CONTINUOUS,
};
struct pthread_timer {
@@ -85,13 +84,15 @@ struct pthread_timer {
/*! Interval in ms for current rate */
unsigned int interval;
unsigned int tick_count;
+ unsigned int pending_ticks;
struct timeval start;
+ unsigned int continuous:1;
};
static void pthread_timer_destructor(void *obj);
static struct pthread_timer *find_timer(int handle, int unlinkobj);
-static void write_byte(int wr_fd);
-static void read_pipe(int rd_fd, unsigned int num, int clear);
+static void write_byte(struct pthread_timer *timer);
+static void read_pipe(struct pthread_timer *timer, unsigned int num);
/*!
* \brief Data for the timing thread
@@ -148,23 +149,6 @@ static void pthread_timer_close(int handle)
ao2_ref(timer, -1);
}
-static void set_state(struct pthread_timer *timer)
-{
- unsigned int rate = timer->rate;
-
- if (rate) {
- timer->state = TIMER_STATE_TICKING;
- timer->interval = roundf(1000.0 / ((float) rate));
- timer->start = ast_tvnow();
- } else {
- timer->state = TIMER_STATE_IDLE;
- timer->interval = 0;
- timer->start = ast_tv(0, 0);
- }
-
- timer->tick_count = 0;
-}
-
static int pthread_timer_set_rate(int handle, unsigned int rate)
{
struct pthread_timer *timer;
@@ -175,17 +159,24 @@ static int pthread_timer_set_rate(int handle, unsigned int rate)
}
if (rate > MAX_RATE) {
- ast_log(LOG_ERROR, "res_timing_pthread only supports timers at a max rate of %d / sec\n",
- MAX_RATE);
+ ast_log(LOG_ERROR, "res_timing_pthread only supports timers at a "
+ "max rate of %d / sec\n", MAX_RATE);
errno = EINVAL;
return -1;
}
ao2_lock(timer);
- timer->rate = rate;
- if (timer->state != TIMER_STATE_CONTINUOUS) {
- set_state(timer);
+
+ if ((timer->rate = rate)) {
+ timer->interval = roundf(1000.0 / ((float) rate));
+ timer->start = ast_tvnow();
+ timer->state = TIMER_STATE_TICKING;
+ } else {
+ timer->interval = 0;
+ timer->start = ast_tv(0, 0);
+ timer->state = TIMER_STATE_IDLE;
}
+ timer->tick_count = 0;
ao2_unlock(timer);
@@ -204,12 +195,9 @@ static void pthread_timer_ack(int handle, unsigned int quantity)
return;
}
- if (timer->state == TIMER_STATE_CONTINUOUS) {
- /* Leave the pipe alone, please! */
- return;
- }
-
- read_pipe(timer->pipe[PIPE_READ], quantity, 0);
+ ao2_lock(timer);
+ read_pipe(timer, quantity);
+ ao2_unlock(timer);
ao2_ref(timer, -1);
}
@@ -224,8 +212,10 @@ static int pthread_timer_enable_continuous(int handle)
}
ao2_lock(timer);
- timer->state = TIMER_STATE_CONTINUOUS;
- write_byte(timer->pipe[PIPE_WRITE]);
+ if (!timer->continuous) {
+ timer->continuous = 1;
+ write_byte(timer);
+ }
ao2_unlock(timer);
ao2_ref(timer, -1);
@@ -243,8 +233,10 @@ static int pthread_timer_disable_continuous(int handle)
}
ao2_lock(timer);
- set_state(timer);
- read_pipe(timer->pipe[PIPE_READ], 0, 1);
+ if (timer->continuous) {
+ timer->continuous = 0;
+ read_pipe(timer, 1);
+ }
ao2_unlock(timer);
ao2_ref(timer, -1);
@@ -261,9 +253,11 @@ static enum ast_timer_event pthread_timer_get_event(int handle)
return res;
}
- if (timer->state == TIMER_STATE_CONTINUOUS) {
+ ao2_lock(timer);
+ if (timer->continuous && timer->pending_ticks == 1) {
res = AST_TIMING_EVENT_CONTINUOUS;
}
+ ao2_unlock(timer);
ao2_ref(timer, -1);
@@ -338,7 +332,7 @@ static int check_timer(struct pthread_timer *timer)
{
struct timeval now;
- if (timer->state == TIMER_STATE_IDLE || timer->state == TIMER_STATE_CONTINUOUS) {
+ if (timer->state == TIMER_STATE_IDLE) {
return 0;
}
@@ -347,6 +341,7 @@ static int check_timer(struct pthread_timer *timer)
if (timer->tick_count < (ast_tvdiff_ms(now, timer->start) / timer->interval)) {
timer->tick_count++;
if (!timer->tick_count) {
+ /* Handle overflow. */
timer->start = now;
}
return 1;
@@ -355,13 +350,16 @@ static int check_timer(struct pthread_timer *timer)
return 0;
}
-static void read_pipe(int rd_fd, unsigned int quantity, int clear)
+/*!
+ * \internal
+ * \pre timer is locked
+ */
+static void read_pipe(struct pthread_timer *timer, unsigned int quantity)
{
- ast_assert(quantity || clear);
+ int rd_fd = timer->pipe[PIPE_READ];
- if (!quantity && clear) {
- quantity = 1;
- }
+ ast_assert(quantity);
+ ast_assert(quantity >= timer->pending_ticks);
do {
unsigned char buf[1024];
@@ -376,6 +374,8 @@ static void read_pipe(int rd_fd, unsigned int quantity, int clear)
FD_SET(rd_fd, &rfds);
if (select(rd_fd + 1, &rfds, NULL, NULL, &timeout) != 1) {
+ ast_debug(1, "Reading not available on timing pipe, "
+ "quantity: %u\n", quantity);
break;
}
@@ -386,33 +386,35 @@ static void read_pipe(int rd_fd, unsigned int quantity, int clear)
if (errno == EAGAIN) {
continue;
}
- ast_log(LOG_ERROR, "read failed on timing pipe: %s\n", strerror(errno));
+ ast_log(LOG_ERROR, "read failed on timing pipe: %s\n",
+ strerror(errno));
break;
}
- if (clear) {
- continue;
- }
-
quantity -= res;
+ timer->pending_ticks -= res;
} while (quantity);
}
-static void write_byte(int wr_fd)
+/*!
+ * \internal
+ * \pre timer is locked
+ */
+static void write_byte(struct pthread_timer *timer)
{
- do {
- ssize_t res;
- unsigned char x = 42;
+ ssize_t res;
+ unsigned char x = 42;
- res = write(wr_fd, &x, 1);
+ do {
+ res = write(timer->pipe[PIPE_WRITE], &x, 1);
+ } while (res == -1 && errno == EAGAIN);
- if (res == -1) {
- if (errno == EAGAIN) {
- continue;
- }
- ast_log(LOG_ERROR, "Error writing to timing pipe: %s\n", strerror(errno));
- }
- } while (0);
+ if (res == -1) {
+ ast_log(LOG_ERROR, "Error writing to timing pipe: %s\n",
+ strerror(errno));
+ } else {
+ timer->pending_ticks++;
+ }
}
static int run_timer(void *obj, void *arg, int flags)
@@ -424,11 +426,9 @@ static int run_timer(void *obj, void *arg, int flags)
}
ao2_lock(timer);
-
if (check_timer(timer)) {
- write_byte(timer->pipe[PIPE_WRITE]);
+ write_byte(timer);
}
-
ao2_unlock(timer);
return 0;