~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/pjmedia/src/pjmedia/delaybuf.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
Tags: 1.3.0-1
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: delaybuf.c 3841 2011-10-24 09:28:13Z ming $ */
2
 
/*
3
 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
 
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5
 
 *
6
 
 * This program is free software; you can redistribute it and/or modify
7
 
 * it under the terms of the GNU General Public License as published by
8
 
 * the Free Software Foundation; either version 2 of the License, or
9
 
 * (at your option) any later version.
10
 
 *
11
 
 * This program is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 
 * GNU General Public License for more details.
15
 
 *
16
 
 * You should have received a copy of the GNU General Public License
17
 
 * along with this program; if not, write to the Free Software
18
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
 
 */
20
 
 
21
 
#include <pjmedia/delaybuf.h>
22
 
#include <pjmedia/circbuf.h>
23
 
#include <pjmedia/errno.h>
24
 
#include <pjmedia/frame.h>
25
 
#include <pjmedia/wsola.h>
26
 
#include <pj/assert.h>
27
 
#include <pj/lock.h>
28
 
#include <pj/log.h>
29
 
#include <pj/math.h>
30
 
#include <pj/pool.h>
31
 
 
32
 
 
33
 
#if 0
34
 
#   define TRACE__(x) PJ_LOG(3,x)
35
 
#else
36
 
#   define TRACE__(x)
37
 
#endif
38
 
 
39
 
/* Operation types of delay buffer */
40
 
enum OP
41
 
{
42
 
    OP_PUT,
43
 
    OP_GET
44
 
};
45
 
 
46
 
/* Specify time for delaybuf to recalculate effective delay, in ms.
47
 
 */
48
 
#define RECALC_TIME         2000
49
 
 
50
 
/* Default value of maximum delay, in ms, this value is used when
51
 
 * maximum delay requested is less than ptime (one frame length).
52
 
 */
53
 
#define DEFAULT_MAX_DELAY   400
54
 
 
55
 
/* Number of frames to add to learnt level for additional stability.
56
 
 */
57
 
#define SAFE_MARGIN         0
58
 
 
59
 
/* This structure describes internal delaybuf settings and states.
60
 
 */
61
 
struct pjmedia_delay_buf
62
 
{
63
 
    /* Properties and configuration */
64
 
    char             obj_name[PJ_MAX_OBJ_NAME];
65
 
    pj_lock_t       *lock;              /**< Lock object.                    */
66
 
    unsigned         samples_per_frame; /**< Number of samples in one frame  */
67
 
    unsigned         ptime;             /**< Frame time, in ms               */
68
 
    unsigned         channel_count;     /**< Channel count, in ms            */
69
 
    pjmedia_circ_buf *circ_buf;         /**< Circular buffer to store audio
70
 
                                             samples                         */
71
 
    unsigned         max_cnt;           /**< Maximum samples to be buffered  */
72
 
    unsigned         eff_cnt;           /**< Effective count of buffered
73
 
                                             samples to keep the optimum
74
 
                                             balance between delay and
75
 
                                             stability. This is calculated
76
 
                                             based on burst level.           */
77
 
 
78
 
    /* Learning vars */
79
 
    unsigned         level;             /**< Burst level counter             */
80
 
    enum OP          last_op;           /**< Last op (GET or PUT) of learning*/
81
 
    int              recalc_timer;      /**< Timer for recalculating max_level*/
82
 
    unsigned         max_level;         /**< Current max burst level         */
83
 
 
84
 
    /* Drift handler */
85
 
    pjmedia_wsola   *wsola;             /**< Drift handler                   */
86
 
};
87
 
 
88
 
 
89
 
PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
90
 
                                              const char *name,
91
 
                                              unsigned clock_rate,
92
 
                                              unsigned samples_per_frame,
93
 
                                              unsigned channel_count,
94
 
                                              unsigned max_delay,
95
 
                                              unsigned options,
96
 
                                              pjmedia_delay_buf **p_b)
97
 
