]>
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_memchunk
*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
);
102 bq
->silence
= *silence
;
103 pa_memblock_ref(bq
->silence
.memblock
);
105 pa_memchunk_reset(&bq
->silence
);
107 bq
->mcalign
= pa_mcalign_new(bq
->base
);
112 void pa_memblockq_free(pa_memblockq
* bq
) {
115 pa_memblockq_flush(bq
);
117 if (bq
->silence
.memblock
)
118 pa_memblock_unref(bq
->silence
.memblock
);
121 pa_mcalign_free(bq
->mcalign
);
126 static void fix_current_read(pa_memblockq
*bq
) {
129 if (PA_UNLIKELY(!bq
->blocks
)) {
130 bq
->current_read
= NULL
;
134 if (PA_UNLIKELY(!bq
->current_read
))
135 bq
->current_read
= bq
->blocks
;
138 while (PA_UNLIKELY(bq
->current_read
->index
> bq
->read_index
))
140 if (bq
->current_read
->prev
)
141 bq
->current_read
= bq
->current_read
->prev
;
146 while (PA_LIKELY(bq
->current_read
!= NULL
) && PA_UNLIKELY(bq
->current_read
->index
+ (int64_t) bq
->current_read
->chunk
.length
<= bq
->read_index
))
147 bq
->current_read
= bq
->current_read
->next
;
149 /* At this point current_read will either point at or left of the
150 next block to play. It may be NULL in case everything in
151 the queue was already played */
154 static void fix_current_write(pa_memblockq
*bq
) {
157 if (PA_UNLIKELY(!bq
->blocks
)) {
158 bq
->current_write
= NULL
;
162 if (PA_UNLIKELY(!bq
->current_write
))
163 bq
->current_write
= bq
->blocks_tail
;
166 while (PA_UNLIKELY(bq
->current_write
->index
+ (int64_t) bq
->current_write
->chunk
.length
<= bq
->write_index
))
168 if (bq
->current_write
->next
)
169 bq
->current_write
= bq
->current_write
->next
;
174 while (PA_LIKELY(bq
->current_write
!= NULL
) && PA_UNLIKELY(bq
->current_write
->index
> bq
->write_index
))
175 bq
->current_write
= bq
->current_write
->prev
;
177 /* At this point current_write will either point at or right of
178 the next block to write data to. It may be NULL in case
179 everything in the queue is still to be played */
182 static void drop_block(pa_memblockq
*bq
, struct list_item
*q
) {
186 pa_assert(bq
->n_blocks
>= 1);
189 q
->prev
->next
= q
->next
;
191 pa_assert(bq
->blocks
== q
);
192 bq
->blocks
= q
->next
;
196 q
->next
->prev
= q
->prev
;
198 pa_assert(bq
->blocks_tail
== q
);
199 bq
->blocks_tail
= q
->prev
;
202 if (bq
->current_write
== q
)
203 bq
->current_write
= q
->prev
;
205 if (bq
->current_read
== q
)
206 bq
->current_read
= q
->next
;
208 pa_memblock_unref(q
->chunk
.memblock
);
210 if (pa_flist_push(PA_STATIC_FLIST_GET(list_items
), q
) < 0)
216 static void drop_backlog(pa_memblockq
*bq
) {
220 boundary
= bq
->read_index
- bq
->maxrewind
;
222 while (bq
->blocks
&& (bq
->blocks
->index
+ (int64_t) bq
->blocks
->chunk
.length
<= boundary
))
223 drop_block(bq
, bq
->blocks
);
226 static pa_bool_t
can_push(pa_memblockq
*bq
, size_t l
) {
231 if (bq
->read_index
> bq
->write_index
) {
232 size_t d
= bq
->read_index
- bq
->write_index
;
240 end
= bq
->blocks_tail
? bq
->blocks_tail
->index
+ (int64_t) bq
->blocks_tail
->chunk
.length
: bq
->write_index
;
242 /* Make sure that the list doesn't get too long */
243 if (bq
->write_index
+ (int64_t) l
> end
)
244 if (bq
->write_index
+ l
- bq
->read_index
> bq
->maxlength
)
250 int pa_memblockq_push(pa_memblockq
* bq
, const pa_memchunk
*uchunk
) {
251 struct list_item
*q
, *n
;
257 pa_assert(uchunk
->memblock
);
258 pa_assert(uchunk
->length
> 0);
259 pa_assert(uchunk
->index
+ uchunk
->length
<= pa_memblock_get_length(uchunk
->memblock
));
261 if (uchunk
->length
% bq
->base
)
264 if (!can_push(bq
, uchunk
->length
))
267 old
= bq
->write_index
;
270 fix_current_write(bq
);
271 q
= bq
->current_write
;
273 /* First we advance the q pointer right of where we want to
277 while (bq
->write_index
+ (int64_t) chunk
.length
> q
->index
)
287 /* We go from back to front to look for the right place to add
288 * this new entry. Drop data we will overwrite on the way */
292 if (bq
->write_index
>= q
->index
+ (int64_t) q
->chunk
.length
)
293 /* We found the entry where we need to place the new entry immediately after */
295 else if (bq
->write_index
+ (int64_t) chunk
.length
<= q
->index
) {
296 /* This entry isn't touched at all, let's skip it */
298 } else if (bq
->write_index
<= q
->index
&&
299 bq
->write_index
+ chunk
.length
>= q
->index
+ q
->chunk
.length
) {
301 /* This entry is fully replaced by the new entry, so let's drop it */
307 } else if (bq
->write_index
>= q
->index
) {
308 /* The write index points into this memblock, so let's
309 * truncate or split it */
311 if (bq
->write_index
+ chunk
.length
< q
->index
+ q
->chunk
.length
) {
313 /* We need to save the end of this memchunk */
317 /* Create a new list entry for the end of thie memchunk */
318 if (!(p
= pa_flist_pop(PA_STATIC_FLIST_GET(list_items
))))
319 p
= pa_xnew(struct list_item
, 1);
322 pa_memblock_ref(p
->chunk
.memblock
);
324 /* Calculate offset */
325 d
= bq
->write_index
+ chunk
.length
- q
->index
;
328 /* Drop it from the new entry */
329 p
->index
= q
->index
+ d
;
330 p
->chunk
.length
-= d
;
332 /* Add it to the list */
334 if ((p
->next
= q
->next
))
343 /* Truncate the chunk */
344 if (!(q
->chunk
.length
= bq
->write_index
- q
->index
)) {
351 /* We had to truncate this block, hence we're now at the right position */
356 pa_assert(bq
->write_index
+ (int64_t)chunk
.length
> q
->index
&&
357 bq
->write_index
+ (int64_t)chunk
.length
< q
->index
+ (int64_t)q
->chunk
.length
&&
358 bq
->write_index
< q
->index
);
360 /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
362 d
= bq
->write_index
+ chunk
.length
- q
->index
;
365 q
->chunk
.length
-= d
;
372 pa_assert(bq
->write_index
>= q
->index
+ (int64_t)q
->chunk
.length
);
373 pa_assert(!q
->next
|| (bq
->write_index
+ (int64_t)chunk
.length
<= q
->next
->index
));
375 /* Try to merge memory blocks */
377 if (q
->chunk
.memblock
== chunk
.memblock
&&
378 q
->chunk
.index
+ (int64_t)q
->chunk
.length
== chunk
.index
&&
379 bq
->write_index
== q
->index
+ (int64_t)q
->chunk
.length
) {
381 q
->chunk
.length
+= chunk
.length
;
382 bq
->write_index
+= chunk
.length
;
386 pa_assert(!bq
->blocks
|| (bq
->write_index
+ (int64_t)chunk
.length
<= bq
->blocks
->index
));
388 if (!(n
= pa_flist_pop(PA_STATIC_FLIST_GET(list_items
))))
389 n
= pa_xnew(struct list_item
, 1);
392 pa_memblock_ref(n
->chunk
.memblock
);
393 n
->index
= bq
->write_index
;
394 bq
->write_index
+= n
->chunk
.length
;
396 n
->next
= q
? q
->next
: bq
->blocks
;
413 delta
= bq
->write_index
- old
;
415 if (delta
>= (int64_t) bq
->requested
) {
416 delta
-= bq
->requested
;
419 bq
->requested
-= delta
;
423 bq
->missing
-= delta
;
428 pa_bool_t
pa_memblockq_prebuf_active(pa_memblockq
*bq
) {
432 return pa_memblockq_get_length(bq
) < bq
->prebuf
;
434 return bq
->prebuf
> 0 && bq
->read_index
>= bq
->write_index
;
437 static pa_bool_t
update_prebuf(pa_memblockq
*bq
) {
442 if (pa_memblockq_get_length(bq
) < bq
->prebuf
)
445 bq
->in_prebuf
= FALSE
;
449 if (bq
->prebuf
> 0 && bq
->read_index
>= bq
->write_index
) {
450 bq
->in_prebuf
= TRUE
;
458 int pa_memblockq_peek(pa_memblockq
* bq
, pa_memchunk
*chunk
) {
463 /* We need to pre-buffer */
464 if (update_prebuf(bq
))
467 fix_current_read(bq
);
469 /* Do we need to spit out silence? */
470 if (!bq
->current_read
|| bq
->current_read
->index
> bq
->read_index
) {
474 /* How much silence shall we return? */
475 if (bq
->current_read
)
476 length
= bq
->current_read
->index
- bq
->read_index
;
477 else if (bq
->write_index
> bq
->read_index
)
478 length
= (size_t) (bq
->write_index
- bq
->read_index
);
482 /* We need to return silence, since no data is yet available */
483 if (bq
->silence
.memblock
) {
484 *chunk
= bq
->silence
;
485 pa_memblock_ref(chunk
->memblock
);
487 if (length
> 0 && length
< chunk
->length
)
488 chunk
->length
= length
;
492 /* If the memblockq is empty, return -1, otherwise return
493 * the time to sleep */
497 chunk
->memblock
= NULL
;
498 chunk
->length
= length
;
505 /* Ok, let's pass real data to the caller */
506 *chunk
= bq
->current_read
->chunk
;
507 pa_memblock_ref(chunk
->memblock
);
509 pa_assert(bq
->read_index
>= bq
->current_read
->index
);
510 d
= bq
->read_index
- bq
->current_read
->index
;
517 void pa_memblockq_drop(pa_memblockq
*bq
, size_t length
) {
520 pa_assert(length
% bq
->base
== 0);
522 old
= bq
->read_index
;
526 /* Do not drop any data when we are in prebuffering mode */
527 if (update_prebuf(bq
))
530 fix_current_read(bq
);
532 if (bq
->current_read
) {
535 /* We go through this piece by piece to make sure we don't
536 * drop more than allowed by prebuf */
538 p
= bq
->current_read
->index
+ bq
->current_read
->chunk
.length
;
539 pa_assert(p
>= bq
->read_index
);
540 d
= p
- bq
->read_index
;
542 if (d
> (int64_t) length
)
550 /* The list is empty, there's nothing we could drop */
551 bq
->read_index
+= length
;
558 delta
= bq
->read_index
- old
;
559 bq
->missing
+= delta
;
562 void pa_memblockq_rewind(pa_memblockq
*bq
, size_t length
) {
564 pa_assert(length
% bq
->base
== 0);
566 /* This is kind of the inverse of pa_memblockq_drop() */
568 bq
->read_index
-= length
;
569 bq
->missing
-= length
;
572 pa_bool_t
pa_memblockq_is_readable(pa_memblockq
*bq
) {
575 if (pa_memblockq_prebuf_active(bq
))
578 if (pa_memblockq_get_length(bq
) <= 0)
584 size_t pa_memblockq_get_length(pa_memblockq
*bq
) {
587 if (bq
->write_index
<= bq
->read_index
)
590 return (size_t) (bq
->write_index
- bq
->read_index
);
593 size_t pa_memblockq_missing(pa_memblockq
*bq
) {
597 if ((l
= pa_memblockq_get_length(bq
)) >= bq
->tlength
)
602 return l
>= bq
->minreq
? l
: 0;
605 void pa_memblockq_seek(pa_memblockq
*bq
, int64_t offset
, pa_seek_mode_t seek
) {
609 old
= bq
->write_index
;
612 case PA_SEEK_RELATIVE
:
613 bq
->write_index
+= offset
;
615 case PA_SEEK_ABSOLUTE
:
616 bq
->write_index
= offset
;
618 case PA_SEEK_RELATIVE_ON_READ
:
619 bq
->write_index
= bq
->read_index
+ offset
;
621 case PA_SEEK_RELATIVE_END
:
622 bq
->write_index
= (bq
->blocks_tail
? bq
->blocks_tail
->index
+ (int64_t) bq
->blocks_tail
->chunk
.length
: bq
->read_index
) + offset
;
625 pa_assert_not_reached();
630 delta
= bq
->write_index
- old
;
632 if (delta
>= (int64_t) bq
->requested
) {
633 delta
-= bq
->requested
;
635 } else if (delta
>= 0) {
636 bq
->requested
-= delta
;
640 bq
->missing
-= delta
;
643 void pa_memblockq_flush(pa_memblockq
*bq
) {
647 pa_memblockq_silence(bq
);
649 old
= bq
->write_index
;
650 bq
->write_index
= bq
->read_index
;
652 pa_memblockq_prebuf_force(bq
);
654 delta
= bq
->write_index
- old
;
656 if (delta
>= (int64_t) bq
->requested
) {
657 delta
-= bq
->requested
;
659 } else if (delta
>= 0) {
660 bq
->requested
-= delta
;
664 bq
->missing
-= delta
;
667 size_t pa_memblockq_get_tlength(pa_memblockq
*bq
) {
673 size_t pa_memblockq_get_minreq(pa_memblockq
*bq
) {
679 int64_t pa_memblockq_get_read_index(pa_memblockq
*bq
) {
682 return bq
->read_index
;
685 int64_t pa_memblockq_get_write_index(pa_memblockq
*bq
) {
688 return bq
->write_index
;
691 int pa_memblockq_push_align(pa_memblockq
* bq
, const pa_memchunk
*chunk
) {
698 return pa_memblockq_push(bq
, chunk
);
700 if (!can_push(bq
, pa_mcalign_csize(bq
->mcalign
, chunk
->length
)))
703 pa_mcalign_push(bq
->mcalign
, chunk
);
705 while (pa_mcalign_pop(bq
->mcalign
, &rchunk
) >= 0) {
707 r
= pa_memblockq_push(bq
, &rchunk
);
708 pa_memblock_unref(rchunk
.memblock
);
711 pa_mcalign_flush(bq
->mcalign
);
719 void pa_memblockq_prebuf_disable(pa_memblockq
*bq
) {
722 bq
->in_prebuf
= FALSE
;
725 void pa_memblockq_prebuf_force(pa_memblockq
*bq
) {
729 bq
->in_prebuf
= TRUE
;
732 size_t pa_memblockq_get_maxlength(pa_memblockq
*bq
) {
735 return bq
->maxlength
;
738 size_t pa_memblockq_get_prebuf(pa_memblockq
*bq
) {
744 size_t pa_memblockq_pop_missing(pa_memblockq
*bq
) {
749 /* pa_log("pop: %lli", bq->missing); */
751 if (bq
->missing
<= 0)
754 l
= (size_t) bq
->missing
;
761 void pa_memblockq_set_maxlength(pa_memblockq
*bq
, size_t maxlength
) {
764 bq
->maxlength
= ((maxlength
+bq
->base
-1)/bq
->base
)*bq
->base
;
766 if (bq
->maxlength
< bq
->base
)
767 bq
->maxlength
= bq
->base
;
769 if (bq
->tlength
> bq
->maxlength
)
770 pa_memblockq_set_tlength(bq
, bq
->maxlength
);
772 if (bq
->prebuf
> bq
->maxlength
)
773 pa_memblockq_set_prebuf(bq
, bq
->maxlength
);
776 void pa_memblockq_set_tlength(pa_memblockq
*bq
, size_t tlength
) {
781 tlength
= bq
->maxlength
;
783 old_tlength
= bq
->tlength
;
784 bq
->tlength
= ((tlength
+bq
->base
-1)/bq
->base
)*bq
->base
;
786 if (bq
->tlength
> bq
->maxlength
)
787 bq
->tlength
= bq
->maxlength
;
789 if (bq
->minreq
> bq
->tlength
)
790 pa_memblockq_set_minreq(bq
, bq
->tlength
);
792 bq
->missing
+= (int64_t) bq
->tlength
- (int64_t) old_tlength
;
795 void pa_memblockq_set_prebuf(pa_memblockq
*bq
, size_t prebuf
) {
798 if (prebuf
== (size_t) -1)
799 prebuf
= bq
->tlength
;
801 bq
->prebuf
= ((prebuf
+bq
->base
-1)/bq
->base
)*bq
->base
;
803 if (prebuf
> 0 && bq
->prebuf
< bq
->base
)
804 bq
->prebuf
= bq
->base
;
806 if (bq
->prebuf
> bq
->maxlength
)
807 bq
->prebuf
= bq
->maxlength
;
809 if (bq
->prebuf
<= 0 || pa_memblockq_get_length(bq
) >= bq
->prebuf
)
810 bq
->in_prebuf
= FALSE
;
812 if (bq
->minreq
> bq
->prebuf
)
813 pa_memblockq_set_minreq(bq
, bq
->prebuf
);
816 void pa_memblockq_set_minreq(pa_memblockq
*bq
, size_t minreq
) {
819 bq
->minreq
= (minreq
/bq
->base
)*bq
->base
;
821 if (bq
->minreq
> bq
->tlength
)
822 bq
->minreq
= bq
->tlength
;
824 if (bq
->minreq
> bq
->prebuf
)
825 bq
->minreq
= bq
->prebuf
;
827 if (bq
->minreq
< bq
->base
)
828 bq
->minreq
= bq
->base
;
831 void pa_memblockq_set_maxrewind(pa_memblockq
*bq
, size_t maxrewind
) {
834 bq
->maxrewind
= (maxrewind
/bq
->base
)*bq
->base
;
837 int pa_memblockq_splice(pa_memblockq
*bq
, pa_memblockq
*source
) {
842 pa_memblockq_prebuf_disable(bq
);
847 if (pa_memblockq_peek(source
, &chunk
) < 0)
850 pa_assert(chunk
.length
> 0);
852 if (chunk
.memblock
) {
854 if (pa_memblockq_push_align(bq
, &chunk
) < 0) {
855 pa_memblock_unref(chunk
.memblock
);
859 pa_memblock_unref(chunk
.memblock
);
861 pa_memblockq_seek(bq
, chunk
.length
, PA_SEEK_RELATIVE
);
863 pa_memblockq_drop(bq
, chunk
.length
);
867 void pa_memblockq_willneed(pa_memblockq
*bq
) {
872 fix_current_read(bq
);
874 for (q
= bq
->current_read
; q
; q
= q
->next
)
875 pa_memchunk_will_need(&q
->chunk
);
878 void pa_memblockq_set_silence(pa_memblockq
*bq
, pa_memchunk
*silence
) {
881 if (bq
->silence
.memblock
)
882 pa_memblock_unref(bq
->silence
.memblock
);
885 bq
->silence
= *silence
;
886 pa_memblock_ref(bq
->silence
.memblock
);
888 pa_memchunk_reset(&bq
->silence
);
891 pa_bool_t
pa_memblockq_is_empty(pa_memblockq
*bq
) {
897 void pa_memblockq_silence(pa_memblockq
*bq
) {
901 drop_block(bq
, bq
->blocks
);
903 pa_assert(bq
->n_blocks
== 0);