add output stream draining
[profile/ivi/pulseaudio.git] / src / memblockq.c
1 #include <sys/time.h>
2 #include <time.h>
3 #include <stdio.h>
4 #include <assert.h>
5 #include <stdlib.h>
6
7 #include "memblockq.h"
8
9 struct memblock_list {
10     struct memblock_list *next;
11     struct pa_memchunk chunk;
12     struct timeval stamp;
13 };
14
15 struct pa_memblockq {
16     struct memblock_list *blocks, *blocks_tail;
17     unsigned n_blocks;
18     size_t current_length, maxlength, tlength, base, prebuf, minreq;
19     int measure_delay;
20     uint32_t delay;
21     struct pa_mcalign *mcalign;
22 };
23
24 struct pa_memblockq* pa_memblockq_new(size_t maxlength, size_t tlength, size_t base, size_t prebuf, size_t minreq) {
25     struct pa_memblockq* bq;
26     assert(maxlength && base && maxlength);
27     
28     bq = malloc(sizeof(struct pa_memblockq));
29     assert(bq);
30     bq->blocks = bq->blocks_tail = 0;
31     bq->n_blocks = 0;
32
33     bq->current_length = 0;
34
35     fprintf(stderr, "memblockq requested: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", maxlength, tlength, base, prebuf, minreq);
36     
37     bq->base = base;
38
39     bq->maxlength = ((maxlength+base-1)/base)*base;
40     assert(bq->maxlength >= base);
41
42     bq->tlength = ((tlength+base-1)/base)*base;
43     if (bq->tlength == 0 || bq->tlength >= bq->maxlength)
44         bq->tlength = bq->maxlength;
45     
46     bq->prebuf = (prebuf == (size_t) -1) ? bq->maxlength/2 : prebuf;
47     bq->prebuf = (bq->prebuf/base)*base;
48     if (bq->prebuf > bq->maxlength)
49         bq->prebuf = bq->maxlength;
50     
51     bq->minreq = (minreq/base)*base;
52     if (bq->minreq == 0)
53         bq->minreq = 1;
54
55     fprintf(stderr, "memblockq sanitized: maxlength=%u, tlength=%u, base=%u, prebuf=%u, minreq=%u\n", bq->maxlength, bq->tlength, bq->base, bq->prebuf, bq->minreq);
56     
57     bq->measure_delay = 0;
58     bq->delay = 0;
59
60     bq->mcalign = NULL;
61     
62     return bq;
63 }
64
65 void pa_memblockq_free(struct pa_memblockq* bq) {
66     struct memblock_list *l;
67     assert(bq);
68
69     if (bq->mcalign)
70         pa_mcalign_free(bq->mcalign);
71
72     while ((l = bq->blocks)) {
73         bq->blocks = l->next;
74         pa_memblock_unref(l->chunk.memblock);
75         free(l);
76     }
77     
78     free(bq);
79 }
80
81 void pa_memblockq_push(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) {
82     struct memblock_list *q;
83     assert(bq && chunk && chunk->memblock && chunk->length && (chunk->length % bq->base) == 0);
84
85     q = malloc(sizeof(struct memblock_list));
86     assert(q);
87
88     if (bq->measure_delay)
89         gettimeofday(&q->stamp, NULL);
90     else
91         timerclear(&q->stamp);
92
93     q->chunk = *chunk;
94     pa_memblock_ref(q->chunk.memblock);
95     assert(q->chunk.index+q->chunk.length <= q->chunk.memblock->length);
96     q->next = NULL;
97     
98     if (bq->blocks_tail)
99         bq->blocks_tail->next = q;
100     else
101         bq->blocks = q;
102     
103     bq->blocks_tail = q;
104
105     bq->n_blocks++;
106     bq->current_length += chunk->length;
107
108     pa_memblockq_shorten(bq, bq->maxlength);
109 }
110
111 int pa_memblockq_peek(struct pa_memblockq* bq, struct pa_memchunk *chunk) {
112     assert(bq && chunk);
113
114     if (!bq->blocks || bq->current_length < bq->prebuf)
115         return -1;
116
117     bq->prebuf = 0;
118
119     *chunk = bq->blocks->chunk;
120     pa_memblock_ref(chunk->memblock);
121
122 /*     if (chunk->memblock->ref != 2) */
123 /*         fprintf(stderr, "block %p with ref %u peeked.\n", chunk->memblock, chunk->memblock->ref); */
124     
125     return 0;
126 }
127
128 /*
129 int memblockq_pop(struct memblockq* bq, struct pa_memchunk *chunk) {
130     struct memblock_list *q;
131     
132     assert(bq && chunk);
133
134     if (!bq->blocks || bq->current_length < bq->prebuf)
135         return -1;
136
137     bq->prebuf = 0;
138
139     q = bq->blocks;
140     bq->blocks = bq->blocks->next;
141
142     *chunk = q->chunk;
143
144     bq->n_blocks--;
145     bq->current_length -= chunk->length;
146
147     free(q);
148     return 0;
149 }
150 */
151
152 static uint32_t age(struct timeval *tv) {
153     assert(tv);
154     struct timeval now;
155     uint32_t r;
156
157     if (tv->tv_sec == 0)
158         return 0;
159
160     gettimeofday(&now, NULL);
161     
162     r = (now.tv_sec-tv->tv_sec) * 1000000;
163
164     if (now.tv_usec >= tv->tv_usec)
165         r += now.tv_usec - tv->tv_usec;
166     else
167         r -= tv->tv_usec - now.tv_usec;
168
169     return r;
170 }
171
172 void pa_memblockq_drop(struct pa_memblockq *bq, size_t length) {
173     assert(bq && length && (length % bq->base) == 0);
174
175     while (length > 0) {
176         size_t l = length;
177         assert(bq->blocks && bq->current_length >= length);
178         
179         if (l > bq->blocks->chunk.length)
180             l = bq->blocks->chunk.length;
181
182         if (bq->measure_delay)
183             bq->delay = age(&bq->blocks->stamp);
184         
185         bq->blocks->chunk.index += l;
186         bq->blocks->chunk.length -= l;
187         bq->current_length -= l;
188         
189         if (bq->blocks->chunk.length == 0) {
190             struct memblock_list *q;
191             
192             q = bq->blocks;
193             bq->blocks = bq->blocks->next;
194             if (bq->blocks == NULL)
195                 bq->blocks_tail = NULL;
196             pa_memblock_unref(q->chunk.memblock);
197             free(q);
198             
199             bq->n_blocks--;
200         }
201
202         length -= l;
203     }
204 }
205
206 void pa_memblockq_shorten(struct pa_memblockq *bq, size_t length) {
207     size_t l;
208     assert(bq);
209
210     if (bq->current_length <= length)
211         return;
212
213     fprintf(stderr, "Warning! pa_memblockq_shorten()\n");
214     
215     l = bq->current_length - length;
216     l /= bq->base;
217     l *= bq->base;
218
219     pa_memblockq_drop(bq, l);
220 }
221
222
223 void pa_memblockq_empty(struct pa_memblockq *bq) {
224     assert(bq);
225     pa_memblockq_shorten(bq, 0);
226 }
227
228 int pa_memblockq_is_readable(struct pa_memblockq *bq) {
229     assert(bq);
230
231     return bq->current_length && (bq->current_length >= bq->prebuf);
232 }
233
234 int pa_memblockq_is_writable(struct pa_memblockq *bq, size_t length) {
235     assert(bq);
236
237     return bq->current_length + length <= bq->tlength;
238 }
239
240 uint32_t pa_memblockq_get_delay(struct pa_memblockq *bq) {
241     assert(bq);
242     return bq->delay;
243 }
244
245 uint32_t pa_memblockq_get_length(struct pa_memblockq *bq) {
246     assert(bq);
247     return bq->current_length;
248 }
249
250 uint32_t pa_memblockq_missing(struct pa_memblockq *bq) {
251     size_t l;
252     assert(bq);
253
254     if (bq->current_length >= bq->tlength)
255         return 0;
256
257     l = bq->tlength - bq->current_length;
258     assert(l);
259
260     return (l >= bq->minreq) ? l : 0;
261 }
262
263 void pa_memblockq_push_align(struct pa_memblockq* bq, const struct pa_memchunk *chunk, size_t delta) {
264     struct pa_memchunk rchunk;
265     assert(bq && chunk && bq->base);
266
267     if (bq->base == 1) {
268         pa_memblockq_push(bq, chunk, delta);
269         return;
270     }
271
272     if (!bq->mcalign) {
273         bq->mcalign = pa_mcalign_new(bq->base);
274         assert(bq->mcalign);
275     }
276     
277     pa_mcalign_push(bq->mcalign, chunk);
278
279     while (pa_mcalign_pop(bq->mcalign, &rchunk) >= 0) {
280         pa_memblockq_push(bq, &rchunk, delta);
281         pa_memblock_unref(rchunk.memblock);
282         delta = 0;
283     }
284 }
285
286 uint32_t pa_memblockq_get_minreq(struct pa_memblockq *bq) {
287     assert(bq);
288     return bq->minreq;
289 }