{
98
 
    pjmedia_delay_buf *b;
99
 
    pj_status_t status;
100
 
 
101
 
    PJ_ASSERT_RETURN(pool && samples_per_frame && clock_rate && channel_count &&
102
 
                     p_b, PJ_EINVAL);
103
 
 
104
 
    if (!name) {
105
 
        name = "delaybuf";
106
 
    }
107
 
 
108
 
    b = PJ_POOL_ZALLOC_T(pool, pjmedia_delay_buf);
109
 
 
110
 
    pj_ansi_strncpy(b->obj_name, name, PJ_MAX_OBJ_NAME-1);
111
 
 
112
 
    b->samples_per_frame = samples_per_frame;
113
 
    b->channel_count = channel_count;
114
 
    b->ptime = samples_per_frame * 1000 / clock_rate / channel_count;
115
 
    if (max_delay < b->ptime)
116
 
        max_delay = PJ_MAX(DEFAULT_MAX_DELAY, b->ptime);
117
 
 
118
 
    b->max_cnt = samples_per_frame * max_delay / b->ptime;
119
 
    b->eff_cnt = b->max_cnt >> 1;
120
 
    b->recalc_timer = RECALC_TIME;
121
 
 
122
 
    /* Create circular buffer */
123
 
    status = pjmedia_circ_buf_create(pool, b->max_cnt, &b->circ_buf);
124
 
    if (status != PJ_SUCCESS)
125
 
        return status;
126
 
 
127
 
    if (!(options & PJMEDIA_DELAY_BUF_SIMPLE_FIFO)) {
128
 
        /* Create WSOLA */
129
 
        status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1,
130
 
                                      PJMEDIA_WSOLA_NO_FADING, &b->wsola);
131
 
        if (status != PJ_SUCCESS)
132
 
            return status;
133
 
        PJ_LOG(5, (b->obj_name, "Using delay buffer with WSOLA."));
134
 
    } else {
135
 
        PJ_LOG(5, (b->obj_name, "Using simple FIFO delay buffer."));
136
 
    }
137
 
 
138
 
    /* Finally, create mutex */
139
 
    status = pj_lock_create_recursive_mutex(pool, b->obj_name,
140
 
                                            &b->lock);
141
 
    if (status != PJ_SUCCESS)
142
 
        return status;
143
 
 
144
 
    *p_b = b;
145
 
 
146
 
    TRACE__((b->obj_name,"Delay buffer created"));
147
 
 
148
 
    return PJ_SUCCESS;
149
 
}
150
 
 
151
 
PJ_DEF(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b)
152
 
{
153
 
    pj_status_t status = PJ_SUCCESS;
154
 
 
155
 
    PJ_ASSERT_RETURN(b, PJ_EINVAL);
156
 
 
157
 
    pj_lock_acquire(b->lock);
158
 
 
159
 
    if (b->wsola) {
160
 
        status = pjmedia_wsola_destroy(b->wsola);
161
 
        if (status == PJ_SUCCESS)
162
 
            b->wsola = NULL;
163
 
    }
164
 
 
165
 
    pj_lock_release(b->lock);
166
 
 
167
 
    pj_lock_destroy(b->lock);
168
 
    b->lock = NULL;
169
 
 
170
 
    return status;
171
 
}
172
 
 
173
 
/* This function will erase samples from delay buffer.
174
 
 * The number of erased samples is guaranteed to be >= erase_cnt.
175
 
 */
176
 
static void shrink_buffer(pjmedia_delay_buf *b, unsigned erase_cnt)
177
 
{
178
 
    pj_int16_t *buf1, *buf2;
179
 
    unsigned buf1len;
180
 
    unsigned buf2len;
181
 
    pj_status_t status;
182
 
 
183
 
    pj_assert(b && erase_cnt && pjmedia_circ_buf_get_len(b->circ_buf));
184
 
 
185
 
    pjmedia_circ_buf_get_read_regions(b->circ_buf, &buf1, &buf1len,
186
 
                                      &buf2, &buf2len);
187
 
    status = pjmedia_wsola_discard(b->wsola, buf1, buf1len, buf2, buf2len,
188
 
                                   &erase_cnt);
189
 
 
190
 
    if ((status == PJ_SUCCESS) && (erase_cnt > 0)) {
191
 
        /* WSOLA discard will manage the first buffer to be full, unless
192
 
         * erase_cnt is greater than second buffer length. So it is safe
193
 
         * to just set the circular buffer length.
194
 
         */
195
 
 
196
 
        pjmedia_circ_buf_set_len(b->circ_buf,
197
 
                                 pjmedia_circ_buf_get_len(b->circ_buf) -
198
 
                                 erase_cnt);
199
 
 
200
 
        PJ_LOG(5,(b->obj_name,"%d samples reduced, buf_cnt=%d",
201
 
               erase_cnt, pjmedia_circ_buf_get_len(b->circ_buf)));
202
 
    }
203
 
}
204
 
 
205
 
