1/***
2  This file is part of PulseAudio.
3
4  Copyright 2013 Matthias Wabersich
5  Copyright 2013 Hajime Fujita
6
7  PulseAudio is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 2.1 of the License,
10  or (at your option) any later version.
11
12  PulseAudio is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16
17  You should have received a copy of the GNU Lesser General Public License
18  along with PulseAudio; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20  USA.
21***/
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdlib.h>
28#include <stdint.h>
29#include <limits.h>
30
31#include <pulse/xmalloc.h>
32
33#include <pulsecore/core-error.h>
34#include <pulsecore/macro.h>
35
36#include "raop-packet-buffer.h"
37
38struct pa_raop_packet_buffer {
39    pa_memchunk *packets;
40    pa_mempool *mempool;
41
42    size_t size;
43    size_t count;
44
45    uint16_t seq;
46    size_t pos;
47};
48
49pa_raop_packet_buffer *pa_raop_packet_buffer_new(pa_mempool *mempool, const size_t size) {
50    pa_raop_packet_buffer *pb = pa_xnew0(pa_raop_packet_buffer, 1);
51
52    pa_assert(mempool);
53    pa_assert(size > 0);
54
55    pb->count = 0;
56    pb->size = size;
57    pb->mempool = mempool;
58    pb->packets = pa_xnew0(pa_memchunk, size);
59    pb->seq = pb->pos = 0;
60
61    return pb;
62}
63
64void pa_raop_packet_buffer_free(pa_raop_packet_buffer *pb) {
65    size_t i;
66
67    pa_assert(pb);
68
69    for (i = 0; pb->packets && i < pb->size; i++) {
70        if (pb->packets[i].memblock)
71            pa_memblock_unref(pb->packets[i].memblock);
72        pa_memchunk_reset(&pb->packets[i]);
73    }
74
75    pa_xfree(pb->packets);
76    pb->packets = NULL;
77    pa_xfree(pb);
78}
79
80void pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq) {
81    size_t i;
82
83    pa_assert(pb);
84    pa_assert(pb->packets);
85
86    pb->pos = 0;
87    pb->count = 0;
88    pb->seq = (!seq) ? UINT16_MAX : seq - 1;
89    for (i = 0; i < pb->size; i++) {
90        if (pb->packets[i].memblock)
91            pa_memblock_unref(pb->packets[i].memblock);
92        pa_memchunk_reset(&pb->packets[i]);
93    }
94}
95
96pa_memchunk *pa_raop_packet_buffer_prepare(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size) {
97    pa_memchunk *packet = NULL;
98    size_t i;
99
100    pa_assert(pb);
101    pa_assert(pb->packets);
102
103    if (seq == 0) {
104        /* 0 means seq reached UINT16_MAX and has been wrapped... */
105        pa_assert(pb->seq == UINT16_MAX);
106        pb->seq = 0;
107    } else {
108        /* ...otherwise, seq MUST have be increased! */
109        pa_assert(seq == pb->seq + 1);
110        pb->seq++;
111    }
112
113    i = (pb->pos + 1) % pb->size;
114
115    if (pb->packets[i].memblock)
116        pa_memblock_unref(pb->packets[i].memblock);
117    pa_memchunk_reset(&pb->packets[i]);
118
119    pb->packets[i].memblock = pa_memblock_new(pb->mempool, size);
120    pb->packets[i].length = size;
121    pb->packets[i].index = 0;
122
123    packet = &pb->packets[i];
124
125    if (pb->count < pb->size)
126        pb->count++;
127    pb->pos = i;
128
129    return packet;
130}
131
132pa_memchunk *pa_raop_packet_buffer_retrieve(pa_raop_packet_buffer *pb, uint16_t seq) {
133    pa_memchunk *packet = NULL;
134    size_t delta, i;
135
136    pa_assert(pb);
137    pa_assert(pb->packets);
138
139    if (seq == pb->seq)
140        packet = &pb->packets[pb->pos];
141    else {
142        if (seq < pb->seq) {
143            /* Regular case: pb->seq did not wrapped since seq. */
144            delta = pb->seq - seq;
145        } else {
146            /* Tricky case: pb->seq wrapped since seq! */
147            delta = pb->seq + (UINT16_MAX - seq);
148        }
149
150        /* If the requested packet is too old, do nothing and return */
151        if (delta > pb->count)
152            return NULL;
153
154        i = (pb->size + pb->pos - delta) % pb->size;
155
156        if (delta < pb->size && pb->packets[i].memblock)
157            packet = &pb->packets[i];
158    }
159
160    return packet;
161}
162