153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2013 Matthias Wabersich
553a5a1b3Sopenharmony_ci  Copyright 2013 Hajime Fujita
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
953a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
1053a5a1b3Sopenharmony_ci  or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci  General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1853a5a1b3Sopenharmony_ci  along with PulseAudio; if not, write to the Free Software
1953a5a1b3Sopenharmony_ci  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
2053a5a1b3Sopenharmony_ci  USA.
2153a5a1b3Sopenharmony_ci***/
2253a5a1b3Sopenharmony_ci
2353a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2453a5a1b3Sopenharmony_ci#include <config.h>
2553a5a1b3Sopenharmony_ci#endif
2653a5a1b3Sopenharmony_ci
2753a5a1b3Sopenharmony_ci#include <stdlib.h>
2853a5a1b3Sopenharmony_ci#include <stdint.h>
2953a5a1b3Sopenharmony_ci#include <limits.h>
3053a5a1b3Sopenharmony_ci
3153a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3253a5a1b3Sopenharmony_ci
3353a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
3453a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3553a5a1b3Sopenharmony_ci
3653a5a1b3Sopenharmony_ci#include "raop-packet-buffer.h"
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_cistruct pa_raop_packet_buffer {
3953a5a1b3Sopenharmony_ci    pa_memchunk *packets;
4053a5a1b3Sopenharmony_ci    pa_mempool *mempool;
4153a5a1b3Sopenharmony_ci
4253a5a1b3Sopenharmony_ci    size_t size;
4353a5a1b3Sopenharmony_ci    size_t count;
4453a5a1b3Sopenharmony_ci
4553a5a1b3Sopenharmony_ci    uint16_t seq;
4653a5a1b3Sopenharmony_ci    size_t pos;
4753a5a1b3Sopenharmony_ci};
4853a5a1b3Sopenharmony_ci
4953a5a1b3Sopenharmony_cipa_raop_packet_buffer *pa_raop_packet_buffer_new(pa_mempool *mempool, const size_t size) {
5053a5a1b3Sopenharmony_ci    pa_raop_packet_buffer *pb = pa_xnew0(pa_raop_packet_buffer, 1);
5153a5a1b3Sopenharmony_ci
5253a5a1b3Sopenharmony_ci    pa_assert(mempool);
5353a5a1b3Sopenharmony_ci    pa_assert(size > 0);
5453a5a1b3Sopenharmony_ci
5553a5a1b3Sopenharmony_ci    pb->count = 0;
5653a5a1b3Sopenharmony_ci    pb->size = size;
5753a5a1b3Sopenharmony_ci    pb->mempool = mempool;
5853a5a1b3Sopenharmony_ci    pb->packets = pa_xnew0(pa_memchunk, size);
5953a5a1b3Sopenharmony_ci    pb->seq = pb->pos = 0;
6053a5a1b3Sopenharmony_ci
6153a5a1b3Sopenharmony_ci    return pb;
6253a5a1b3Sopenharmony_ci}
6353a5a1b3Sopenharmony_ci
6453a5a1b3Sopenharmony_civoid pa_raop_packet_buffer_free(pa_raop_packet_buffer *pb) {
6553a5a1b3Sopenharmony_ci    size_t i;
6653a5a1b3Sopenharmony_ci
6753a5a1b3Sopenharmony_ci    pa_assert(pb);
6853a5a1b3Sopenharmony_ci
6953a5a1b3Sopenharmony_ci    for (i = 0; pb->packets && i < pb->size; i++) {
7053a5a1b3Sopenharmony_ci        if (pb->packets[i].memblock)
7153a5a1b3Sopenharmony_ci            pa_memblock_unref(pb->packets[i].memblock);
7253a5a1b3Sopenharmony_ci        pa_memchunk_reset(&pb->packets[i]);
7353a5a1b3Sopenharmony_ci    }
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_ci    pa_xfree(pb->packets);
7653a5a1b3Sopenharmony_ci    pb->packets = NULL;
7753a5a1b3Sopenharmony_ci    pa_xfree(pb);
7853a5a1b3Sopenharmony_ci}
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_civoid pa_raop_packet_buffer_reset(pa_raop_packet_buffer *pb, uint16_t seq) {
8153a5a1b3Sopenharmony_ci    size_t i;
8253a5a1b3Sopenharmony_ci
8353a5a1b3Sopenharmony_ci    pa_assert(pb);
8453a5a1b3Sopenharmony_ci    pa_assert(pb->packets);
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ci    pb->pos = 0;
8753a5a1b3Sopenharmony_ci    pb->count = 0;
8853a5a1b3Sopenharmony_ci    pb->seq = (!seq) ? UINT16_MAX : seq - 1;
8953a5a1b3Sopenharmony_ci    for (i = 0; i < pb->size; i++) {
9053a5a1b3Sopenharmony_ci        if (pb->packets[i].memblock)
9153a5a1b3Sopenharmony_ci            pa_memblock_unref(pb->packets[i].memblock);
9253a5a1b3Sopenharmony_ci        pa_memchunk_reset(&pb->packets[i]);
9353a5a1b3Sopenharmony_ci    }
9453a5a1b3Sopenharmony_ci}
9553a5a1b3Sopenharmony_ci
9653a5a1b3Sopenharmony_cipa_memchunk *pa_raop_packet_buffer_prepare(pa_raop_packet_buffer *pb, uint16_t seq, const size_t size) {
9753a5a1b3Sopenharmony_ci    pa_memchunk *packet = NULL;
9853a5a1b3Sopenharmony_ci    size_t i;
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci    pa_assert(pb);
10153a5a1b3Sopenharmony_ci    pa_assert(pb->packets);
10253a5a1b3Sopenharmony_ci
10353a5a1b3Sopenharmony_ci    if (seq == 0) {
10453a5a1b3Sopenharmony_ci        /* 0 means seq reached UINT16_MAX and has been wrapped... */
10553a5a1b3Sopenharmony_ci        pa_assert(pb->seq == UINT16_MAX);
10653a5a1b3Sopenharmony_ci        pb->seq = 0;
10753a5a1b3Sopenharmony_ci    } else {
10853a5a1b3Sopenharmony_ci        /* ...otherwise, seq MUST have be increased! */
10953a5a1b3Sopenharmony_ci        pa_assert(seq == pb->seq + 1);
11053a5a1b3Sopenharmony_ci        pb->seq++;
11153a5a1b3Sopenharmony_ci    }
11253a5a1b3Sopenharmony_ci
11353a5a1b3Sopenharmony_ci    i = (pb->pos + 1) % pb->size;
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_ci    if (pb->packets[i].memblock)
11653a5a1b3Sopenharmony_ci        pa_memblock_unref(pb->packets[i].memblock);
11753a5a1b3Sopenharmony_ci    pa_memchunk_reset(&pb->packets[i]);
11853a5a1b3Sopenharmony_ci
11953a5a1b3Sopenharmony_ci    pb->packets[i].memblock = pa_memblock_new(pb->mempool, size);
12053a5a1b3Sopenharmony_ci    pb->packets[i].length = size;
12153a5a1b3Sopenharmony_ci    pb->packets[i].index = 0;
12253a5a1b3Sopenharmony_ci
12353a5a1b3Sopenharmony_ci    packet = &pb->packets[i];
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci    if (pb->count < pb->size)
12653a5a1b3Sopenharmony_ci        pb->count++;
12753a5a1b3Sopenharmony_ci    pb->pos = i;
12853a5a1b3Sopenharmony_ci
12953a5a1b3Sopenharmony_ci    return packet;
13053a5a1b3Sopenharmony_ci}
13153a5a1b3Sopenharmony_ci
13253a5a1b3Sopenharmony_cipa_memchunk *pa_raop_packet_buffer_retrieve(pa_raop_packet_buffer *pb, uint16_t seq) {
13353a5a1b3Sopenharmony_ci    pa_memchunk *packet = NULL;
13453a5a1b3Sopenharmony_ci    size_t delta, i;
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci    pa_assert(pb);
13753a5a1b3Sopenharmony_ci    pa_assert(pb->packets);
13853a5a1b3Sopenharmony_ci
13953a5a1b3Sopenharmony_ci    if (seq == pb->seq)
14053a5a1b3Sopenharmony_ci        packet = &pb->packets[pb->pos];
14153a5a1b3Sopenharmony_ci    else {
14253a5a1b3Sopenharmony_ci        if (seq < pb->seq) {
14353a5a1b3Sopenharmony_ci            /* Regular case: pb->seq did not wrapped since seq. */
14453a5a1b3Sopenharmony_ci            delta = pb->seq - seq;
14553a5a1b3Sopenharmony_ci        } else {
14653a5a1b3Sopenharmony_ci            /* Tricky case: pb->seq wrapped since seq! */
14753a5a1b3Sopenharmony_ci            delta = pb->seq + (UINT16_MAX - seq);
14853a5a1b3Sopenharmony_ci        }
14953a5a1b3Sopenharmony_ci
15053a5a1b3Sopenharmony_ci        /* If the requested packet is too old, do nothing and return */
15153a5a1b3Sopenharmony_ci        if (delta > pb->count)
15253a5a1b3Sopenharmony_ci            return NULL;
15353a5a1b3Sopenharmony_ci
15453a5a1b3Sopenharmony_ci        i = (pb->size + pb->pos - delta) % pb->size;
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci        if (delta < pb->size && pb->packets[i].memblock)
15753a5a1b3Sopenharmony_ci            packet = &pb->packets[i];
15853a5a1b3Sopenharmony_ci    }
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci    return packet;
16153a5a1b3Sopenharmony_ci}
162