]>
code.delx.au - pulseaudio/blob - src/pulsecore/memblockq.c
4 This file is part of PulseAudio.
6 Copyright 2004-2006 Lennart Poettering
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
34 #include <pulse/xmalloc.h>
36 #include <pulsecore/log.h>
37 #include <pulsecore/mcalign.h>
38 #include <pulsecore/macro.h>
39 #include <pulsecore/flist.h>
41 #include "memblockq.h"
44 struct list_item
*next
, *prev
;
49 PA_STATIC_FLIST_DECLARE(list_items
, 0, pa_xfree
);
52 struct list_item
*blocks
, *blocks_tail
;
53 struct list_item
*current_read
, *current_write
;
55 size_t maxlength
, tlength
, base
, prebuf
, minreq
, maxrewind
;
56 int64_t read_index
, write_index
;
64 pa_memblockq
* pa_memblockq_new(
72 pa_memblock
*silence
) {
78 bq
= pa_xnew(pa_memblockq
, 1);
79 bq
->blocks
= bq
->blocks_tail
= NULL
;
80 bq
->current_read
= bq
->current_write
= NULL
;
84 bq
->read_index
= bq
->write_index
= idx
;
86 pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
87 (unsigned long) maxlength
, (unsigned long) tlength
, (unsigned long) base
, (unsigned long) prebuf
, (unsigned long) minreq
, (unsigned long) maxrewind
);
89 bq
->missing
= bq
->requested
= bq
->maxlength
= bq
->tlength
= bq
->prebuf
= bq
->minreq
= bq
->maxrewind
= 0;
92 pa_memblockq_set_maxlength(bq
, maxlength
);
93 pa_memblockq_set_tlength(bq
, tlength
);
94 pa_memblockq_set_prebuf(bq
, prebuf
);
95 pa_memblockq_set_minreq(bq
, minreq
);
96 pa_memblockq_set_maxrewind(bq
, maxrewind
);
98 pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
99 (unsigned long) bq
->maxlength
, (unsigned long) bq
->tlength
, (unsigned long) bq
->base
, (unsigned long) bq
->prebuf
, (unsigned long) bq
->minreq
, (unsigned long) bq
->maxrewind
);
101 bq
->silence
= silence
? pa_memblock_ref(silence
) : NULL
;
102 bq
->mcalign
= pa_mcalign_new(bq
->base
);
107 void pa_memblockq_free(pa_memblockq
* bq
) {
110 pa_memblockq_flush(bq
);
113 pa_memblock_unref(bq
->silence
);
116 pa_mcalign_free(bq
->mcalign
);
121 static void fix_current_read(pa_memblockq
*bq
) {
124 if (PA_UNLIKELY(!bq
->blocks
)) {
125 bq
->current_read
= NULL
;
129 if (PA_UNLIKELY(!bq
->current_read
))
130 bq
->current_read
= bq
->blocks
;
133 while (PA_UNLIKELY(bq
->current_read
->index
> bq
->read_index
))
135 if (bq
->current_read
->prev
)
136 bq
->current_read
= bq
->current_read
->prev
;
141 while (PA_LIKELY(bq
->current_read
!= NULL
) && PA_UNLIKELY(bq
->current_read
->index
+ bq
->current_read
->chunk
.length
<= bq
->read_index
))
142 bq
->current_read
= bq
->current_read
->next
;
144 /* At this point current_read will either point at or left of the
145 next block to play. It may be NULL in case everything in
146 the queue was already played */
149 static void fix_current_write(pa_memblockq
*bq
) {
152 if (PA_UNLIKELY(!bq
->blocks
)) {
153 bq
->current_write
= NULL
;
157 if (PA_UNLIKELY(!bq
->current_write
))
158 bq
->current_write
= bq
->blocks_tail
;
161 while (PA_UNLIKELY(bq
->current_write
->index
+ bq
->current_write
->chunk
.length
<= bq
->write_index
))
163 if (bq
->current_write
->next
)
164 bq
->current_write
= bq
->current_write
->next
;
169 while (PA_LIKELY(bq
->current_write
!= NULL
) && PA_UNLIKELY(bq
->current_write
->index
> bq
->write_index
))
170 bq
->current_write
= bq
->current_write
->prev
;
172 /* At this point current_write will either point at or right of
173 the next block to write data to. It may be NULL in case
174 everything in the queue is still to be played */
177 static void drop_block(pa_memblockq
*bq
, struct list_item
*q
) {
181 pa_assert(bq
->n_blocks
>= 1);
184 q
->prev
->next
= q
->next
;
186 pa_assert(bq
->blocks
== q
);
187 bq
->blocks
= q
->next
;
191 q
->next
->prev
= q
->prev
;
193 pa_assert(bq
->blocks_tail
== q
);
194 bq
->blocks_tail
= q
->prev
;
197 if (bq
->current_write
== q
)
198 bq
->current_write
= q
->prev
;
200 if (bq
->current_read
== q
)
201 bq
->current_read
= q
->next
;
203 pa_memblock_unref(q
->chunk
.memblock
);
205 if (pa_flist_push(PA_STATIC_FLIST_GET(list_items
), q
) < 0)
211 static void drop_backlog(pa_memblockq
*bq
) {
215 boundary
= bq
->read_index
- bq
->maxrewind
;
217 while (bq
->blocks
&& (bq
->blocks
->index
+ bq
->blocks
->chunk
.length
<= boundary
))
218 drop_block(bq
, bq
->blocks
);
221 static pa_bool_t
can_push(pa_memblockq
*bq
, size_t l
) {
226 if (bq
->read_index
> bq
->write_index
) {
227 size_t d
= bq
->read_index
- bq
->write_index
;
235 end
= bq
->blocks_tail
? bq
->blocks_tail
->index
+ bq
->blocks_tail
->chunk
.length
: bq
->write_index
;
237 /* Make sure that the list doesn't get too long */
238 if (bq
->write_index
+ l
> end
)
239 if (bq
->write_index
+ l
- bq
->read_index
> bq
->maxlength
)
245 int pa_memblockq_push(pa_memblockq
* bq
, const pa_memchunk
*uchunk
) {
246 struct list_item
*q
, *n
;
252 pa_assert(uchunk
->memblock
);
253 pa_assert(uchunk
->length
> 0);
254 pa_assert(uchunk
->index
+ uchunk
->length
<= pa_memblock_get_length(uchunk
->memblock
));
256 if (uchunk
->length
% bq
->base
)
259 if (!can_push(bq
, uchunk
->length
))
262 old
= bq
->write_index
;
265 fix_current_write(bq
);
266 q
= bq
->current_write
;
268 /* First we advance the q pointer right of where we want to
272 while (bq
->write_index
+ chunk
.length
> q
->index
)
282 /* We go from back to front to look for the right place to add
283 * this new entry. Drop data we will overwrite on the way */
287 if (bq
->write_index
>= q
->index
+ q
->chunk
.length
)
288 /* We found the entry where we need to place the new entry immediately after */
290 else if (bq
->write_index
+ chunk
.length
<= q
->index
) {
291 /* This entry isn't touched at all, let's skip it */
293 } else if (bq
->write_index
<= q
->index
&&
294 bq
->write_index
+ chunk
.length
>= q
->index
+ q
->chunk
.length
) {
296 /* This entry is fully replaced by the new entry, so let's drop it */
302 } else if (bq
->write_index
>= q
->index
) {
303 /* The write index points into this memblock, so let's
304 * truncate or split it */
306 if (bq
->write_index
+ chunk
.length
< q
->index
+ q
->chunk
.length
) {
308 /* We need to save the end of this memchunk */
312 /* Create a new list entry for the end of thie memchunk */
313 if (!(p
= pa_flist_pop(PA_STATIC_FLIST_GET(list_items
))))
314 p
= pa_xnew(struct list_item
, 1);
317 pa_memblock_ref(p
->chunk
.memblock
);
319 /* Calculate offset */
320 d
= bq
->write_index
+ chunk
.length
- q
->index
;
323 /* Drop it from the new entry */
324 p
->index
= q
->index
+ d
;
325 p
->chunk
.length
-= d
;
327 /* Add it to the list */
329 if ((p
->next
= q
->next
))
338 /* Truncate the chunk */
339 if (!(q
->chunk
.length
= bq
->write_index
- q
->index
)) {
346 /* We had to truncate this block, hence we're now at the right position */
351 pa_assert(bq
->write_index
+ (int64_t)chunk
.length
> q
->index
&&
352 bq
->write_index
+ (int64_t)chunk
.length
< q
->index
+ (int64_t)q
->chunk
.length
&&
353 bq
->write_index
< q
->index
);
355 /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
357 d
= bq
->write_index
+ chunk
.length
- q
->index
;
360 q
->chunk
.length
-= d
;
367 pa_assert(bq
->write_index
>= q
->index
+ (int64_t)q
->chunk
.length
);
368 pa_assert(!q
->next
|| (bq
->write_index
+ (int64_t)chunk
.length
<= q
->next
->index
));
370 /* Try to merge memory blocks */
372 if (q
->chunk
.memblock
== chunk
.memblock
&&
373 q
->chunk
.index
+ (int64_t)q
->chunk
.length
== chunk
.index
&&
374 bq
->write_index
== q
->index
+ (int64_t)q
->chunk
.length
) {
376 q
->chunk
.length
+= chunk
.length
;
377 bq
->write_index
+= chunk
.length
;
381 pa_assert(!bq
->blocks
|| (bq
->write_index
+ (int64_t)chunk
.length
<= bq
->blocks
->index
));
383 if (!(n
= pa_flist_pop(PA_STATIC_FLIST_GET(list_items
))))
384 n
= pa_xnew(struct list_item
, 1);
387 pa_memblock_ref(n
->chunk
.memblock
);
388 n
->index
= bq
->write_index
;
389 bq
->write_index
+= n
->chunk
.length
;
391 n
->next
= q
? q
->next
: bq
->blocks
;
408 delta
= bq
->write_index
- old
;
410 if (delta
>= bq
->requested
) {
411 delta
-= bq
->requested
;
414 bq
->requested
-= delta
;
418 bq
->missing
-= delta
;
423 static pa_bool_t
memblockq_check_prebuf(pa_memblockq
*bq
) {
428 if (pa_memblockq_get_length(bq
) < bq
->prebuf
)
431 bq
->in_prebuf
= FALSE
;
435 if (bq
->prebuf
> 0 && bq
->read_index
>= bq
->write_index
) {
436 bq
->in_prebuf
= TRUE
;
444 int pa_memblockq_peek(pa_memblockq
* bq
, pa_memchunk
*chunk
) {
449 /* We need to pre-buffer */
450 if (memblockq_check_prebuf(bq
))
453 fix_current_read(bq
);
455 /* Do we need to spit out silence? */
456 if (!bq
->current_read
|| bq
->current_read
->index
> bq
->read_index
) {
460 /* How much silence shall we return? */
461 if (bq
->current_read
)
462 length
= bq
->current_read
->index
- bq
->read_index
;
463 else if (bq
->write_index
> bq
->read_index
)
464 length
= (size_t) (bq
->write_index
- bq
->read_index
);
468 /* We need to return silence, since no data is yet available */
472 chunk
->memblock
= pa_memblock_ref(bq
->silence
);
474 l
= pa_memblock_get_length(chunk
->memblock
);
475 chunk
->length
= (length
<= 0 || length
> l
) ? l
: length
;
479 /* If the memblockq is empty, return -1, otherwise return
480 * the time to sleep */
484 chunk
->memblock
= NULL
;
485 chunk
->length
= length
;
492 /* Ok, let's pass real data to the caller */
493 *chunk
= bq
->current_read
->chunk
;
494 pa_memblock_ref(chunk
->memblock
);
496 pa_assert(bq
->read_index
>= bq
->current_read
->index
);
497 d
= bq
->read_index
- bq
->current_read
->index
;
504 void pa_memblockq_drop(pa_memblockq
*bq
, size_t length
) {
507 pa_assert(length
% bq
->base
== 0);
509 old
= bq
->read_index
;
513 /* Do not drop any data when we are in prebuffering mode */
514 if (memblockq_check_prebuf(bq
))
517 fix_current_read(bq
);
519 if (bq
->current_read
) {
522 /* We go through this piece by piece to make sure we don't
523 * drop more than allowed by prebuf */
525 p
= bq
->current_read
->index
+ bq
->current_read
->chunk
.length
;
526 pa_assert(p
>= bq
->read_index
);
527 d
= p
- bq
->read_index
;
537 /* The list is empty, there's nothing we could drop */
538 bq
->read_index
+= length
;
545 delta
= bq
->read_index
- old
;
546 bq
->missing
+= delta
;
549 pa_bool_t
pa_memblockq_is_readable(pa_memblockq
*bq
) {
552 if (memblockq_check_prebuf(bq
))
555 if (pa_memblockq_get_length(bq
) <= 0)
561 size_t pa_memblockq_get_length(pa_memblockq
*bq
) {
564 if (bq
->write_index
<= bq
->read_index
)
567 return (size_t) (bq
->write_index
- bq
->read_index
);
570 size_t pa_memblockq_missing(pa_memblockq
*bq
) {
574 if ((l
= pa_memblockq_get_length(bq
)) >= bq
->tlength
)
579 return l
>= bq
->minreq
? l
: 0;
582 void pa_memblockq_seek(pa_memblockq
*bq
, int64_t offset
, pa_seek_mode_t seek
) {
586 old
= bq
->write_index
;
589 case PA_SEEK_RELATIVE
:
590 bq
->write_index
+= offset
;
592 case PA_SEEK_ABSOLUTE
:
593 bq
->write_index
= offset
;
595 case PA_SEEK_RELATIVE_ON_READ
:
596 bq
->write_index
= bq
->read_index
+ offset
;
598 case PA_SEEK_RELATIVE_END
:
599 bq
->write_index
= (bq
->blocks_tail
? bq
->blocks_tail
->index
+ (int64_t) bq
->blocks_tail
->chunk
.length
: bq
->read_index
) + offset
;
602 pa_assert_not_reached();
607 delta
= bq
->write_index
- old
;
609 if (delta
>= bq
->requested
) {
610 delta
-= bq
->requested
;
612 } else if (delta
>= 0) {
613 bq
->requested
-= delta
;
617 bq
->missing
-= delta
;
620 void pa_memblockq_flush(pa_memblockq
*bq
) {
625 drop_block(bq
, bq
->blocks
);
627 pa_assert(bq
->n_blocks
== 0);
629 old
= bq
->write_index
;
630 bq
->write_index
= bq
->read_index
;
632 pa_memblockq_prebuf_force(bq
);
634 delta
= bq
->write_index
- old
;
636 if (delta
>= bq
->requested
) {
637 delta
-= bq
->requested
;
639 } else if (delta
>= 0) {
640 bq
->requested
-= delta
;
644 bq
->missing
-= delta
;
647 size_t pa_memblockq_get_tlength(pa_memblockq
*bq
) {
653 size_t pa_memblockq_get_minreq(pa_memblockq
*bq
) {
659 int64_t pa_memblockq_get_read_index(pa_memblockq
*bq
) {
662 return bq
->read_index
;
665 int64_t pa_memblockq_get_write_index(pa_memblockq
*bq
) {
668 return bq
->write_index
;
671 int pa_memblockq_push_align(pa_memblockq
* bq
, const pa_memchunk
*chunk
) {
678 return pa_memblockq_push(bq
, chunk
);
680 if (!can_push(bq
, pa_mcalign_csize(bq
->mcalign
, chunk
->length
)))
683 pa_mcalign_push(bq
->mcalign
, chunk
);
685 while (pa_mcalign_pop(bq
->mcalign
, &rchunk
) >= 0) {
687 r
= pa_memblockq_push(bq
, &rchunk
);
688 pa_memblock_unref(rchunk
.memblock
);
691 pa_mcalign_flush(bq
->mcalign
);
699 void pa_memblockq_prebuf_disable(pa_memblockq
*bq
) {
702 bq
->in_prebuf
= FALSE
;
705 void pa_memblockq_prebuf_force(pa_memblockq
*bq
) {
709 bq
->in_prebuf
= TRUE
;
712 size_t pa_memblockq_get_maxlength(pa_memblockq
*bq
) {
715 return bq
->maxlength
;
718 size_t pa_memblockq_get_prebuf(pa_memblockq
*bq
) {
724 size_t pa_memblockq_pop_missing(pa_memblockq
*bq
) {
729 /* pa_log("pop: %lli", bq->missing); */
731 if (bq
->missing
<= 0)
734 l
= (size_t) bq
->missing
;
741 void pa_memblockq_set_maxlength(pa_memblockq
*bq
, size_t maxlength
) {
744 bq
->maxlength
= ((maxlength
+bq
->base
-1)/bq
->base
)*bq
->base
;
746 if (bq
->maxlength
< bq
->base
)
747 bq
->maxlength
= bq
->base
;
749 if (bq
->tlength
> bq
->maxlength
)
750 pa_memblockq_set_tlength(bq
, bq
->maxlength
);
752 if (bq
->prebuf
> bq
->maxlength
)
753 pa_memblockq_set_prebuf(bq
, bq
->maxlength
);
756 void pa_memblockq_set_tlength(pa_memblockq
*bq
, size_t tlength
) {
760 old_tlength
= bq
->tlength
;
763 tlength
= bq
->maxlength
;
765 bq
->tlength
= ((tlength
+bq
->base
-1)/bq
->base
)*bq
->base
;
767 if (bq
->tlength
> bq
->maxlength
)
768 bq
->tlength
= bq
->maxlength
;
770 if (bq
->minreq
> bq
->tlength
- bq
->prebuf
)
771 pa_memblockq_set_minreq(bq
, bq
->tlength
- bq
->prebuf
);
773 bq
->missing
+= (int64_t) bq
->tlength
- (int64_t) old_tlength
;
776 void pa_memblockq_set_prebuf(pa_memblockq
*bq
, size_t prebuf
) {
779 bq
->prebuf
= (prebuf
== (size_t) -1) ? bq
->tlength
: prebuf
;
780 bq
->prebuf
= ((bq
->prebuf
+bq
->base
-1)/bq
->base
)*bq
->base
;
782 if (prebuf
> 0 && bq
->prebuf
< bq
->base
)
783 bq
->prebuf
= bq
->base
;
785 if (bq
->prebuf
> bq
->maxlength
)
786 bq
->prebuf
= bq
->maxlength
;
788 if (bq
->prebuf
<= 0 || pa_memblockq_get_length(bq
) >= bq
->prebuf
)
789 bq
->in_prebuf
= FALSE
;
791 if (bq
->minreq
> bq
->tlength
- bq
->prebuf
)
792 pa_memblockq_set_minreq(bq
, bq
->tlength
- bq
->prebuf
);
795 void pa_memblockq_set_minreq(pa_memblockq
*bq
, size_t minreq
) {
798 bq
->minreq
= (minreq
/bq
->base
)*bq
->base
;
800 if (bq
->minreq
> bq
->tlength
- bq
->prebuf
)
801 bq
->minreq
= bq
->tlength
- bq
->prebuf
;
803 if (bq
->minreq
< bq
->base
)
804 bq
->minreq
= bq
->base
;
807 void pa_memblockq_set_maxrewind(pa_memblockq
*bq
, size_t maxrewind
) {
810 bq
->maxrewind
= (maxrewind
/bq
->base
)*bq
->base
;
813 void pa_memblockq_rewind(pa_memblockq
*bq
, size_t length
) {
815 pa_assert(length
% bq
->base
== 0);
817 bq
->read_index
-= length
;
818 bq
->missing
-= length
;
821 int pa_memblockq_splice(pa_memblockq
*bq
, pa_memblockq
*source
) {
826 pa_memblockq_prebuf_disable(bq
);
831 if (pa_memblockq_peek(source
, &chunk
) < 0)
834 pa_assert(chunk
.length
> 0);
836 if (chunk
.memblock
) {
838 if (pa_memblockq_push_align(bq
, &chunk
) < 0) {
839 pa_memblock_unref(chunk
.memblock
);
843 pa_memblock_unref(chunk
.memblock
);
845 pa_memblockq_seek(bq
, chunk
.length
, PA_SEEK_RELATIVE
);
847 pa_memblockq_drop(bq
, chunk
.length
);
851 void pa_memblockq_willneed(pa_memblockq
*bq
) {
856 fix_current_read(bq
);
858 for (q
= bq
->current_read
; q
; q
= q
->next
)
859 pa_memchunk_will_need(&q
->chunk
);
862 void pa_memblockq_set_silence(pa_memblockq
*bq
, pa_memblock
*silence
) {
866 pa_memblock_unref(bq
->silence
);
868 bq
->silence
= silence
? pa_memblock_ref(silence
) : NULL
;
871 pa_bool_t
pa_memblockq_is_empty(pa_memblockq
*bq
) {