/* Fast increase, slow decrease */
206
 
#define AGC_UP(cur, target) cur = (cur + target*3) >> 2
207
 
#define AGC_DOWN(cur, target) cur = (cur*3 + target) >> 2
208
 
#define AGC(cur, target) \
209
 
    if (cur < target) AGC_UP(cur, target); \
210
 
    else AGC_DOWN(cur, target)
211
 
 
212
 
static void update(pjmedia_delay_buf *b, enum OP op)
213
 
{
214
 
    /* Sequential operation */
215
 
    if (op == b->last_op) {
216
 
        ++b->level;
217
 
        return;
218
 
    }
219
 
 
220
 
    /* Switching operation */
221
 
    if (b->level > b->max_level)
222
 
        b->max_level = b->level;
223
 
 
224
 
    b->recalc_timer -= (b->level * b->ptime) >> 1;
225
 
 
226
 
    b->last_op = op;
227
 
    b->level = 1;
228
 
 
229
 
    /* Recalculate effective count based on max_level */
230
 
    if (b->recalc_timer <= 0) {
231
 
        unsigned new_eff_cnt = (b->max_level+SAFE_MARGIN)*b->samples_per_frame;
232
 
 
233
 
        /* Smoothening effective count transition */
234
 
        AGC(b->eff_cnt, new_eff_cnt);
235
 
 
236
 
        /* Make sure the new effective count is multiplication of
237
 
         * channel_count, so let's round it up.
238
 
         */
239
 
        if (b->eff_cnt % b->channel_count)
240
 
            b->eff_cnt += b->channel_count - (b->eff_cnt % b->channel_count);
241
 
 
242
 
        TRACE__((b->obj_name,"Cur eff_cnt=%d", b->eff_cnt));
243
 
 
244
 
        b->max_level = 0;
245
 
        b->recalc_timer = RECALC_TIME;
246
 
    }
247
 
 
248
 
    /* See if we need to shrink the buffer to reduce delay */
249
 
    if (op == OP_PUT && pjmedia_circ_buf_get_len(b->circ_buf) >
250
 
        b->samples_per_frame + b->eff_cnt)
251
 
    {
252
 
        unsigned erase_cnt = b->samples_per_frame >> 1;
253
 
        unsigned old_buf_cnt = pjmedia_circ_buf_get_len(b->circ_buf);
254
 
 
255
 
        shrink_buffer(b, erase_cnt);
256
 
        PJ_LOG(4,(b->obj_name,"Buffer size adjusted from %d to %d (eff_cnt=%d)",
257
 
                  old_buf_cnt,
258
 
                  pjmedia_circ_buf_get_len(b->circ_buf),
259
 
                  b->eff_cnt));
260
 
    }
261
 
}
262
 
 
263
 
PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
264
 
                                           pj_int16_t frame[])
265
 
{
266
 
    pj_status_t status;
267
 
 
268
 
    PJ_ASSERT_RETURN(b && frame, PJ_EINVAL);
269
 
 
270
 
    pj_lock_acquire(b->lock);
271
 
 
272
 
    if (b->wsola) {
273
 
        update(b, OP_PUT);
274
 
 
275
 
        status = pjmedia_wsola_save(b->wsola, frame, PJ_FALSE);
276
 
        if (status != PJ_SUCCESS) {
277
 
            pj_lock_release(b->lock);
278
 
            return status;
279
 
        }
280
 
    }
281
 
 
282
 
    /* Overflow checking */
283
 
    if (pjmedia_circ_buf_get_len(b->circ_buf) + b->samples_per_frame >
284
 
        b->max_cnt)
285
 
    {
286
 
        unsigned erase_cnt;
287
 
 
288
 
        if (b->wsola) {
289
 
            /* shrink one frame or just the diff? */
290
 
            //erase_cnt = b->samples_per_frame;
291
 
            erase_cnt = pjmedia_circ_buf_get_len(b->circ_buf) +
292
 
                        b->samples_per_frame - b->max_cnt;
293
 
 
294
 
            shrink_buffer(b, erase_cnt);
295
 
        }
296
 
 
297
 
        /* Check if shrinking failed or erased count is less than requested,
298
 
         * delaybuf needs to drop eldest samples, this is bad since the voice
299
 
         * samples get rough transition which may produce tick noise.
300
 
         */
301
 
        if (pjmedia_circ_buf_get_len(b->circ_buf) + b->samples_per_frame >
302
 
            b->max_cnt)
303
 
        {
304
 
            erase_cnt = pjmedia_circ_buf_get_len(b->circ_buf) +
305
 
                        b->samples_per_frame - b->max_cnt;
306
 
 
307
 
            pjmedia_circ_buf_adv_read_ptr(b->circ_buf, erase_cnt);
308
 
 
309
 
            PJ_LOG(4,(b->obj_name,"%sDropping %d eldest samples, buf_cnt=%d",
310
 
                      (b->wsola? "Shrinking failed or insufficient. ": ""),
311
 
                      erase_cnt, pjmedia_circ_buf_get_len(b->circ_buf)));
312
 
        }
313
 
    }
314
 
 
315
 
    pjmedia_circ_buf_write(b->circ_buf, frame, b->samples_per_frame);
316
 
 
317
 
    pj_lock_release(b->lock);
318
 
    return PJ_SUCCESS;
319
 
}
320
 
 
321
 
PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
322
 
                                           pj_int16_t frame[])
323
 
{
324
 
    pj_status_t status = PJ_SUCCESS;
325
 
 
326
 
    PJ_ASSERT_RETURN(b && frame, PJ_EINVAL);
327
 
 
328
 
    pj_lock_acquire(b->lock);
329
 
 
330
 
    if (b->wsola)
331
 
        update(b, OP_GET);
332
 
 
333
 
    /* Starvation checking */
334
 
    if (pjmedia_circ_buf_get_len(b->circ_buf) < b->samples_per_frame) {
335
 
 
336
 
        PJ_LOG(4,(b->obj_name,"Underflow, buf_cnt=%d, will generate 1 frame",
337
 
                  pjmedia_circ_buf_get_len(b->circ_buf)));
338
 
 
339
 
        if (b->wsola) {
340
 
            status = pjmedia_wsola_generate(b->wsola, frame);
341
 
 
342
 
            if (status == PJ_SUCCESS) {
343
 
                TRACE__((b->obj_name,"Successfully generate 1 frame"));
344
 
                if (pjmedia_circ_buf_get_len(b->circ_buf) == 0) {
345
 
                    pj_lock_release(b->lock);
346
 
                    return PJ_SUCCESS;
347
 
                }
348
 
 
349
 
                /* Put generated frame into buffer */
350
 
                pjmedia_circ_buf_write(b->circ_buf, frame,
351
 
                                       b->samples_per_frame);
352
 
            }
353
 
        }
354
 
 
355
 
        if (!b->wsola || status != PJ_SUCCESS) {
356
 
            unsigned buf_len = pjmedia_circ_buf_get_len(b->circ_buf);
357
 
 
358
 
            /* Give all what delay buffer has, then pad with zeroes */
359
 
            if (b->wsola)
360
 
                PJ_LOG(4,(b->obj_name,"Error generating frame, status=%d",
361
 
                          status));
362
 
 
363
 
            pjmedia_circ_buf_read(b->circ_buf, frame, buf_len);
364
 
            pjmedia_zero_samples(&frame[buf_len],
365
 
                                 b->samples_per_frame - buf_len);
366
 
 
367
 
            /* The buffer is empty now, reset it */
368
 
            pjmedia_circ_buf_reset(b->circ_buf);
369
 
 
370
 
            pj_lock_release(b->lock);
371
 
 
372
 
            return PJ_SUCCESS;
373
 
        }
374
 
    }
375
 
 
376
 
    pjmedia_circ_buf_read(b->circ_buf, frame, b->samples_per_frame);
377
 
 
378
 
    pj_lock_release(b->lock);
379
 
 
380
 
    return PJ_SUCCESS;
381
 
}
382
 
 
383
 
 
384
 
PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b)
385
 
{
386
 
    PJ_ASSERT_RETURN(b, PJ_EINVAL);
387
 
 
388
 
    pj_lock_acquire(b->lock);
389
 
 
390
 
    b->recalc_timer = RECALC_TIME;
391
 
 
392
 
    /* Reset buffer */
393
 
    pjmedia_circ_buf_reset(b->circ_buf);
394
 
 
395
 
    /* Reset WSOLA */
396
 
    if (b->wsola)
397
 
        pjmedia_wsola_reset(b->wsola, 0);
398
 
 
399
 
    pj_lock_release(b->lock);
400
 
 
401
 
    PJ_LOG(5,(b->obj_name,"Delay buffer is reset"));
402
 
 
403
 
    return PJ_SUCCESS;
404
 
}