2016-11-06 12:54:00 -06:00
|
|
|
/***
|
|
|
|
|
Circular buffer for RTP audio packets with random access support
|
|
|
|
|
by RTP sequence number.
|
|
|
|
|
|
|
|
|
|
Copyright 2013 Matthias Wabersich, Hajime Fujita
|
|
|
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU Lesser General Public License as published
|
|
|
|
|
by the Free Software Foundation; either version 2.1 of the License,
|
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This is distributed in the hope that it will be useful, but
|
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
|
along with PulseAudio; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
|
|
|
USA.
|
|
|
|
|
|
|
|
|
|
***/
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <pulsecore/core-error.h>
|
2016-11-06 12:54:07 -06:00
|
|
|
#include "raop-client.h"
|
2016-11-06 12:54:00 -06:00
|
|
|
|
2016-11-06 12:54:07 -06:00
|
|
|
#include "raop-packet-buffer.h"
|
2016-11-06 12:54:00 -06:00
|
|
|
|
|
|
|
|
/* FRAMES_PER_PACKET*2*2 + sizeof(udp_audio_header) + sizeof(ALAC header), unencoded */
|
2016-11-06 12:54:07 -06:00
|
|
|
#define PACKET_SIZE_MAX (352*2*2 + 12 + 7) /* FIXME; hardcoded constant ! */
|
2016-11-06 12:54:00 -06:00
|
|
|
/* Header room for packet retransmission header */
|
|
|
|
|
#define RETRANS_HEADER_ROOM 4
|
|
|
|
|
|
|
|
|
|
/* Packet element */
|
|
|
|
|
struct pa_raop_packet_element {
|
|
|
|
|
uint16_t seq_num; /* RTP sequence number (in host byte order) */
|
|
|
|
|
ssize_t length; /* Actual packet length */
|
|
|
|
|
/* Packet data including RTP header */
|
|
|
|
|
uint8_t data[PACKET_SIZE_MAX + RETRANS_HEADER_ROOM];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Buffer struct */
|
|
|
|
|
struct pa_raop_packet_buffer {
|
|
|
|
|
size_t size; /* max number of packets in buffer */
|
|
|
|
|
size_t start; /* index of oldest packet */
|
|
|
|
|
size_t count; /* number of packets in buffer */
|
|
|
|
|
uint16_t first_seq_num; /* Sequence number of first packet in buffer */
|
|
|
|
|
uint16_t latest_seq_num; /* Debug purpose */
|
|
|
|
|
pa_raop_packet_element *packets; /* Packet element pointer */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pa_raop_packet_buffer *pa_raop_pb_new(size_t size) {
|
|
|
|
|
pa_raop_packet_buffer *pb = pa_xmalloc0(sizeof(*pb));
|
|
|
|
|
|
|
|
|
|
pb->size = size;
|
|
|
|
|
pb->packets = (pa_raop_packet_element *)
|
|
|
|
|
pa_xmalloc(size * sizeof(pa_raop_packet_element));
|
|
|
|
|
|
|
|
|
|
pa_raop_pb_clear(pb);
|
|
|
|
|
|
|
|
|
|
return pb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pa_raop_pb_clear(pa_raop_packet_buffer *pb) {
|
|
|
|
|
pb->start = 0;
|
|
|
|
|
pb->count = 0;
|
|
|
|
|
pb->first_seq_num = 0;
|
|
|
|
|
pb->latest_seq_num = 0;
|
|
|
|
|
memset(pb->packets, 0, pb->size * sizeof(pa_raop_packet_element));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pa_raop_pb_delete(pa_raop_packet_buffer *pb) {
|
|
|
|
|
pa_xfree(pb->packets);
|
|
|
|
|
pa_xfree(pb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int pb_is_full(pa_raop_packet_buffer *pb) {
|
|
|
|
|
return pb->count == pb->size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int pb_is_empty(pa_raop_packet_buffer *pb) {
|
|
|
|
|
return pb->count == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static pa_raop_packet_element *pb_prepare_write(pa_raop_packet_buffer *pb, uint16_t seq) {
|
|
|
|
|
size_t end = (pb->start + pb->count) % pb->size;
|
|
|
|
|
pa_raop_packet_element *packet;
|
|
|
|
|
|
|
|
|
|
/* Set first packet sequence number in buffer if buffer is empty */
|
|
|
|
|
if (pb_is_empty(pb))
|
|
|
|
|
pb->first_seq_num = seq;
|
|
|
|
|
else
|
|
|
|
|
pa_assert((uint16_t) (pb->latest_seq_num + 1) == seq);
|
|
|
|
|
|
|
|
|
|
packet = &pb->packets[end];
|
|
|
|
|
|
|
|
|
|
if (pb_is_full(pb)) {
|
|
|
|
|
pb->start = (pb->start + 1) % pb->size; /* full, overwrite */
|
|
|
|
|
|
|
|
|
|
/* Set first packet sequence number in buffer
|
|
|
|
|
to new start packet sequence number */
|
|
|
|
|
pb->first_seq_num = pb->packets[pb->start].seq_num;
|
|
|
|
|
} else
|
|
|
|
|
++ pb->count;
|
|
|
|
|
|
|
|
|
|
pb->latest_seq_num = seq;
|
|
|
|
|
|
|
|
|
|
return packet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write packet data to packet buffer */
|
|
|
|
|
void pa_raop_pb_write_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, const uint8_t *packet_data, ssize_t packet_length) {
|
|
|
|
|
pa_raop_packet_element *packet;
|
|
|
|
|
|
|
|
|
|
pa_assert(pb);
|
|
|
|
|
pa_assert(packet_data);
|
|
|
|
|
pa_assert(packet_length <= PACKET_SIZE_MAX);
|
|
|
|
|
|
|
|
|
|
packet = pb_prepare_write(pb, seq_num);
|
|
|
|
|
packet->seq_num = seq_num;
|
|
|
|
|
packet->length = packet_length + RETRANS_HEADER_ROOM;
|
|
|
|
|
|
|
|
|
|
/* Insert RETRANS_HEADER_ROOM bytes in front of packet data,
|
|
|
|
|
for retransmission header */
|
|
|
|
|
memset(packet->data, 0, RETRANS_HEADER_ROOM);
|
|
|
|
|
memcpy(packet->data + RETRANS_HEADER_ROOM, packet_data, packet_length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* l < r?, considers wrapping */
|
|
|
|
|
static bool seq_lt(uint16_t l, uint16_t r) {
|
|
|
|
|
return l - r > USHRT_MAX/2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Random access to packet from buffer by sequence number for (re-)sending. */
|
|
|
|
|
ssize_t pa_raop_pb_read_packet(pa_raop_packet_buffer *pb, uint16_t seq_num, uint8_t **packet_data) {
|
|
|
|
|
uint16_t index = 0; /* Index of requested packet */
|
|
|
|
|
pa_raop_packet_element *packet;
|
|
|
|
|
|
|
|
|
|
/* If the buffer is empty, there is no use in calculating indices */
|
|
|
|
|
if (pb_is_empty(pb))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* If the requested packet is too old (seq_num below first seq number
|
|
|
|
|
in buffer) or too young (seq_num greater than current seq number),
|
|
|
|
|
do nothing and return */
|
|
|
|
|
if (seq_lt(seq_num, pb->first_seq_num))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
index = (uint16_t) (seq_num - pb->first_seq_num);
|
|
|
|
|
if (index >= pb->count)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Index of the requested packet in the buffer is calculated
|
|
|
|
|
using the first sequence number stored in the buffer.
|
|
|
|
|
The offset (seq_num - first_seq_num) is used to access the array. */
|
|
|
|
|
packet = &pb->packets[(pb->start + index) % pb->size];
|
|
|
|
|
|
|
|
|
|
pa_assert(packet->data[RETRANS_HEADER_ROOM + 2] == (seq_num >> 8));
|
|
|
|
|
pa_assert(packet->data[RETRANS_HEADER_ROOM + 3] == (seq_num & 0xff));
|
|
|
|
|
pa_assert(packet_data);
|
|
|
|
|
|
|
|
|
|
*packet_data = packet->data;
|
|
|
|
|
|
|
|
|
|
return packet->length;
|
|
|
|
|
}
|