1*28e138c6SAndroid Build Coastguard Worker /* Copyright (C) 2002 Jean-Marc Valin
2*28e138c6SAndroid Build Coastguard Worker File: speex_jitter.h
3*28e138c6SAndroid Build Coastguard Worker
4*28e138c6SAndroid Build Coastguard Worker Adaptive jitter buffer for Speex
5*28e138c6SAndroid Build Coastguard Worker
6*28e138c6SAndroid Build Coastguard Worker Redistribution and use in source and binary forms, with or without
7*28e138c6SAndroid Build Coastguard Worker modification, are permitted provided that the following conditions
8*28e138c6SAndroid Build Coastguard Worker are met:
9*28e138c6SAndroid Build Coastguard Worker
10*28e138c6SAndroid Build Coastguard Worker - Redistributions of source code must retain the above copyright
11*28e138c6SAndroid Build Coastguard Worker notice, this list of conditions and the following disclaimer.
12*28e138c6SAndroid Build Coastguard Worker
13*28e138c6SAndroid Build Coastguard Worker - Redistributions in binary form must reproduce the above copyright
14*28e138c6SAndroid Build Coastguard Worker notice, this list of conditions and the following disclaimer in the
15*28e138c6SAndroid Build Coastguard Worker documentation and/or other materials provided with the distribution.
16*28e138c6SAndroid Build Coastguard Worker
17*28e138c6SAndroid Build Coastguard Worker - Neither the name of the Xiph.org Foundation nor the names of its
18*28e138c6SAndroid Build Coastguard Worker contributors may be used to endorse or promote products derived from
19*28e138c6SAndroid Build Coastguard Worker this software without specific prior written permission.
20*28e138c6SAndroid Build Coastguard Worker
21*28e138c6SAndroid Build Coastguard Worker THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*28e138c6SAndroid Build Coastguard Worker ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*28e138c6SAndroid Build Coastguard Worker LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24*28e138c6SAndroid Build Coastguard Worker A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
25*28e138c6SAndroid Build Coastguard Worker CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26*28e138c6SAndroid Build Coastguard Worker EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27*28e138c6SAndroid Build Coastguard Worker PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28*28e138c6SAndroid Build Coastguard Worker PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29*28e138c6SAndroid Build Coastguard Worker LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30*28e138c6SAndroid Build Coastguard Worker NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31*28e138c6SAndroid Build Coastguard Worker SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*28e138c6SAndroid Build Coastguard Worker
33*28e138c6SAndroid Build Coastguard Worker */
34*28e138c6SAndroid Build Coastguard Worker
35*28e138c6SAndroid Build Coastguard Worker /*
36*28e138c6SAndroid Build Coastguard Worker TODO:
37*28e138c6SAndroid Build Coastguard Worker - Add short-term estimate
38*28e138c6SAndroid Build Coastguard Worker - Defensive programming
39*28e138c6SAndroid Build Coastguard Worker + warn when last returned < last desired (begative buffering)
40*28e138c6SAndroid Build Coastguard Worker + warn if update_delay not called between get() and tick() or is called twice in a row
41*28e138c6SAndroid Build Coastguard Worker - Linked list structure for holding the packets instead of the current fixed-size array
42*28e138c6SAndroid Build Coastguard Worker + return memory to a pool
43*28e138c6SAndroid Build Coastguard Worker + allow pre-allocation of the pool
44*28e138c6SAndroid Build Coastguard Worker + optional max number of elements
45*28e138c6SAndroid Build Coastguard Worker - Statistics
46*28e138c6SAndroid Build Coastguard Worker + drift
47*28e138c6SAndroid Build Coastguard Worker + loss
48*28e138c6SAndroid Build Coastguard Worker + late
49*28e138c6SAndroid Build Coastguard Worker + jitter
50*28e138c6SAndroid Build Coastguard Worker + buffering delay
51*28e138c6SAndroid Build Coastguard Worker */
52*28e138c6SAndroid Build Coastguard Worker #ifdef HAVE_CONFIG_H
53*28e138c6SAndroid Build Coastguard Worker #include "config.h"
54*28e138c6SAndroid Build Coastguard Worker #endif
55*28e138c6SAndroid Build Coastguard Worker
56*28e138c6SAndroid Build Coastguard Worker
57*28e138c6SAndroid Build Coastguard Worker #include "arch.h"
58*28e138c6SAndroid Build Coastguard Worker #include "speex/speex_jitter.h"
59*28e138c6SAndroid Build Coastguard Worker #include "os_support.h"
60*28e138c6SAndroid Build Coastguard Worker
61*28e138c6SAndroid Build Coastguard Worker #ifndef NULL
62*28e138c6SAndroid Build Coastguard Worker #define NULL 0
63*28e138c6SAndroid Build Coastguard Worker #endif
64*28e138c6SAndroid Build Coastguard Worker
65*28e138c6SAndroid Build Coastguard Worker #define SPEEX_JITTER_MAX_BUFFER_SIZE 200 /**< Maximum number of packets in jitter buffer */
66*28e138c6SAndroid Build Coastguard Worker
67*28e138c6SAndroid Build Coastguard Worker #define TSUB(a,b) ((spx_int32_t)((a)-(b)))
68*28e138c6SAndroid Build Coastguard Worker
69*28e138c6SAndroid Build Coastguard Worker #define GT32(a,b) (((spx_int32_t)((a)-(b)))>0)
70*28e138c6SAndroid Build Coastguard Worker #define GE32(a,b) (((spx_int32_t)((a)-(b)))>=0)
71*28e138c6SAndroid Build Coastguard Worker #define LT32(a,b) (((spx_int32_t)((a)-(b)))<0)
72*28e138c6SAndroid Build Coastguard Worker #define LE32(a,b) (((spx_int32_t)((a)-(b)))<=0)
73*28e138c6SAndroid Build Coastguard Worker
74*28e138c6SAndroid Build Coastguard Worker #define ROUND_DOWN(x, step) ((x)<0 ? ((x)-(step)+1)/(step)*(step) : (x)/(step)*(step))
75*28e138c6SAndroid Build Coastguard Worker
76*28e138c6SAndroid Build Coastguard Worker #define MAX_TIMINGS 40
77*28e138c6SAndroid Build Coastguard Worker #define MAX_BUFFERS 3
78*28e138c6SAndroid Build Coastguard Worker #define TOP_DELAY 40
79*28e138c6SAndroid Build Coastguard Worker
80*28e138c6SAndroid Build Coastguard Worker /** Buffer that keeps the time of arrival of the latest packets */
81*28e138c6SAndroid Build Coastguard Worker struct TimingBuffer {
82*28e138c6SAndroid Build Coastguard Worker int filled; /**< Number of entries occupied in "timing" and "counts"*/
83*28e138c6SAndroid Build Coastguard Worker int curr_count; /**< Number of packet timings we got (including those we discarded) */
84*28e138c6SAndroid Build Coastguard Worker spx_int32_t timing[MAX_TIMINGS]; /**< Sorted list of all timings ("latest" packets first) */
85*28e138c6SAndroid Build Coastguard Worker spx_int16_t counts[MAX_TIMINGS]; /**< Order the packets were put in (will be used for short-term estimate) */
86*28e138c6SAndroid Build Coastguard Worker };
87*28e138c6SAndroid Build Coastguard Worker
tb_init(struct TimingBuffer * tb)88*28e138c6SAndroid Build Coastguard Worker static void tb_init(struct TimingBuffer *tb)
89*28e138c6SAndroid Build Coastguard Worker {
90*28e138c6SAndroid Build Coastguard Worker tb->filled = 0;
91*28e138c6SAndroid Build Coastguard Worker tb->curr_count = 0;
92*28e138c6SAndroid Build Coastguard Worker }
93*28e138c6SAndroid Build Coastguard Worker
94*28e138c6SAndroid Build Coastguard Worker /* Add the timing of a new packet to the TimingBuffer */
tb_add(struct TimingBuffer * tb,spx_int16_t timing)95*28e138c6SAndroid Build Coastguard Worker static void tb_add(struct TimingBuffer *tb, spx_int16_t timing)
96*28e138c6SAndroid Build Coastguard Worker {
97*28e138c6SAndroid Build Coastguard Worker int pos;
98*28e138c6SAndroid Build Coastguard Worker /* Discard packet that won't make it into the list because they're too early */
99*28e138c6SAndroid Build Coastguard Worker if (tb->filled >= MAX_TIMINGS && timing >= tb->timing[tb->filled-1])
100*28e138c6SAndroid Build Coastguard Worker {
101*28e138c6SAndroid Build Coastguard Worker tb->curr_count++;
102*28e138c6SAndroid Build Coastguard Worker return;
103*28e138c6SAndroid Build Coastguard Worker }
104*28e138c6SAndroid Build Coastguard Worker
105*28e138c6SAndroid Build Coastguard Worker /* Find where the timing info goes in the sorted list */
106*28e138c6SAndroid Build Coastguard Worker pos = 0;
107*28e138c6SAndroid Build Coastguard Worker /* FIXME: Do bisection instead of linear search */
108*28e138c6SAndroid Build Coastguard Worker while (pos<tb->filled && timing >= tb->timing[pos])
109*28e138c6SAndroid Build Coastguard Worker {
110*28e138c6SAndroid Build Coastguard Worker pos++;
111*28e138c6SAndroid Build Coastguard Worker }
112*28e138c6SAndroid Build Coastguard Worker
113*28e138c6SAndroid Build Coastguard Worker speex_assert(pos <= tb->filled && pos < MAX_TIMINGS);
114*28e138c6SAndroid Build Coastguard Worker
115*28e138c6SAndroid Build Coastguard Worker /* Shift everything so we can perform the insertion */
116*28e138c6SAndroid Build Coastguard Worker if (pos < tb->filled)
117*28e138c6SAndroid Build Coastguard Worker {
118*28e138c6SAndroid Build Coastguard Worker int move_size = tb->filled-pos;
119*28e138c6SAndroid Build Coastguard Worker if (tb->filled == MAX_TIMINGS)
120*28e138c6SAndroid Build Coastguard Worker move_size -= 1;
121*28e138c6SAndroid Build Coastguard Worker SPEEX_MOVE(&tb->timing[pos+1], &tb->timing[pos], move_size);
122*28e138c6SAndroid Build Coastguard Worker SPEEX_MOVE(&tb->counts[pos+1], &tb->counts[pos], move_size);
123*28e138c6SAndroid Build Coastguard Worker }
124*28e138c6SAndroid Build Coastguard Worker /* Insert */
125*28e138c6SAndroid Build Coastguard Worker tb->timing[pos] = timing;
126*28e138c6SAndroid Build Coastguard Worker tb->counts[pos] = tb->curr_count;
127*28e138c6SAndroid Build Coastguard Worker
128*28e138c6SAndroid Build Coastguard Worker tb->curr_count++;
129*28e138c6SAndroid Build Coastguard Worker if (tb->filled<MAX_TIMINGS)
130*28e138c6SAndroid Build Coastguard Worker tb->filled++;
131*28e138c6SAndroid Build Coastguard Worker }
132*28e138c6SAndroid Build Coastguard Worker
133*28e138c6SAndroid Build Coastguard Worker
134*28e138c6SAndroid Build Coastguard Worker
135*28e138c6SAndroid Build Coastguard Worker /** Jitter buffer structure */
136*28e138c6SAndroid Build Coastguard Worker struct JitterBuffer_ {
137*28e138c6SAndroid Build Coastguard Worker spx_uint32_t pointer_timestamp; /**< Timestamp of what we will *get* next */
138*28e138c6SAndroid Build Coastguard Worker spx_uint32_t last_returned_timestamp; /**< Useful for getting the next packet with the same timestamp (for fragmented media) */
139*28e138c6SAndroid Build Coastguard Worker spx_uint32_t next_stop; /**< Estimated time the next get() will be called */
140*28e138c6SAndroid Build Coastguard Worker
141*28e138c6SAndroid Build Coastguard Worker spx_int32_t buffered; /**< Amount of data we think is still buffered by the application (timestamp units)*/
142*28e138c6SAndroid Build Coastguard Worker
143*28e138c6SAndroid Build Coastguard Worker JitterBufferPacket packets[SPEEX_JITTER_MAX_BUFFER_SIZE]; /**< Packets stored in the buffer */
144*28e138c6SAndroid Build Coastguard Worker spx_uint32_t arrival[SPEEX_JITTER_MAX_BUFFER_SIZE]; /**< Packet arrival time (0 means it was late, even though it's a valid timestamp) */
145*28e138c6SAndroid Build Coastguard Worker
146*28e138c6SAndroid Build Coastguard Worker void (*destroy) (void *); /**< Callback for destroying a packet */
147*28e138c6SAndroid Build Coastguard Worker
148*28e138c6SAndroid Build Coastguard Worker spx_int32_t delay_step; /**< Size of the steps when adjusting buffering (timestamp units) */
149*28e138c6SAndroid Build Coastguard Worker spx_int32_t concealment_size; /**< Size of the packet loss concealment "units" */
150*28e138c6SAndroid Build Coastguard Worker int reset_state; /**< True if state was just reset */
151*28e138c6SAndroid Build Coastguard Worker int buffer_margin; /**< How many frames we want to keep in the buffer (lower bound) */
152*28e138c6SAndroid Build Coastguard Worker int late_cutoff; /**< How late must a packet be for it not to be considered at all */
153*28e138c6SAndroid Build Coastguard Worker int interp_requested; /**< An interpolation is requested by speex_jitter_update_delay() */
154*28e138c6SAndroid Build Coastguard Worker int auto_adjust; /**< Whether to automatically adjust the delay at any time */
155*28e138c6SAndroid Build Coastguard Worker
156*28e138c6SAndroid Build Coastguard Worker struct TimingBuffer _tb[MAX_BUFFERS]; /**< Don't use those directly */
157*28e138c6SAndroid Build Coastguard Worker struct TimingBuffer *timeBuffers[MAX_BUFFERS]; /**< Storing arrival time of latest frames so we can compute some stats */
158*28e138c6SAndroid Build Coastguard Worker int window_size; /**< Total window over which the late frames are counted */
159*28e138c6SAndroid Build Coastguard Worker int subwindow_size; /**< Sub-window size for faster computation */
160*28e138c6SAndroid Build Coastguard Worker int max_late_rate; /**< Absolute maximum amount of late packets tolerable (in percent) */
161*28e138c6SAndroid Build Coastguard Worker int latency_tradeoff; /**< Latency equivalent of losing one percent of packets */
162*28e138c6SAndroid Build Coastguard Worker int auto_tradeoff; /**< Latency equivalent of losing one percent of packets (automatic default) */
163*28e138c6SAndroid Build Coastguard Worker
164*28e138c6SAndroid Build Coastguard Worker int lost_count; /**< Number of consecutive lost packets */
165*28e138c6SAndroid Build Coastguard Worker };
166*28e138c6SAndroid Build Coastguard Worker
167*28e138c6SAndroid Build Coastguard Worker /** Based on available data, this computes the optimal delay for the jitter buffer.
168*28e138c6SAndroid Build Coastguard Worker The optimised function is in timestamp units and is:
169*28e138c6SAndroid Build Coastguard Worker cost = delay + late_factor*[number of frames that would be late if we used that delay]
170*28e138c6SAndroid Build Coastguard Worker @param tb Array of buffers
171*28e138c6SAndroid Build Coastguard Worker @param late_factor Equivalent cost of a late frame (in timestamp units)
172*28e138c6SAndroid Build Coastguard Worker */
compute_opt_delay(JitterBuffer * jitter)173*28e138c6SAndroid Build Coastguard Worker static spx_int16_t compute_opt_delay(JitterBuffer *jitter)
174*28e138c6SAndroid Build Coastguard Worker {
175*28e138c6SAndroid Build Coastguard Worker int i;
176*28e138c6SAndroid Build Coastguard Worker spx_int16_t opt=0;
177*28e138c6SAndroid Build Coastguard Worker spx_int32_t best_cost=0x7fffffff;
178*28e138c6SAndroid Build Coastguard Worker int late = 0;
179*28e138c6SAndroid Build Coastguard Worker int pos[MAX_BUFFERS];
180*28e138c6SAndroid Build Coastguard Worker int tot_count;
181*28e138c6SAndroid Build Coastguard Worker float late_factor;
182*28e138c6SAndroid Build Coastguard Worker int penalty_taken = 0;
183*28e138c6SAndroid Build Coastguard Worker int best = 0;
184*28e138c6SAndroid Build Coastguard Worker int worst = 0;
185*28e138c6SAndroid Build Coastguard Worker spx_int32_t deltaT;
186*28e138c6SAndroid Build Coastguard Worker struct TimingBuffer *tb;
187*28e138c6SAndroid Build Coastguard Worker
188*28e138c6SAndroid Build Coastguard Worker tb = jitter->_tb;
189*28e138c6SAndroid Build Coastguard Worker
190*28e138c6SAndroid Build Coastguard Worker /* Number of packet timings we have received (including those we didn't keep) */
191*28e138c6SAndroid Build Coastguard Worker tot_count = 0;
192*28e138c6SAndroid Build Coastguard Worker for (i=0;i<MAX_BUFFERS;i++)
193*28e138c6SAndroid Build Coastguard Worker tot_count += tb[i].curr_count;
194*28e138c6SAndroid Build Coastguard Worker if (tot_count==0)
195*28e138c6SAndroid Build Coastguard Worker return 0;
196*28e138c6SAndroid Build Coastguard Worker
197*28e138c6SAndroid Build Coastguard Worker /* Compute cost for one lost packet */
198*28e138c6SAndroid Build Coastguard Worker if (jitter->latency_tradeoff != 0)
199*28e138c6SAndroid Build Coastguard Worker late_factor = jitter->latency_tradeoff * 100.0f / tot_count;
200*28e138c6SAndroid Build Coastguard Worker else
201*28e138c6SAndroid Build Coastguard Worker late_factor = jitter->auto_tradeoff * jitter->window_size/tot_count;
202*28e138c6SAndroid Build Coastguard Worker
203*28e138c6SAndroid Build Coastguard Worker /*fprintf(stderr, "late_factor = %f\n", late_factor);*/
204*28e138c6SAndroid Build Coastguard Worker for (i=0;i<MAX_BUFFERS;i++)
205*28e138c6SAndroid Build Coastguard Worker pos[i] = 0;
206*28e138c6SAndroid Build Coastguard Worker
207*28e138c6SAndroid Build Coastguard Worker /* Pick the TOP_DELAY "latest" packets (doesn't need to actually be late
208*28e138c6SAndroid Build Coastguard Worker for the current settings) */
209*28e138c6SAndroid Build Coastguard Worker for (i=0;i<TOP_DELAY;i++)
210*28e138c6SAndroid Build Coastguard Worker {
211*28e138c6SAndroid Build Coastguard Worker int j;
212*28e138c6SAndroid Build Coastguard Worker int next=-1;
213*28e138c6SAndroid Build Coastguard Worker int latest = 32767;
214*28e138c6SAndroid Build Coastguard Worker /* Pick latest amoung all sub-windows */
215*28e138c6SAndroid Build Coastguard Worker for (j=0;j<MAX_BUFFERS;j++)
216*28e138c6SAndroid Build Coastguard Worker {
217*28e138c6SAndroid Build Coastguard Worker if (pos[j] < tb[j].filled && tb[j].timing[pos[j]] < latest)
218*28e138c6SAndroid Build Coastguard Worker {
219*28e138c6SAndroid Build Coastguard Worker next = j;
220*28e138c6SAndroid Build Coastguard Worker latest = tb[j].timing[pos[j]];
221*28e138c6SAndroid Build Coastguard Worker }
222*28e138c6SAndroid Build Coastguard Worker }
223*28e138c6SAndroid Build Coastguard Worker if (next != -1)
224*28e138c6SAndroid Build Coastguard Worker {
225*28e138c6SAndroid Build Coastguard Worker spx_int32_t cost;
226*28e138c6SAndroid Build Coastguard Worker
227*28e138c6SAndroid Build Coastguard Worker if (i==0)
228*28e138c6SAndroid Build Coastguard Worker worst = latest;
229*28e138c6SAndroid Build Coastguard Worker best = latest;
230*28e138c6SAndroid Build Coastguard Worker latest = ROUND_DOWN(latest, jitter->delay_step);
231*28e138c6SAndroid Build Coastguard Worker pos[next]++;
232*28e138c6SAndroid Build Coastguard Worker
233*28e138c6SAndroid Build Coastguard Worker /* Actual cost function that tells us how bad using this delay would be */
234*28e138c6SAndroid Build Coastguard Worker cost = -latest + late_factor*late;
235*28e138c6SAndroid Build Coastguard Worker /*fprintf(stderr, "cost %d = %d + %f * %d\n", cost, -latest, late_factor, late);*/
236*28e138c6SAndroid Build Coastguard Worker if (cost < best_cost)
237*28e138c6SAndroid Build Coastguard Worker {
238*28e138c6SAndroid Build Coastguard Worker best_cost = cost;
239*28e138c6SAndroid Build Coastguard Worker opt = latest;
240*28e138c6SAndroid Build Coastguard Worker }
241*28e138c6SAndroid Build Coastguard Worker } else {
242*28e138c6SAndroid Build Coastguard Worker break;
243*28e138c6SAndroid Build Coastguard Worker }
244*28e138c6SAndroid Build Coastguard Worker
245*28e138c6SAndroid Build Coastguard Worker /* For the next timing we will consider, there will be one more late packet to count */
246*28e138c6SAndroid Build Coastguard Worker late++;
247*28e138c6SAndroid Build Coastguard Worker /* Two-frame penalty if we're going to increase the amount of late frames (hysteresis) */
248*28e138c6SAndroid Build Coastguard Worker if (latest >= 0 && !penalty_taken)
249*28e138c6SAndroid Build Coastguard Worker {
250*28e138c6SAndroid Build Coastguard Worker penalty_taken = 1;
251*28e138c6SAndroid Build Coastguard Worker late+=4;
252*28e138c6SAndroid Build Coastguard Worker }
253*28e138c6SAndroid Build Coastguard Worker }
254*28e138c6SAndroid Build Coastguard Worker
255*28e138c6SAndroid Build Coastguard Worker deltaT = best-worst;
256*28e138c6SAndroid Build Coastguard Worker /* This is a default "automatic latency tradeoff" when none is provided */
257*28e138c6SAndroid Build Coastguard Worker jitter->auto_tradeoff = 1 + deltaT/TOP_DELAY;
258*28e138c6SAndroid Build Coastguard Worker /*fprintf(stderr, "auto_tradeoff = %d (%d %d %d)\n", jitter->auto_tradeoff, best, worst, i);*/
259*28e138c6SAndroid Build Coastguard Worker
260*28e138c6SAndroid Build Coastguard Worker /* FIXME: Compute a short-term estimate too and combine with the long-term one */
261*28e138c6SAndroid Build Coastguard Worker
262*28e138c6SAndroid Build Coastguard Worker /* Prevents reducing the buffer size when we haven't really had much data */
263*28e138c6SAndroid Build Coastguard Worker if (tot_count < TOP_DELAY && opt > 0)
264*28e138c6SAndroid Build Coastguard Worker return 0;
265*28e138c6SAndroid Build Coastguard Worker return opt;
266*28e138c6SAndroid Build Coastguard Worker }
267*28e138c6SAndroid Build Coastguard Worker
268*28e138c6SAndroid Build Coastguard Worker
269*28e138c6SAndroid Build Coastguard Worker /** Initialise jitter buffer */
jitter_buffer_init(int step_size)270*28e138c6SAndroid Build Coastguard Worker EXPORT JitterBuffer *jitter_buffer_init(int step_size)
271*28e138c6SAndroid Build Coastguard Worker {
272*28e138c6SAndroid Build Coastguard Worker JitterBuffer *jitter = (JitterBuffer*)speex_alloc(sizeof(JitterBuffer));
273*28e138c6SAndroid Build Coastguard Worker if (jitter)
274*28e138c6SAndroid Build Coastguard Worker {
275*28e138c6SAndroid Build Coastguard Worker int i;
276*28e138c6SAndroid Build Coastguard Worker spx_int32_t tmp;
277*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
278*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].data=NULL;
279*28e138c6SAndroid Build Coastguard Worker jitter->delay_step = step_size;
280*28e138c6SAndroid Build Coastguard Worker jitter->concealment_size = step_size;
281*28e138c6SAndroid Build Coastguard Worker /*FIXME: Should this be 0 or 1?*/
282*28e138c6SAndroid Build Coastguard Worker jitter->buffer_margin = 0;
283*28e138c6SAndroid Build Coastguard Worker jitter->late_cutoff = 50;
284*28e138c6SAndroid Build Coastguard Worker jitter->destroy = NULL;
285*28e138c6SAndroid Build Coastguard Worker jitter->latency_tradeoff = 0;
286*28e138c6SAndroid Build Coastguard Worker jitter->auto_adjust = 1;
287*28e138c6SAndroid Build Coastguard Worker tmp = 4;
288*28e138c6SAndroid Build Coastguard Worker jitter_buffer_ctl(jitter, JITTER_BUFFER_SET_MAX_LATE_RATE, &tmp);
289*28e138c6SAndroid Build Coastguard Worker jitter_buffer_reset(jitter);
290*28e138c6SAndroid Build Coastguard Worker }
291*28e138c6SAndroid Build Coastguard Worker return jitter;
292*28e138c6SAndroid Build Coastguard Worker }
293*28e138c6SAndroid Build Coastguard Worker
294*28e138c6SAndroid Build Coastguard Worker /** Reset jitter buffer */
jitter_buffer_reset(JitterBuffer * jitter)295*28e138c6SAndroid Build Coastguard Worker EXPORT void jitter_buffer_reset(JitterBuffer *jitter)
296*28e138c6SAndroid Build Coastguard Worker {
297*28e138c6SAndroid Build Coastguard Worker int i;
298*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
299*28e138c6SAndroid Build Coastguard Worker {
300*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data)
301*28e138c6SAndroid Build Coastguard Worker {
302*28e138c6SAndroid Build Coastguard Worker if (jitter->destroy)
303*28e138c6SAndroid Build Coastguard Worker jitter->destroy(jitter->packets[i].data);
304*28e138c6SAndroid Build Coastguard Worker else
305*28e138c6SAndroid Build Coastguard Worker speex_free(jitter->packets[i].data);
306*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].data = NULL;
307*28e138c6SAndroid Build Coastguard Worker }
308*28e138c6SAndroid Build Coastguard Worker }
309*28e138c6SAndroid Build Coastguard Worker /* Timestamp is actually undefined at this point */
310*28e138c6SAndroid Build Coastguard Worker jitter->pointer_timestamp = 0;
311*28e138c6SAndroid Build Coastguard Worker jitter->next_stop = 0;
312*28e138c6SAndroid Build Coastguard Worker jitter->reset_state = 1;
313*28e138c6SAndroid Build Coastguard Worker jitter->lost_count = 0;
314*28e138c6SAndroid Build Coastguard Worker jitter->buffered = 0;
315*28e138c6SAndroid Build Coastguard Worker jitter->auto_tradeoff = 32000;
316*28e138c6SAndroid Build Coastguard Worker
317*28e138c6SAndroid Build Coastguard Worker for (i=0;i<MAX_BUFFERS;i++)
318*28e138c6SAndroid Build Coastguard Worker {
319*28e138c6SAndroid Build Coastguard Worker tb_init(&jitter->_tb[i]);
320*28e138c6SAndroid Build Coastguard Worker jitter->timeBuffers[i] = &jitter->_tb[i];
321*28e138c6SAndroid Build Coastguard Worker }
322*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "reset\n");*/
323*28e138c6SAndroid Build Coastguard Worker }
324*28e138c6SAndroid Build Coastguard Worker
325*28e138c6SAndroid Build Coastguard Worker /** Destroy jitter buffer */
jitter_buffer_destroy(JitterBuffer * jitter)326*28e138c6SAndroid Build Coastguard Worker EXPORT void jitter_buffer_destroy(JitterBuffer *jitter)
327*28e138c6SAndroid Build Coastguard Worker {
328*28e138c6SAndroid Build Coastguard Worker jitter_buffer_reset(jitter);
329*28e138c6SAndroid Build Coastguard Worker speex_free(jitter);
330*28e138c6SAndroid Build Coastguard Worker }
331*28e138c6SAndroid Build Coastguard Worker
332*28e138c6SAndroid Build Coastguard Worker /** Take the following timing into consideration for future calculations */
update_timings(JitterBuffer * jitter,spx_int32_t timing)333*28e138c6SAndroid Build Coastguard Worker static void update_timings(JitterBuffer *jitter, spx_int32_t timing)
334*28e138c6SAndroid Build Coastguard Worker {
335*28e138c6SAndroid Build Coastguard Worker if (timing < -32767)
336*28e138c6SAndroid Build Coastguard Worker timing = -32767;
337*28e138c6SAndroid Build Coastguard Worker if (timing > 32767)
338*28e138c6SAndroid Build Coastguard Worker timing = 32767;
339*28e138c6SAndroid Build Coastguard Worker /* If the current sub-window is full, perform a rotation and discard oldest sub-widow */
340*28e138c6SAndroid Build Coastguard Worker if (jitter->timeBuffers[0]->curr_count >= jitter->subwindow_size)
341*28e138c6SAndroid Build Coastguard Worker {
342*28e138c6SAndroid Build Coastguard Worker int i;
343*28e138c6SAndroid Build Coastguard Worker /*fprintf(stderr, "Rotate buffer\n");*/
344*28e138c6SAndroid Build Coastguard Worker struct TimingBuffer *tmp = jitter->timeBuffers[MAX_BUFFERS-1];
345*28e138c6SAndroid Build Coastguard Worker for (i=MAX_BUFFERS-1;i>=1;i--)
346*28e138c6SAndroid Build Coastguard Worker jitter->timeBuffers[i] = jitter->timeBuffers[i-1];
347*28e138c6SAndroid Build Coastguard Worker jitter->timeBuffers[0] = tmp;
348*28e138c6SAndroid Build Coastguard Worker tb_init(jitter->timeBuffers[0]);
349*28e138c6SAndroid Build Coastguard Worker }
350*28e138c6SAndroid Build Coastguard Worker tb_add(jitter->timeBuffers[0], timing);
351*28e138c6SAndroid Build Coastguard Worker }
352*28e138c6SAndroid Build Coastguard Worker
353*28e138c6SAndroid Build Coastguard Worker /** Compensate all timings when we do an adjustment of the buffering */
shift_timings(JitterBuffer * jitter,spx_int16_t amount)354*28e138c6SAndroid Build Coastguard Worker static void shift_timings(JitterBuffer *jitter, spx_int16_t amount)
355*28e138c6SAndroid Build Coastguard Worker {
356*28e138c6SAndroid Build Coastguard Worker int i, j;
357*28e138c6SAndroid Build Coastguard Worker for (i=0;i<MAX_BUFFERS;i++)
358*28e138c6SAndroid Build Coastguard Worker {
359*28e138c6SAndroid Build Coastguard Worker for (j=0;j<jitter->timeBuffers[i]->filled;j++)
360*28e138c6SAndroid Build Coastguard Worker jitter->timeBuffers[i]->timing[j] += amount;
361*28e138c6SAndroid Build Coastguard Worker }
362*28e138c6SAndroid Build Coastguard Worker }
363*28e138c6SAndroid Build Coastguard Worker
364*28e138c6SAndroid Build Coastguard Worker
365*28e138c6SAndroid Build Coastguard Worker /** Put one packet into the jitter buffer */
jitter_buffer_put(JitterBuffer * jitter,const JitterBufferPacket * packet)366*28e138c6SAndroid Build Coastguard Worker EXPORT void jitter_buffer_put(JitterBuffer *jitter, const JitterBufferPacket *packet)
367*28e138c6SAndroid Build Coastguard Worker {
368*28e138c6SAndroid Build Coastguard Worker int i,j;
369*28e138c6SAndroid Build Coastguard Worker int late;
370*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "put packet %d %d\n", timestamp, span);*/
371*28e138c6SAndroid Build Coastguard Worker
372*28e138c6SAndroid Build Coastguard Worker /* Cleanup buffer (remove old packets that weren't played) */
373*28e138c6SAndroid Build Coastguard Worker if (!jitter->reset_state)
374*28e138c6SAndroid Build Coastguard Worker {
375*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
376*28e138c6SAndroid Build Coastguard Worker {
377*28e138c6SAndroid Build Coastguard Worker /* Make sure we don't discard a "just-late" packet in case we want to play it next (if we interpolate). */
378*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data && LE32(jitter->packets[i].timestamp + jitter->packets[i].span, jitter->pointer_timestamp))
379*28e138c6SAndroid Build Coastguard Worker {
380*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "cleaned (not played)\n");*/
381*28e138c6SAndroid Build Coastguard Worker if (jitter->destroy)
382*28e138c6SAndroid Build Coastguard Worker jitter->destroy(jitter->packets[i].data);
383*28e138c6SAndroid Build Coastguard Worker else
384*28e138c6SAndroid Build Coastguard Worker speex_free(jitter->packets[i].data);
385*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].data = NULL;
386*28e138c6SAndroid Build Coastguard Worker }
387*28e138c6SAndroid Build Coastguard Worker }
388*28e138c6SAndroid Build Coastguard Worker }
389*28e138c6SAndroid Build Coastguard Worker
390*28e138c6SAndroid Build Coastguard Worker /*fprintf(stderr, "arrival: %d %d %d\n", packet->timestamp, jitter->next_stop, jitter->pointer_timestamp);*/
391*28e138c6SAndroid Build Coastguard Worker /* Check if packet is late (could still be useful though) */
392*28e138c6SAndroid Build Coastguard Worker if (!jitter->reset_state && LT32(packet->timestamp, jitter->next_stop))
393*28e138c6SAndroid Build Coastguard Worker {
394*28e138c6SAndroid Build Coastguard Worker update_timings(jitter, ((spx_int32_t)packet->timestamp) - ((spx_int32_t)jitter->next_stop) - jitter->buffer_margin);
395*28e138c6SAndroid Build Coastguard Worker late = 1;
396*28e138c6SAndroid Build Coastguard Worker } else {
397*28e138c6SAndroid Build Coastguard Worker late = 0;
398*28e138c6SAndroid Build Coastguard Worker }
399*28e138c6SAndroid Build Coastguard Worker
400*28e138c6SAndroid Build Coastguard Worker /* For some reason, the consumer has failed the last 20 fetches. Make sure this packet is
401*28e138c6SAndroid Build Coastguard Worker * used to resync. */
402*28e138c6SAndroid Build Coastguard Worker if (jitter->lost_count>20)
403*28e138c6SAndroid Build Coastguard Worker {
404*28e138c6SAndroid Build Coastguard Worker jitter_buffer_reset(jitter);
405*28e138c6SAndroid Build Coastguard Worker }
406*28e138c6SAndroid Build Coastguard Worker
407*28e138c6SAndroid Build Coastguard Worker /* Only insert the packet if it's not hopelessly late (i.e. totally useless) */
408*28e138c6SAndroid Build Coastguard Worker if (jitter->reset_state || GE32(packet->timestamp+packet->span+jitter->delay_step, jitter->pointer_timestamp))
409*28e138c6SAndroid Build Coastguard Worker {
410*28e138c6SAndroid Build Coastguard Worker
411*28e138c6SAndroid Build Coastguard Worker /*Find an empty slot in the buffer*/
412*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
413*28e138c6SAndroid Build Coastguard Worker {
414*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data==NULL)
415*28e138c6SAndroid Build Coastguard Worker break;
416*28e138c6SAndroid Build Coastguard Worker }
417*28e138c6SAndroid Build Coastguard Worker
418*28e138c6SAndroid Build Coastguard Worker /*No place left in the buffer, need to make room for it by discarding the oldest packet */
419*28e138c6SAndroid Build Coastguard Worker if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
420*28e138c6SAndroid Build Coastguard Worker {
421*28e138c6SAndroid Build Coastguard Worker int earliest=jitter->packets[0].timestamp;
422*28e138c6SAndroid Build Coastguard Worker i=0;
423*28e138c6SAndroid Build Coastguard Worker for (j=1;j<SPEEX_JITTER_MAX_BUFFER_SIZE;j++)
424*28e138c6SAndroid Build Coastguard Worker {
425*28e138c6SAndroid Build Coastguard Worker if (!jitter->packets[i].data || LT32(jitter->packets[j].timestamp,earliest))
426*28e138c6SAndroid Build Coastguard Worker {
427*28e138c6SAndroid Build Coastguard Worker earliest = jitter->packets[j].timestamp;
428*28e138c6SAndroid Build Coastguard Worker i=j;
429*28e138c6SAndroid Build Coastguard Worker }
430*28e138c6SAndroid Build Coastguard Worker }
431*28e138c6SAndroid Build Coastguard Worker if (jitter->destroy)
432*28e138c6SAndroid Build Coastguard Worker jitter->destroy(jitter->packets[i].data);
433*28e138c6SAndroid Build Coastguard Worker else
434*28e138c6SAndroid Build Coastguard Worker speex_free(jitter->packets[i].data);
435*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].data=NULL;
436*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "Buffer is full, discarding earliest frame %d (currently at %d)\n", timestamp, jitter->pointer_timestamp);*/
437*28e138c6SAndroid Build Coastguard Worker }
438*28e138c6SAndroid Build Coastguard Worker
439*28e138c6SAndroid Build Coastguard Worker /* Copy packet in buffer */
440*28e138c6SAndroid Build Coastguard Worker if (jitter->destroy)
441*28e138c6SAndroid Build Coastguard Worker {
442*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].data = packet->data;
443*28e138c6SAndroid Build Coastguard Worker } else {
444*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].data=(char*)speex_alloc(packet->len);
445*28e138c6SAndroid Build Coastguard Worker for (j=0;j<packet->len;j++)
446*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].data[j]=packet->data[j];
447*28e138c6SAndroid Build Coastguard Worker }
448*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].timestamp=packet->timestamp;
449*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].span=packet->span;
450*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].len=packet->len;
451*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].sequence=packet->sequence;
452*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].user_data=packet->user_data;
453*28e138c6SAndroid Build Coastguard Worker if (jitter->reset_state || late)
454*28e138c6SAndroid Build Coastguard Worker jitter->arrival[i] = 0;
455*28e138c6SAndroid Build Coastguard Worker else
456*28e138c6SAndroid Build Coastguard Worker jitter->arrival[i] = jitter->next_stop;
457*28e138c6SAndroid Build Coastguard Worker }
458*28e138c6SAndroid Build Coastguard Worker
459*28e138c6SAndroid Build Coastguard Worker
460*28e138c6SAndroid Build Coastguard Worker }
461*28e138c6SAndroid Build Coastguard Worker
462*28e138c6SAndroid Build Coastguard Worker /** Get one packet from the jitter buffer */
jitter_buffer_get(JitterBuffer * jitter,JitterBufferPacket * packet,spx_int32_t desired_span,spx_int32_t * start_offset)463*28e138c6SAndroid Build Coastguard Worker EXPORT int jitter_buffer_get(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t desired_span, spx_int32_t *start_offset)
464*28e138c6SAndroid Build Coastguard Worker {
465*28e138c6SAndroid Build Coastguard Worker int i;
466*28e138c6SAndroid Build Coastguard Worker unsigned int j;
467*28e138c6SAndroid Build Coastguard Worker spx_int16_t opt;
468*28e138c6SAndroid Build Coastguard Worker
469*28e138c6SAndroid Build Coastguard Worker if (start_offset != NULL)
470*28e138c6SAndroid Build Coastguard Worker *start_offset = 0;
471*28e138c6SAndroid Build Coastguard Worker
472*28e138c6SAndroid Build Coastguard Worker /* Syncing on the first call */
473*28e138c6SAndroid Build Coastguard Worker if (jitter->reset_state)
474*28e138c6SAndroid Build Coastguard Worker {
475*28e138c6SAndroid Build Coastguard Worker int found = 0;
476*28e138c6SAndroid Build Coastguard Worker /* Find the oldest packet */
477*28e138c6SAndroid Build Coastguard Worker spx_uint32_t oldest=0;
478*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
479*28e138c6SAndroid Build Coastguard Worker {
480*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data && (!found || LT32(jitter->packets[i].timestamp,oldest)))
481*28e138c6SAndroid Build Coastguard Worker {
482*28e138c6SAndroid Build Coastguard Worker oldest = jitter->packets[i].timestamp;
483*28e138c6SAndroid Build Coastguard Worker found = 1;
484*28e138c6SAndroid Build Coastguard Worker }
485*28e138c6SAndroid Build Coastguard Worker }
486*28e138c6SAndroid Build Coastguard Worker if (found)
487*28e138c6SAndroid Build Coastguard Worker {
488*28e138c6SAndroid Build Coastguard Worker jitter->reset_state=0;
489*28e138c6SAndroid Build Coastguard Worker jitter->pointer_timestamp = oldest;
490*28e138c6SAndroid Build Coastguard Worker jitter->next_stop = oldest;
491*28e138c6SAndroid Build Coastguard Worker } else {
492*28e138c6SAndroid Build Coastguard Worker packet->timestamp = 0;
493*28e138c6SAndroid Build Coastguard Worker packet->span = jitter->interp_requested;
494*28e138c6SAndroid Build Coastguard Worker return JITTER_BUFFER_MISSING;
495*28e138c6SAndroid Build Coastguard Worker }
496*28e138c6SAndroid Build Coastguard Worker }
497*28e138c6SAndroid Build Coastguard Worker
498*28e138c6SAndroid Build Coastguard Worker
499*28e138c6SAndroid Build Coastguard Worker jitter->last_returned_timestamp = jitter->pointer_timestamp;
500*28e138c6SAndroid Build Coastguard Worker
501*28e138c6SAndroid Build Coastguard Worker if (jitter->interp_requested != 0)
502*28e138c6SAndroid Build Coastguard Worker {
503*28e138c6SAndroid Build Coastguard Worker packet->timestamp = jitter->pointer_timestamp;
504*28e138c6SAndroid Build Coastguard Worker packet->span = jitter->interp_requested;
505*28e138c6SAndroid Build Coastguard Worker
506*28e138c6SAndroid Build Coastguard Worker /* Increment the pointer because it got decremented in the delay update */
507*28e138c6SAndroid Build Coastguard Worker jitter->pointer_timestamp += jitter->interp_requested;
508*28e138c6SAndroid Build Coastguard Worker packet->len = 0;
509*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "Deferred interpolate\n");*/
510*28e138c6SAndroid Build Coastguard Worker
511*28e138c6SAndroid Build Coastguard Worker jitter->interp_requested = 0;
512*28e138c6SAndroid Build Coastguard Worker
513*28e138c6SAndroid Build Coastguard Worker jitter->buffered = packet->span - desired_span;
514*28e138c6SAndroid Build Coastguard Worker
515*28e138c6SAndroid Build Coastguard Worker return JITTER_BUFFER_INSERTION;
516*28e138c6SAndroid Build Coastguard Worker }
517*28e138c6SAndroid Build Coastguard Worker
518*28e138c6SAndroid Build Coastguard Worker /* Searching for the packet that fits best */
519*28e138c6SAndroid Build Coastguard Worker
520*28e138c6SAndroid Build Coastguard Worker /* Search the buffer for a packet with the right timestamp and spanning the whole current chunk */
521*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
522*28e138c6SAndroid Build Coastguard Worker {
523*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data && jitter->packets[i].timestamp==jitter->pointer_timestamp && GE32(jitter->packets[i].timestamp+jitter->packets[i].span,jitter->pointer_timestamp+desired_span))
524*28e138c6SAndroid Build Coastguard Worker break;
525*28e138c6SAndroid Build Coastguard Worker }
526*28e138c6SAndroid Build Coastguard Worker
527*28e138c6SAndroid Build Coastguard Worker /* If no match, try for an "older" packet that still spans (fully) the current chunk */
528*28e138c6SAndroid Build Coastguard Worker if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
529*28e138c6SAndroid Build Coastguard Worker {
530*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
531*28e138c6SAndroid Build Coastguard Worker {
532*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data && LE32(jitter->packets[i].timestamp, jitter->pointer_timestamp) && GE32(jitter->packets[i].timestamp+jitter->packets[i].span,jitter->pointer_timestamp+desired_span))
533*28e138c6SAndroid Build Coastguard Worker break;
534*28e138c6SAndroid Build Coastguard Worker }
535*28e138c6SAndroid Build Coastguard Worker }
536*28e138c6SAndroid Build Coastguard Worker
537*28e138c6SAndroid Build Coastguard Worker /* If still no match, try for an "older" packet that spans part of the current chunk */
538*28e138c6SAndroid Build Coastguard Worker if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
539*28e138c6SAndroid Build Coastguard Worker {
540*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
541*28e138c6SAndroid Build Coastguard Worker {
542*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data && LE32(jitter->packets[i].timestamp, jitter->pointer_timestamp) && GT32(jitter->packets[i].timestamp+jitter->packets[i].span,jitter->pointer_timestamp))
543*28e138c6SAndroid Build Coastguard Worker break;
544*28e138c6SAndroid Build Coastguard Worker }
545*28e138c6SAndroid Build Coastguard Worker }
546*28e138c6SAndroid Build Coastguard Worker
547*28e138c6SAndroid Build Coastguard Worker /* If still no match, try for earliest packet possible */
548*28e138c6SAndroid Build Coastguard Worker if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
549*28e138c6SAndroid Build Coastguard Worker {
550*28e138c6SAndroid Build Coastguard Worker int found = 0;
551*28e138c6SAndroid Build Coastguard Worker spx_uint32_t best_time=0;
552*28e138c6SAndroid Build Coastguard Worker int best_span=0;
553*28e138c6SAndroid Build Coastguard Worker int besti=0;
554*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
555*28e138c6SAndroid Build Coastguard Worker {
556*28e138c6SAndroid Build Coastguard Worker /* check if packet starts within current chunk */
557*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data && LT32(jitter->packets[i].timestamp,jitter->pointer_timestamp+desired_span) && GE32(jitter->packets[i].timestamp,jitter->pointer_timestamp))
558*28e138c6SAndroid Build Coastguard Worker {
559*28e138c6SAndroid Build Coastguard Worker if (!found || LT32(jitter->packets[i].timestamp,best_time) || (jitter->packets[i].timestamp==best_time && GT32(jitter->packets[i].span,best_span)))
560*28e138c6SAndroid Build Coastguard Worker {
561*28e138c6SAndroid Build Coastguard Worker best_time = jitter->packets[i].timestamp;
562*28e138c6SAndroid Build Coastguard Worker best_span = jitter->packets[i].span;
563*28e138c6SAndroid Build Coastguard Worker besti = i;
564*28e138c6SAndroid Build Coastguard Worker found = 1;
565*28e138c6SAndroid Build Coastguard Worker }
566*28e138c6SAndroid Build Coastguard Worker }
567*28e138c6SAndroid Build Coastguard Worker }
568*28e138c6SAndroid Build Coastguard Worker if (found)
569*28e138c6SAndroid Build Coastguard Worker {
570*28e138c6SAndroid Build Coastguard Worker i=besti;
571*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "incomplete: %d %d %d %d\n", jitter->packets[i].timestamp, jitter->pointer_timestamp, chunk_size, jitter->packets[i].span);*/
572*28e138c6SAndroid Build Coastguard Worker }
573*28e138c6SAndroid Build Coastguard Worker }
574*28e138c6SAndroid Build Coastguard Worker
575*28e138c6SAndroid Build Coastguard Worker /* If we find something */
576*28e138c6SAndroid Build Coastguard Worker if (i!=SPEEX_JITTER_MAX_BUFFER_SIZE)
577*28e138c6SAndroid Build Coastguard Worker {
578*28e138c6SAndroid Build Coastguard Worker spx_int32_t offset;
579*28e138c6SAndroid Build Coastguard Worker
580*28e138c6SAndroid Build Coastguard Worker /* We (obviously) haven't lost this packet */
581*28e138c6SAndroid Build Coastguard Worker jitter->lost_count = 0;
582*28e138c6SAndroid Build Coastguard Worker
583*28e138c6SAndroid Build Coastguard Worker /* In this case, 0 isn't as a valid timestamp */
584*28e138c6SAndroid Build Coastguard Worker if (jitter->arrival[i] != 0)
585*28e138c6SAndroid Build Coastguard Worker {
586*28e138c6SAndroid Build Coastguard Worker update_timings(jitter, ((spx_int32_t)jitter->packets[i].timestamp) - ((spx_int32_t)jitter->arrival[i]) - jitter->buffer_margin);
587*28e138c6SAndroid Build Coastguard Worker }
588*28e138c6SAndroid Build Coastguard Worker
589*28e138c6SAndroid Build Coastguard Worker
590*28e138c6SAndroid Build Coastguard Worker /* Copy packet */
591*28e138c6SAndroid Build Coastguard Worker if (jitter->destroy)
592*28e138c6SAndroid Build Coastguard Worker {
593*28e138c6SAndroid Build Coastguard Worker packet->data = jitter->packets[i].data;
594*28e138c6SAndroid Build Coastguard Worker packet->len = jitter->packets[i].len;
595*28e138c6SAndroid Build Coastguard Worker } else {
596*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].len > packet->len)
597*28e138c6SAndroid Build Coastguard Worker {
598*28e138c6SAndroid Build Coastguard Worker speex_warning_int("jitter_buffer_get(): packet too large to fit. Size is", jitter->packets[i].len);
599*28e138c6SAndroid Build Coastguard Worker } else {
600*28e138c6SAndroid Build Coastguard Worker packet->len = jitter->packets[i].len;
601*28e138c6SAndroid Build Coastguard Worker }
602*28e138c6SAndroid Build Coastguard Worker for (j=0;j<packet->len;j++)
603*28e138c6SAndroid Build Coastguard Worker packet->data[j] = jitter->packets[i].data[j];
604*28e138c6SAndroid Build Coastguard Worker /* Remove packet */
605*28e138c6SAndroid Build Coastguard Worker speex_free(jitter->packets[i].data);
606*28e138c6SAndroid Build Coastguard Worker }
607*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].data = NULL;
608*28e138c6SAndroid Build Coastguard Worker /* Set timestamp and span (if requested) */
609*28e138c6SAndroid Build Coastguard Worker offset = (spx_int32_t)jitter->packets[i].timestamp-(spx_int32_t)jitter->pointer_timestamp;
610*28e138c6SAndroid Build Coastguard Worker if (start_offset != NULL)
611*28e138c6SAndroid Build Coastguard Worker *start_offset = offset;
612*28e138c6SAndroid Build Coastguard Worker else if (offset != 0)
613*28e138c6SAndroid Build Coastguard Worker speex_warning_int("jitter_buffer_get() discarding non-zero start_offset", offset);
614*28e138c6SAndroid Build Coastguard Worker
615*28e138c6SAndroid Build Coastguard Worker packet->timestamp = jitter->packets[i].timestamp;
616*28e138c6SAndroid Build Coastguard Worker jitter->last_returned_timestamp = packet->timestamp;
617*28e138c6SAndroid Build Coastguard Worker
618*28e138c6SAndroid Build Coastguard Worker packet->span = jitter->packets[i].span;
619*28e138c6SAndroid Build Coastguard Worker packet->sequence = jitter->packets[i].sequence;
620*28e138c6SAndroid Build Coastguard Worker packet->user_data = jitter->packets[i].user_data;
621*28e138c6SAndroid Build Coastguard Worker /* Point to the end of the current packet */
622*28e138c6SAndroid Build Coastguard Worker jitter->pointer_timestamp = jitter->packets[i].timestamp+jitter->packets[i].span;
623*28e138c6SAndroid Build Coastguard Worker
624*28e138c6SAndroid Build Coastguard Worker jitter->buffered = packet->span - desired_span;
625*28e138c6SAndroid Build Coastguard Worker
626*28e138c6SAndroid Build Coastguard Worker if (start_offset != NULL)
627*28e138c6SAndroid Build Coastguard Worker jitter->buffered += *start_offset;
628*28e138c6SAndroid Build Coastguard Worker
629*28e138c6SAndroid Build Coastguard Worker return JITTER_BUFFER_OK;
630*28e138c6SAndroid Build Coastguard Worker }
631*28e138c6SAndroid Build Coastguard Worker
632*28e138c6SAndroid Build Coastguard Worker
633*28e138c6SAndroid Build Coastguard Worker /* If we haven't found anything worth returning */
634*28e138c6SAndroid Build Coastguard Worker
635*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "not found\n");*/
636*28e138c6SAndroid Build Coastguard Worker jitter->lost_count++;
637*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "m");*/
638*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "lost_count = %d\n", jitter->lost_count);*/
639*28e138c6SAndroid Build Coastguard Worker
640*28e138c6SAndroid Build Coastguard Worker opt = compute_opt_delay(jitter);
641*28e138c6SAndroid Build Coastguard Worker
642*28e138c6SAndroid Build Coastguard Worker /* Should we force an increase in the buffer or just do normal interpolation? */
643*28e138c6SAndroid Build Coastguard Worker if (opt < 0)
644*28e138c6SAndroid Build Coastguard Worker {
645*28e138c6SAndroid Build Coastguard Worker /* Need to increase buffering */
646*28e138c6SAndroid Build Coastguard Worker
647*28e138c6SAndroid Build Coastguard Worker /* Shift histogram to compensate */
648*28e138c6SAndroid Build Coastguard Worker shift_timings(jitter, -opt);
649*28e138c6SAndroid Build Coastguard Worker
650*28e138c6SAndroid Build Coastguard Worker packet->timestamp = jitter->pointer_timestamp;
651*28e138c6SAndroid Build Coastguard Worker packet->span = -opt;
652*28e138c6SAndroid Build Coastguard Worker /* Don't move the pointer_timestamp forward */
653*28e138c6SAndroid Build Coastguard Worker packet->len = 0;
654*28e138c6SAndroid Build Coastguard Worker
655*28e138c6SAndroid Build Coastguard Worker jitter->buffered = packet->span - desired_span;
656*28e138c6SAndroid Build Coastguard Worker return JITTER_BUFFER_INSERTION;
657*28e138c6SAndroid Build Coastguard Worker /*jitter->pointer_timestamp -= jitter->delay_step;*/
658*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "Forced to interpolate\n");*/
659*28e138c6SAndroid Build Coastguard Worker } else {
660*28e138c6SAndroid Build Coastguard Worker /* Normal packet loss */
661*28e138c6SAndroid Build Coastguard Worker packet->timestamp = jitter->pointer_timestamp;
662*28e138c6SAndroid Build Coastguard Worker
663*28e138c6SAndroid Build Coastguard Worker desired_span = ROUND_DOWN(desired_span, jitter->concealment_size);
664*28e138c6SAndroid Build Coastguard Worker packet->span = desired_span;
665*28e138c6SAndroid Build Coastguard Worker jitter->pointer_timestamp += desired_span;
666*28e138c6SAndroid Build Coastguard Worker packet->len = 0;
667*28e138c6SAndroid Build Coastguard Worker
668*28e138c6SAndroid Build Coastguard Worker jitter->buffered = packet->span - desired_span;
669*28e138c6SAndroid Build Coastguard Worker return JITTER_BUFFER_MISSING;
670*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "Normal loss\n");*/
671*28e138c6SAndroid Build Coastguard Worker }
672*28e138c6SAndroid Build Coastguard Worker
673*28e138c6SAndroid Build Coastguard Worker
674*28e138c6SAndroid Build Coastguard Worker }
675*28e138c6SAndroid Build Coastguard Worker
jitter_buffer_get_another(JitterBuffer * jitter,JitterBufferPacket * packet)676*28e138c6SAndroid Build Coastguard Worker EXPORT int jitter_buffer_get_another(JitterBuffer *jitter, JitterBufferPacket *packet)
677*28e138c6SAndroid Build Coastguard Worker {
678*28e138c6SAndroid Build Coastguard Worker int i, j;
679*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
680*28e138c6SAndroid Build Coastguard Worker {
681*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data && jitter->packets[i].timestamp==jitter->last_returned_timestamp)
682*28e138c6SAndroid Build Coastguard Worker break;
683*28e138c6SAndroid Build Coastguard Worker }
684*28e138c6SAndroid Build Coastguard Worker if (i!=SPEEX_JITTER_MAX_BUFFER_SIZE)
685*28e138c6SAndroid Build Coastguard Worker {
686*28e138c6SAndroid Build Coastguard Worker /* Copy packet */
687*28e138c6SAndroid Build Coastguard Worker packet->len = jitter->packets[i].len;
688*28e138c6SAndroid Build Coastguard Worker if (jitter->destroy)
689*28e138c6SAndroid Build Coastguard Worker {
690*28e138c6SAndroid Build Coastguard Worker packet->data = jitter->packets[i].data;
691*28e138c6SAndroid Build Coastguard Worker } else {
692*28e138c6SAndroid Build Coastguard Worker for (j=0;j<packet->len;j++)
693*28e138c6SAndroid Build Coastguard Worker packet->data[j] = jitter->packets[i].data[j];
694*28e138c6SAndroid Build Coastguard Worker /* Remove packet */
695*28e138c6SAndroid Build Coastguard Worker speex_free(jitter->packets[i].data);
696*28e138c6SAndroid Build Coastguard Worker }
697*28e138c6SAndroid Build Coastguard Worker jitter->packets[i].data = NULL;
698*28e138c6SAndroid Build Coastguard Worker packet->timestamp = jitter->packets[i].timestamp;
699*28e138c6SAndroid Build Coastguard Worker packet->span = jitter->packets[i].span;
700*28e138c6SAndroid Build Coastguard Worker packet->sequence = jitter->packets[i].sequence;
701*28e138c6SAndroid Build Coastguard Worker packet->user_data = jitter->packets[i].user_data;
702*28e138c6SAndroid Build Coastguard Worker return JITTER_BUFFER_OK;
703*28e138c6SAndroid Build Coastguard Worker } else {
704*28e138c6SAndroid Build Coastguard Worker packet->data = NULL;
705*28e138c6SAndroid Build Coastguard Worker packet->len = 0;
706*28e138c6SAndroid Build Coastguard Worker packet->span = 0;
707*28e138c6SAndroid Build Coastguard Worker return JITTER_BUFFER_MISSING;
708*28e138c6SAndroid Build Coastguard Worker }
709*28e138c6SAndroid Build Coastguard Worker }
710*28e138c6SAndroid Build Coastguard Worker
711*28e138c6SAndroid Build Coastguard Worker /* Let the jitter buffer know it's the right time to adjust the buffering delay to the network conditions */
_jitter_buffer_update_delay(JitterBuffer * jitter,JitterBufferPacket * packet,spx_int32_t * start_offset)712*28e138c6SAndroid Build Coastguard Worker static int _jitter_buffer_update_delay(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t *start_offset)
713*28e138c6SAndroid Build Coastguard Worker {
714*28e138c6SAndroid Build Coastguard Worker spx_int16_t opt = compute_opt_delay(jitter);
715*28e138c6SAndroid Build Coastguard Worker /*fprintf(stderr, "opt adjustment is %d ", opt);*/
716*28e138c6SAndroid Build Coastguard Worker
717*28e138c6SAndroid Build Coastguard Worker if (opt < 0)
718*28e138c6SAndroid Build Coastguard Worker {
719*28e138c6SAndroid Build Coastguard Worker shift_timings(jitter, -opt);
720*28e138c6SAndroid Build Coastguard Worker
721*28e138c6SAndroid Build Coastguard Worker jitter->pointer_timestamp += opt;
722*28e138c6SAndroid Build Coastguard Worker jitter->interp_requested = -opt;
723*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "Decision to interpolate %d samples\n", -opt);*/
724*28e138c6SAndroid Build Coastguard Worker } else if (opt > 0)
725*28e138c6SAndroid Build Coastguard Worker {
726*28e138c6SAndroid Build Coastguard Worker shift_timings(jitter, -opt);
727*28e138c6SAndroid Build Coastguard Worker jitter->pointer_timestamp += opt;
728*28e138c6SAndroid Build Coastguard Worker /*fprintf (stderr, "Decision to drop %d samples\n", opt);*/
729*28e138c6SAndroid Build Coastguard Worker }
730*28e138c6SAndroid Build Coastguard Worker
731*28e138c6SAndroid Build Coastguard Worker return opt;
732*28e138c6SAndroid Build Coastguard Worker }
733*28e138c6SAndroid Build Coastguard Worker
734*28e138c6SAndroid Build Coastguard Worker /* Let the jitter buffer know it's the right time to adjust the buffering delay to the network conditions */
jitter_buffer_update_delay(JitterBuffer * jitter,JitterBufferPacket * packet,spx_int32_t * start_offset)735*28e138c6SAndroid Build Coastguard Worker EXPORT int jitter_buffer_update_delay(JitterBuffer *jitter, JitterBufferPacket *packet, spx_int32_t *start_offset)
736*28e138c6SAndroid Build Coastguard Worker {
737*28e138c6SAndroid Build Coastguard Worker /* If the programmer calls jitter_buffer_update_delay() directly,
738*28e138c6SAndroid Build Coastguard Worker automatically disable auto-adjustment */
739*28e138c6SAndroid Build Coastguard Worker jitter->auto_adjust = 0;
740*28e138c6SAndroid Build Coastguard Worker
741*28e138c6SAndroid Build Coastguard Worker return _jitter_buffer_update_delay(jitter, packet, start_offset);
742*28e138c6SAndroid Build Coastguard Worker }
743*28e138c6SAndroid Build Coastguard Worker
744*28e138c6SAndroid Build Coastguard Worker /** Get pointer timestamp of jitter buffer */
jitter_buffer_get_pointer_timestamp(JitterBuffer * jitter)745*28e138c6SAndroid Build Coastguard Worker EXPORT int jitter_buffer_get_pointer_timestamp(JitterBuffer *jitter)
746*28e138c6SAndroid Build Coastguard Worker {
747*28e138c6SAndroid Build Coastguard Worker return jitter->pointer_timestamp;
748*28e138c6SAndroid Build Coastguard Worker }
749*28e138c6SAndroid Build Coastguard Worker
jitter_buffer_tick(JitterBuffer * jitter)750*28e138c6SAndroid Build Coastguard Worker EXPORT void jitter_buffer_tick(JitterBuffer *jitter)
751*28e138c6SAndroid Build Coastguard Worker {
752*28e138c6SAndroid Build Coastguard Worker /* Automatically-adjust the buffering delay if requested */
753*28e138c6SAndroid Build Coastguard Worker if (jitter->auto_adjust)
754*28e138c6SAndroid Build Coastguard Worker _jitter_buffer_update_delay(jitter, NULL, NULL);
755*28e138c6SAndroid Build Coastguard Worker
756*28e138c6SAndroid Build Coastguard Worker if (jitter->buffered >= 0)
757*28e138c6SAndroid Build Coastguard Worker {
758*28e138c6SAndroid Build Coastguard Worker jitter->next_stop = jitter->pointer_timestamp - jitter->buffered;
759*28e138c6SAndroid Build Coastguard Worker } else {
760*28e138c6SAndroid Build Coastguard Worker jitter->next_stop = jitter->pointer_timestamp;
761*28e138c6SAndroid Build Coastguard Worker speex_warning_int("jitter buffer sees negative buffering, your code might be broken. Value is ", jitter->buffered);
762*28e138c6SAndroid Build Coastguard Worker }
763*28e138c6SAndroid Build Coastguard Worker jitter->buffered = 0;
764*28e138c6SAndroid Build Coastguard Worker }
765*28e138c6SAndroid Build Coastguard Worker
jitter_buffer_remaining_span(JitterBuffer * jitter,spx_uint32_t rem)766*28e138c6SAndroid Build Coastguard Worker EXPORT void jitter_buffer_remaining_span(JitterBuffer *jitter, spx_uint32_t rem)
767*28e138c6SAndroid Build Coastguard Worker {
768*28e138c6SAndroid Build Coastguard Worker /* Automatically-adjust the buffering delay if requested */
769*28e138c6SAndroid Build Coastguard Worker if (jitter->auto_adjust)
770*28e138c6SAndroid Build Coastguard Worker _jitter_buffer_update_delay(jitter, NULL, NULL);
771*28e138c6SAndroid Build Coastguard Worker
772*28e138c6SAndroid Build Coastguard Worker if (jitter->buffered < 0)
773*28e138c6SAndroid Build Coastguard Worker speex_warning_int("jitter buffer sees negative buffering, your code might be broken. Value is ", jitter->buffered);
774*28e138c6SAndroid Build Coastguard Worker jitter->next_stop = jitter->pointer_timestamp - rem;
775*28e138c6SAndroid Build Coastguard Worker }
776*28e138c6SAndroid Build Coastguard Worker
777*28e138c6SAndroid Build Coastguard Worker
778*28e138c6SAndroid Build Coastguard Worker /* Used like the ioctl function to control the jitter buffer parameters */
jitter_buffer_ctl(JitterBuffer * jitter,int request,void * ptr)779*28e138c6SAndroid Build Coastguard Worker EXPORT int jitter_buffer_ctl(JitterBuffer *jitter, int request, void *ptr)
780*28e138c6SAndroid Build Coastguard Worker {
781*28e138c6SAndroid Build Coastguard Worker int count, i;
782*28e138c6SAndroid Build Coastguard Worker switch(request)
783*28e138c6SAndroid Build Coastguard Worker {
784*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_SET_MARGIN:
785*28e138c6SAndroid Build Coastguard Worker jitter->buffer_margin = *(spx_int32_t*)ptr;
786*28e138c6SAndroid Build Coastguard Worker break;
787*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_GET_MARGIN:
788*28e138c6SAndroid Build Coastguard Worker *(spx_int32_t*)ptr = jitter->buffer_margin;
789*28e138c6SAndroid Build Coastguard Worker break;
790*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_GET_AVALIABLE_COUNT:
791*28e138c6SAndroid Build Coastguard Worker count = 0;
792*28e138c6SAndroid Build Coastguard Worker for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
793*28e138c6SAndroid Build Coastguard Worker {
794*28e138c6SAndroid Build Coastguard Worker if (jitter->packets[i].data && LE32(jitter->pointer_timestamp, jitter->packets[i].timestamp))
795*28e138c6SAndroid Build Coastguard Worker {
796*28e138c6SAndroid Build Coastguard Worker count++;
797*28e138c6SAndroid Build Coastguard Worker }
798*28e138c6SAndroid Build Coastguard Worker }
799*28e138c6SAndroid Build Coastguard Worker *(spx_int32_t*)ptr = count;
800*28e138c6SAndroid Build Coastguard Worker break;
801*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_SET_DESTROY_CALLBACK:
802*28e138c6SAndroid Build Coastguard Worker jitter->destroy = (void (*) (void *))ptr;
803*28e138c6SAndroid Build Coastguard Worker break;
804*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_GET_DESTROY_CALLBACK:
805*28e138c6SAndroid Build Coastguard Worker *(void (**) (void *))ptr = jitter->destroy;
806*28e138c6SAndroid Build Coastguard Worker break;
807*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_SET_DELAY_STEP:
808*28e138c6SAndroid Build Coastguard Worker jitter->delay_step = *(spx_int32_t*)ptr;
809*28e138c6SAndroid Build Coastguard Worker break;
810*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_GET_DELAY_STEP:
811*28e138c6SAndroid Build Coastguard Worker *(spx_int32_t*)ptr = jitter->delay_step;
812*28e138c6SAndroid Build Coastguard Worker break;
813*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_SET_CONCEALMENT_SIZE:
814*28e138c6SAndroid Build Coastguard Worker jitter->concealment_size = *(spx_int32_t*)ptr;
815*28e138c6SAndroid Build Coastguard Worker break;
816*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_GET_CONCEALMENT_SIZE:
817*28e138c6SAndroid Build Coastguard Worker *(spx_int32_t*)ptr = jitter->concealment_size;
818*28e138c6SAndroid Build Coastguard Worker break;
819*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_SET_MAX_LATE_RATE:
820*28e138c6SAndroid Build Coastguard Worker jitter->max_late_rate = *(spx_int32_t*)ptr;
821*28e138c6SAndroid Build Coastguard Worker jitter->window_size = 100*TOP_DELAY/jitter->max_late_rate;
822*28e138c6SAndroid Build Coastguard Worker jitter->subwindow_size = jitter->window_size/MAX_BUFFERS;
823*28e138c6SAndroid Build Coastguard Worker break;
824*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_GET_MAX_LATE_RATE:
825*28e138c6SAndroid Build Coastguard Worker *(spx_int32_t*)ptr = jitter->max_late_rate;
826*28e138c6SAndroid Build Coastguard Worker break;
827*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_SET_LATE_COST:
828*28e138c6SAndroid Build Coastguard Worker jitter->latency_tradeoff = *(spx_int32_t*)ptr;
829*28e138c6SAndroid Build Coastguard Worker break;
830*28e138c6SAndroid Build Coastguard Worker case JITTER_BUFFER_GET_LATE_COST:
831*28e138c6SAndroid Build Coastguard Worker *(spx_int32_t*)ptr = jitter->latency_tradeoff;
832*28e138c6SAndroid Build Coastguard Worker break;
833*28e138c6SAndroid Build Coastguard Worker default:
834*28e138c6SAndroid Build Coastguard Worker speex_warning_int("Unknown jitter_buffer_ctl request: ", request);
835*28e138c6SAndroid Build Coastguard Worker return -1;
836*28e138c6SAndroid Build Coastguard Worker }
837*28e138c6SAndroid Build Coastguard Worker return 0;
838*28e138c6SAndroid Build Coastguard Worker }
839*28e138c6SAndroid Build Coastguard Worker
840