]>
code.delx.au - pulseaudio/blob - src/pulsecore/memblockq.c
2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
32 #include <pulse/xmalloc.h>
34 #include <pulsecore/log.h>
35 #include <pulsecore/mcalign.h>
36 #include <pulsecore/macro.h>
37 #include <pulsecore/flist.h>
39 #include "memblockq.h"
42 struct list_item
*next
, *prev
;
47 PA_STATIC_FLIST_DECLARE(list_items
, 0, pa_xfree
);
50 struct list_item
*blocks
, *blocks_tail
;
51 struct list_item
*current_read
, *current_write
;
53 size_t maxlength
, tlength
, base
, prebuf
, minreq
, maxrewind
;
54 int64_t read_index
, write_index
;
62 pa_memblockq
* pa_memblockq_new(
70 pa_memchunk
*silence
) {
76 bq
= pa_xnew(pa_memblockq
, 1);
77 bq
->blocks
= bq
->blocks_tail
= NULL
;
78 bq
->current_read
= bq
->current_write
= NULL
;
82 bq
->read_index
= bq
->write_index
= idx
;
84 pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
85 (unsigned long) maxlength
, (unsigned long) tlength
, (unsigned long) base
, (unsigned long) prebuf
, (unsigned long) minreq
, (unsigned long) maxrewind
);
87 bq
->missing
= bq
->requested
= bq
->maxlength
= bq
->tlength
= bq
->prebuf
= bq
->minreq
= bq
->maxrewind
= 0;
90 pa_memblockq_set_maxlength(bq
, maxlength
);
91 pa_memblockq_set_tlength(bq
, tlength
);
92 pa_memblockq_set_prebuf(bq
, prebuf
);
93 pa_memblockq_set_minreq(bq
, minreq
);
94 pa_memblockq_set_maxrewind(bq
, maxrewind
);
96 pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
97 (unsigned long) bq
->maxlength
, (unsigned long) bq
->tlength
, (unsigned long) bq
->base
, (unsigned long) bq
->prebuf
, (unsigned long) bq
->minreq
, (unsigned long) bq
->maxrewind
);
100 bq
->silence
= *silence
;
101 pa_memblock_ref(bq
->silence
.memblock
);
103 pa_memchunk_reset(&bq
->silence
);
105 bq
->mcalign
= pa_mcalign_new(bq
->base
);
110 void pa_memblockq_free(pa_memblockq
* bq
) {
113 pa_memblockq_flush(bq
);
115 if (bq
->silence
.memblock
)
116 pa_memblock_unref(bq
->silence
.memblock
);
119 pa_mcalign_free(bq
->mcalign
);
124 static void fix_current_read(pa_memblockq
*bq
) {
127 if (PA_UNLIKELY(!bq
->blocks
)) {
128 bq
->current_read
= NULL
;
132 if (PA_UNLIKELY(!bq
->current_read
))
133 bq
->current_read
= bq
->blocks
;
136 while (PA_UNLIKELY(bq
->current_read
->index
> bq
->read_index
))
138 if (bq
->current_read
->prev
)
139 bq
->current_read
= bq
->current_read
->prev
;
144 while (PA_LIKELY(bq
->current_read
!= NULL
) && PA_UNLIKELY(bq
->current_read
->index
+ (int64_t) bq
->current_read
->chunk
.length
<= bq
->read_index
))
145 bq
->current_read
= bq
->current_read
->next
;
147 /* At this point current_read will either point at or left of the
148 next block to play. It may be NULL in case everything in
149 the queue was already played */
152 static void fix_current_write(pa_memblockq
*bq
) {
155 if (PA_UNLIKELY(!bq
->blocks
)) {
156 bq
->current_write
= NULL
;
160 if (PA_UNLIKELY(!bq
->current_write
))
161 bq
->current_write
= bq
->blocks_tail
;
164 while (PA_UNLIKELY(bq
->current_write
->index
+ (int64_t) bq
->current_write
->chunk
.length
<= bq
->write_index
))
166 if (bq
->current_write
->next
)
167 bq
->current_write
= bq
->current_write
->next
;
172 while (PA_LIKELY(bq
->current_write
!= NULL
) && PA_UNLIKELY(bq
->current_write
->index
> bq
->write_index
))
173 bq
->current_write
= bq
->current_write
->prev
;
175 /* At this point current_write will either point at or right of
176 the next block to write data to. It may be NULL in case
177 everything in the queue is still to be played */
180 static void drop_block(pa_memblockq
*bq
, struct list_item
*q
) {
184 pa_assert(bq
->n_blocks
>= 1);
187 q
->prev
->next
= q
->next
;
189 pa_assert(bq
->blocks
== q
);
190 bq
->blocks
= q
->next
;
194 q
->next
->prev
= q
->prev
;
196 pa_assert(bq
->blocks_tail
== q
);
197 bq
->blocks_tail
= q
->prev
;
200 if (bq
->current_write
== q
)
201 bq
->current_write
= q
->prev
;
203 if (bq
->current_read
== q
)
204 bq
->current_read
= q
->next
;
206 pa_memblock_unref(q
->chunk
.memblock
);
208 if (pa_flist_push(PA_STATIC_FLIST_GET(list_items
), q
) < 0)
214 static void drop_backlog(pa_memblockq
*bq
) {
218 boundary
= bq
->read_index
- bq
->maxrewind
;
220 while (bq
->blocks
&& (bq
->blocks
->index
+ (int64_t) bq
->blocks
->chunk
.length
<= boundary
))
221 drop_block(bq
, bq
->blocks
);
224 static pa_bool_t
can_push(pa_memblockq
*bq
, size_t l
) {
229 if (bq
->read_index
> bq
->write_index
) {
230 size_t d
= bq
->read_index
- bq
->write_index
;
238 end
= bq
->blocks_tail
? bq
->blocks_tail
->index
+ (int64_t) bq
->blocks_tail
->chunk
.length
: bq
->write_index
;
240 /* Make sure that the list doesn't get too long */
241 if (bq
->write_index
+ (int64_t) l
> end
)
242 if (bq
->write_index
+ l
- bq
->read_index
> bq
->maxlength
)
248 int pa_memblockq_push(pa_memblockq
* bq
, const pa_memchunk
*uchunk
) {
249 struct list_item
*q
, *n
;
255 pa_assert(uchunk
->memblock
);
256 pa_assert(uchunk
->length
> 0);
257 pa_assert(uchunk
->index
+ uchunk
->length
<= pa_memblock_get_length(uchunk
->memblock
));
259 if (uchunk
->length
% bq
->base
)
262 if (!can_push(bq
, uchunk
->length
))
265 old
= bq
->write_index
;
268 fix_current_write(bq
);
269 q
= bq
->current_write
;
271 /* First we advance the q pointer right of where we want to
275 while (bq
->write_index
+ (int64_t) chunk
.length
> q
->index
)
285 /* We go from back to front to look for the right place to add
286 * this new entry. Drop data we will overwrite on the way */
290 if (bq
->write_index
>= q
->index
+ (int64_t) q
->chunk
.length
)
291 /* We found the entry where we need to place the new entry immediately after */
293 else if (bq
->write_index
+ (int64_t) chunk
.length
<= q
->index
) {
294 /* This entry isn't touched at all, let's skip it */
296 } else if (bq
->write_index
<= q
->index
&&
297 bq
->write_index
+ chunk
.length
>= q
->index
+ q
->chunk
.length
) {
299 /* This entry is fully replaced by the new entry, so let's drop it */
305 } else if (bq
->write_index
>= q
->index
) {
306 /* The write index points into this memblock, so let's
307 * truncate or split it */
309 if (bq
->write_index
+ chunk
.length
< q
->index
+ q
->chunk
.length
) {
311 /* We need to save the end of this memchunk */
315 /* Create a new list entry for the end of thie memchunk */
316 if (!(p
= pa_flist_pop(PA_STATIC_FLIST_GET(list_items
))))
317 p
= pa_xnew(struct list_item
, 1);
320 pa_memblock_ref(p
->chunk
.memblock
);
322 /* Calculate offset */
323 d
= bq
->write_index
+ chunk
.length
- q
->index
;
326 /* Drop it from the new entry */
327 p
->index
= q
->index
+ d
;
328 p
->chunk
.length
-= d
;
330 /* Add it to the list */
332 if ((p
->next
= q
->next
))
341 /* Truncate the chunk */
342 if (!(q
->chunk
.length
= bq
->write_index
- q
->index
)) {
349 /* We had to truncate this block, hence we're now at the right position */
354 pa_assert(bq
->write_index
+ (int64_t)chunk
.length
> q
->index
&&
355 bq
->write_index
+ (int64_t)chunk
.length
< q
->index
+ (int64_t)q
->chunk
.length
&&
356 bq
->write_index
< q
->index
);
358 /* The job overwrites the current entry at the end, so let's drop the beginning of this entry */
360 d
= bq
->write_index
+ chunk
.length
- q
->index
;
363 q
->chunk
.length
-= d
;
370 pa_assert(bq
->write_index
>= q
->index
+ (int64_t)q
->chunk
.length
);
371 pa_assert(!q
->next
|| (bq
->write_index
+ (int64_t)chunk
.length
<= q
->next
->index
));
373 /* Try to merge memory blocks */
375 if (q
->chunk
.memblock
== chunk
.memblock
&&
376 q
->chunk
.index
+ (int64_t)q
->chunk
.length
== chunk
.index
&&
377 bq
->write_index
== q
->index
+ (int64_t)q
->chunk
.length
) {
379 q
->chunk
.length
+= chunk
.length
;
380 bq
->write_index
+= chunk
.length
;
384 pa_assert(!bq
->blocks
|| (bq
->write_index
+ (int64_t)chunk
.length
<= bq
->blocks
->index
));
386 if (!(n
= pa_flist_pop(PA_STATIC_FLIST_GET(list_items
))))
387 n
= pa_xnew(struct list_item
, 1);
390 pa_memblock_ref(n
->chunk
.memblock
);
391 n
->index
= bq
->write_index
;
392 bq
->write_index
+= n
->chunk
.length
;
394 n
->next
= q
? q
->next
: bq
->blocks
;
411 delta
= bq
->write_index
- old
;
413 if (delta
>= (int64_t) bq
->requested
) {
414 delta
-= bq
->requested
;
417 bq
->requested
-= delta
;
421 bq
->missing
-= delta
;
426 pa_bool_t
pa_memblockq_prebuf_active(pa_memblockq
*bq
) {
430 return pa_memblockq_get_length(bq
) < bq
->prebuf
;
432 return bq
->prebuf
> 0 && bq
->read_index
>= bq
->write_index
;
435 static pa_bool_t
update_prebuf(pa_memblockq
*bq
) {
440 if (pa_memblockq_get_length(bq
) < bq
->prebuf
)
443 bq
->in_prebuf
= FALSE
;
447 if (bq
->prebuf
> 0 && bq
->read_index
>= bq
->write_index
) {
448 bq
->in_prebuf
= TRUE
;
456 int pa_memblockq_peek(pa_memblockq
* bq
, pa_memchunk
*chunk
) {
461 /* We need to pre-buffer */
462 if (update_prebuf(bq
))
465 fix_current_read(bq
);
467 /* Do we need to spit out silence? */
468 if (!bq
->current_read
|| bq
->current_read
->index
> bq
->read_index
) {
472 /* How much silence shall we return? */
473 if (bq
->current_read
)
474 length
= bq
->current_read
->index
- bq
->read_index
;
475 else if (bq
->write_index
> bq
->read_index
)
476 length
= (size_t) (bq
->write_index
- bq
->read_index
);
480 /* We need to return silence, since no data is yet available */
481 if (bq
->silence
.memblock
) {
482 *chunk
= bq
->silence
;
483 pa_memblock_ref(chunk
->memblock
);
485 if (length
> 0 && length
< chunk
->length
)
486 chunk
->length
= length
;
490 /* If the memblockq is empty, return -1, otherwise return
491 * the time to sleep */
495 chunk
->memblock
= NULL
;
496 chunk
->length
= length
;
503 /* Ok, let's pass real data to the caller */
504 *chunk
= bq
->current_read
->chunk
;
505 pa_memblock_ref(chunk
->memblock
);
507 pa_assert(bq
->read_index
>= bq
->current_read
->index
);
508 d
= bq
->read_index
- bq
->current_read
->index
;
515 void pa_memblockq_drop(pa_memblockq
*bq
, size_t length
) {
518 pa_assert(length
% bq
->base
== 0);
520 old
= bq
->read_index
;
524 /* Do not drop any data when we are in prebuffering mode */
525 if (update_prebuf(bq
))
528 fix_current_read(bq
);
530 if (bq
->current_read
) {
533 /* We go through this piece by piece to make sure we don't
534 * drop more than allowed by prebuf */
536 p
= bq
->current_read
->index
+ bq
->current_read
->chunk
.length
;
537 pa_assert(p
>= bq
->read_index
);
538 d
= p
- bq
->read_index
;
540 if (d
> (int64_t) length
)
548 /* The list is empty, there's nothing we could drop */
549 bq
->read_index
+= length
;
556 delta
= bq
->read_index
- old
;
557 bq
->missing
+= delta
;
560 void pa_memblockq_rewind(pa_memblockq
*bq
, size_t length
) {
562 pa_assert(length
% bq
->base
== 0);
564 /* This is kind of the inverse of pa_memblockq_drop() */
566 bq
->read_index
-= length
;
567 bq
->missing
-= length
;
570 pa_bool_t
pa_memblockq_is_readable(pa_memblockq
*bq
) {
573 if (pa_memblockq_prebuf_active(bq
))
576 if (pa_memblockq_get_length(bq
) <= 0)
582 size_t pa_memblockq_get_length(pa_memblockq
*bq
) {
585 if (bq
->write_index
<= bq
->read_index
)
588 return (size_t) (bq
->write_index
- bq
->read_index
);
591 size_t pa_memblockq_missing(pa_memblockq
*bq
) {
595 if ((l
= pa_memblockq_get_length(bq
)) >= bq
->tlength
)
600 return l
>= bq
->minreq
? l
: 0;
603 void pa_memblockq_seek(pa_memblockq
*bq
, int64_t offset
, pa_seek_mode_t seek
) {
607 old
= bq
->write_index
;
610 case PA_SEEK_RELATIVE
:
611 bq
->write_index
+= offset
;
613 case PA_SEEK_ABSOLUTE
:
614 bq
->write_index
= offset
;
616 case PA_SEEK_RELATIVE_ON_READ
:
617 bq
->write_index
= bq
->read_index
+ offset
;
619 case PA_SEEK_RELATIVE_END
:
620 bq
->write_index
= (bq
->blocks_tail
? bq
->blocks_tail
->index
+ (int64_t) bq
->blocks_tail
->chunk
.length
: bq
->read_index
) + offset
;
623 pa_assert_not_reached();
628 delta
= bq
->write_index
- old
;
630 if (delta
>= (int64_t) bq
->requested
) {
631 delta
-= bq
->requested
;
633 } else if (delta
>= 0) {
634 bq
->requested
-= delta
;
638 bq
->missing
-= delta
;
641 void pa_memblockq_flush(pa_memblockq
*bq
) {
645 pa_memblockq_silence(bq
);
647 old
= bq
->write_index
;
648 bq
->write_index
= bq
->read_index
;
650 pa_memblockq_prebuf_force(bq
);
652 delta
= bq
->write_index
- old
;
654 if (delta
>= (int64_t) bq
->requested
) {
655 delta
-= bq
->requested
;
657 } else if (delta
>= 0) {
658 bq
->requested
-= delta
;
662 bq
->missing
-= delta
;
665 size_t pa_memblockq_get_tlength(pa_memblockq
*bq
) {
671 size_t pa_memblockq_get_minreq(pa_memblockq
*bq
) {
677 int64_t pa_memblockq_get_read_index(pa_memblockq
*bq
) {
680 return bq
->read_index
;
683 int64_t pa_memblockq_get_write_index(pa_memblockq
*bq
) {
686 return bq
->write_index
;
689 int pa_memblockq_push_align(pa_memblockq
* bq
, const pa_memchunk
*chunk
) {
696 return pa_memblockq_push(bq
, chunk
);
698 if (!can_push(bq
, pa_mcalign_csize(bq
->mcalign
, chunk
->length
)))
701 pa_mcalign_push(bq
->mcalign
, chunk
);
703 while (pa_mcalign_pop(bq
->mcalign
, &rchunk
) >= 0) {
705 r
= pa_memblockq_push(bq
, &rchunk
);
706 pa_memblock_unref(rchunk
.memblock
);
709 pa_mcalign_flush(bq
->mcalign
);
717 void pa_memblockq_prebuf_disable(pa_memblockq
*bq
) {
720 bq
->in_prebuf
= FALSE
;
723 void pa_memblockq_prebuf_force(pa_memblockq
*bq
) {
727 bq
->in_prebuf
= TRUE
;
730 size_t pa_memblockq_get_maxlength(pa_memblockq
*bq
) {
733 return bq
->maxlength
;
736 size_t pa_memblockq_get_prebuf(pa_memblockq
*bq
) {
742 size_t pa_memblockq_pop_missing(pa_memblockq
*bq
) {
747 /* pa_log("pop: %lli", bq->missing); */
749 if (bq
->missing
<= 0)
752 l
= (size_t) bq
->missing
;
759 void pa_memblockq_set_maxlength(pa_memblockq
*bq
, size_t maxlength
) {
762 bq
->maxlength
= ((maxlength
+bq
->base
-1)/bq
->base
)*bq
->base
;
764 if (bq
->maxlength
< bq
->base
)
765 bq
->maxlength
= bq
->base
;
767 if (bq
->tlength
> bq
->maxlength
)
768 pa_memblockq_set_tlength(bq
, bq
->maxlength
);
770 if (bq
->prebuf
> bq
->maxlength
)
771 pa_memblockq_set_prebuf(bq
, bq
->maxlength
);
774 void pa_memblockq_set_tlength(pa_memblockq
*bq
, size_t tlength
) {
779 tlength
= bq
->maxlength
;
781 old_tlength
= bq
->tlength
;
782 bq
->tlength
= ((tlength
+bq
->base
-1)/bq
->base
)*bq
->base
;
784 if (bq
->tlength
> bq
->maxlength
)
785 bq
->tlength
= bq
->maxlength
;
787 if (bq
->prebuf
> bq
->tlength
)
788 pa_memblockq_set_prebuf(bq
, bq
->tlength
);
790 if (bq
->minreq
> bq
->tlength
)
791 pa_memblockq_set_minreq(bq
, bq
->tlength
);
793 bq
->missing
+= (int64_t) bq
->tlength
- (int64_t) old_tlength
;
796 void pa_memblockq_set_prebuf(pa_memblockq
*bq
, size_t prebuf
) {
799 if (prebuf
== (size_t) -1)
800 prebuf
= bq
->tlength
;
802 bq
->prebuf
= ((prebuf
+bq
->base
-1)/bq
->base
)*bq
->base
;
804 if (prebuf
> 0 && bq
->prebuf
< bq
->base
)
805 bq
->prebuf
= bq
->base
;
807 if (bq
->prebuf
> bq
->tlength
)
808 bq
->prebuf
= bq
->tlength
;
810 if (bq
->prebuf
<= 0 || pa_memblockq_get_length(bq
) >= bq
->prebuf
)
811 bq
->in_prebuf
= FALSE
;
813 if (bq
->minreq
> bq
->prebuf
)
814 pa_memblockq_set_minreq(bq
, bq
->prebuf
);
817 void pa_memblockq_set_minreq(pa_memblockq
*bq
, size_t minreq
) {
820 bq
->minreq
= (minreq
/bq
->base
)*bq
->base
;
822 if (bq
->minreq
> bq
->tlength
)
823 bq
->minreq
= bq
->tlength
;
825 if (bq
->minreq
> bq
->prebuf
)
826 bq
->minreq
= bq
->prebuf
;
828 if (bq
->minreq
< bq
->base
)
829 bq
->minreq
= bq
->base
;
832 void pa_memblockq_set_maxrewind(pa_memblockq
*bq
, size_t maxrewind
) {
835 bq
->maxrewind
= (maxrewind
/bq
->base
)*bq
->base
;
838 int pa_memblockq_splice(pa_memblockq
*bq
, pa_memblockq
*source
) {
843 pa_memblockq_prebuf_disable(bq
);
848 if (pa_memblockq_peek(source
, &chunk
) < 0)
851 pa_assert(chunk
.length
> 0);
853 if (chunk
.memblock
) {
855 if (pa_memblockq_push_align(bq
, &chunk
) < 0) {
856 pa_memblock_unref(chunk
.memblock
);
860 pa_memblock_unref(chunk
.memblock
);
862 pa_memblockq_seek(bq
, chunk
.length
, PA_SEEK_RELATIVE
);
864 pa_memblockq_drop(bq
, chunk
.length
);
868 void pa_memblockq_willneed(pa_memblockq
*bq
) {
873 fix_current_read(bq
);
875 for (q
= bq
->current_read
; q
; q
= q
->next
)
876 pa_memchunk_will_need(&q
->chunk
);
879 void pa_memblockq_set_silence(pa_memblockq
*bq
, pa_memchunk
*silence
) {
882 if (bq
->silence
.memblock
)
883 pa_memblock_unref(bq
->silence
.memblock
);
886 bq
->silence
= *silence
;
887 pa_memblock_ref(bq
->silence
.memblock
);
889 pa_memchunk_reset(&bq
->silence
);
892 pa_bool_t
pa_memblockq_is_empty(pa_memblockq
*bq
) {
898 void pa_memblockq_silence(pa_memblockq
*bq
) {
902 drop_block(bq
, bq
->blocks
);
904 pa_assert(bq
->n_blocks
== 0);
907 unsigned pa_memblockq_get_nblocks(pa_memblockq
*bq
) {
913 size_t pa_memblockq_get_base(pa_memblockq
*bq
) {