Merge remote-tracking branch 'abrasive/1.0-dev' into 1.0-dev

This commit is contained in:
Brendan Shanks
2013-04-03 08:43:52 -07:00
10 changed files with 81 additions and 37 deletions
+4 -1
View File
@@ -13,8 +13,11 @@ ifdef CONFIG_AO
SRCS += audio_ao.c
endif
# default target
all: shairport
shairport: $(SRCS)
$(CC) $(CFLAGS) $(SRCS) $(LDFLAGS) -o shairport
clean:
rm shairport
rm -f shairport
+7 -5
View File
@@ -4,16 +4,18 @@ By [James Laird](mailto:jhl@mafipulation.org) ([announcement](http://mafipulatio
What it is
----------
This program emulates an AirPort Express for the purpose of streaming music from iTunes and compatible iPods. It implements a server for the Apple RAOP protocol.
This program emulates an AirPort Express for the purpose of streaming music from iTunes and compatible iPods and iPhones. It implements a server for the Apple RAOP protocol.
ShairPort does not support AirPlay v2 (video and photo streaming).
It supports multiple simultaneous streams, if your audio output chain (as detected by libao) does so.
How to use it
-------------
`perl shairport.pl`. See INSTALL.md for further information.
```
./configure
make clean all
./shairport -a 'AP Name'
```
The triangle-in-rectangle AirTunes (now AirPlay) logo will appear in the iTunes status bar of any machine on the network, or on iPod play controls screen. Choose your access point name to start streaming to the ShairPort instance.
The triangle-in-rectangle AirTunes (now AirPlay) logo will appear in the iTunes status bar of any machine on the network, or on iPod/iPhone play controls screen. Choose your access point name to start streaming to the ShairPort instance.
Thanks
------
+6 -2
View File
@@ -1,4 +1,8 @@
- makefile
- documentation
- PKGBUILD, initscript, systemd, launchd plist
- tidy debug printing
- alsa
- pipe
- avahi
- volume
- supervisor?
- pico mdns?
+2 -1
View File
@@ -49,7 +49,8 @@ static int init(int argc, char **argv) {
optind = 0;
// some platforms apparently require optreset = 1; - which?
char opt, *mid;
int opt;
char *mid;
while ((opt = getopt(argc, argv, "d:i:n:o:")) > 0) {
switch (opt) {
case 'd':
+2 -3
View File
@@ -56,10 +56,9 @@ void warn(char *format, ...) {
va_end(args);
}
void debug(char *format, ...) {
if (!debuglev)
void debug(int level, char *format, ...) {
if (level > debuglev)
return;
fprintf(stderr, "DEBUG: ");
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
+1 -1
View File
@@ -29,7 +29,7 @@ typedef struct {
extern int debuglev;
void die(char *format, ...);
void warn(char *format, ...);
void debug(char *format, ...);
void debug(int level, char *format, ...);
uint8_t *base64_dec(char *input, int *outlen);
char *base64_enc(uint8_t *input, int length);
+14 -4
View File
@@ -218,7 +218,7 @@ void player_put_packet(seq_t seqno, uint8_t *data, int len) {
pthread_mutex_lock(&ab_mutex);
if (ab_buffering && buf_fill >= config.buffer_start_fill) {
debug("buffering over. starting play\n");
debug(1, "buffering over. starting play\n");
ab_buffering = 0;
pthread_cond_signal(&ab_buffer_ready);
}
@@ -302,10 +302,20 @@ static void bf_est_reset(short fill) {
}
static void bf_est_update(short fill) {
// the rate-matching system needs to decide how full to keep the buffer.
// the initial fill is present when the system starts to output samples,
// but most output chains will instantly gobble their own buffer's worth of
// data. we average for a while to decide where to draw the line.
if (fill_count < 1000) {
desired_fill += (double)fill/1000.0;
fill_count++;
return;
} else if (fill_count == 1000) {
// this information could be used to help estimate our effective latency?
debug(1, "established desired fill of %f frames, "
"so output chain buffered about %f frames\n", desired_fill,
config.buffer_start_fill - desired_fill);
fill_count++;
}
#define CONTROL_A (1e-4)
@@ -318,7 +328,7 @@ static void bf_est_update(short fill) {
bf_est_drift = biquad_filt(&bf_drift_lpf, CONTROL_B*(adj_error + err_deriv) + bf_est_drift);
debug("bf %d err %f drift %f desiring %f ed %f estd %f\n",
debug(3, "bf %d err %f drift %f desiring %f ed %f estd %f\n",
fill, bf_est_err, bf_est_drift, desired_fill, err_deriv, err_deriv + adj_error);
bf_playback_rate = 1.0 + adj_error + bf_est_drift;
@@ -401,12 +411,12 @@ static int stuff_buffer(double playback_rate, short *inptr, short *outptr) {
};
if (stuff) {
if (stuff==1) {
debug("+++++++++\n");
debug(2, "+++++++++\n");
// interpolate one sample
*outptr++ = dithered_vol(((long)inptr[-2] + (long)inptr[0]) >> 1);
*outptr++ = dithered_vol(((long)inptr[-1] + (long)inptr[1]) >> 1);
} else if (stuff==-1) {
debug("---------\n");
debug(2, "---------\n");
inptr++;
inptr++;
}
+5 -5
View File
@@ -74,15 +74,15 @@ static void *rtp_receiver(void *arg) {
continue;
}
if (type == 0x56 && seqno == 0) {
debug("Suspected resync request packet received.\n");
player_flush();
debug(2, "resend-related request packet received, ignoring.\n");
continue;
}
debug(1, "Unknown RTP packet of type 0x%02X length %d seqno %d\n", type, nread, seqno);
}
warn("Unknown RTP packet of type 0x%02X length %d\n", type, nread);
}
debug("RTP thread interrupted. terminating.\n");
debug(1, "RTP thread interrupted. terminating.\n");
close(sock);
return NULL;
@@ -130,7 +130,7 @@ int rtp_setup(SOCKADDR *remote, int cport, int tport) {
if (running)
die("rtp_setup called with active stream!\n");
debug("rtp_setup: cport=%d tport=%d\n", cport, tport);
debug(1, "rtp_setup: cport=%d tport=%d\n", cport, tport);
// we do our own timing and ignore the timing port.
// an audio perfectionist may wish to learn the protocol.
@@ -149,7 +149,7 @@ int rtp_setup(SOCKADDR *remote, int cport, int tport) {
int sport = bind_port(remote);
debug("rtp listening on port %d\n", sport);
debug(1, "rtp listening on port %d\n", sport);
please_shutdown = 0;
pthread_create(&rtp_thread, NULL, &rtp_receiver, NULL);
+39 -14
View File
@@ -68,7 +68,7 @@ static inline int rtsp_playing(void) {
static void rtsp_take_player(void) {
if (pthread_mutex_trylock(&playing_mutex)) {
debug("shutting down playing thread\n");
debug(1, "shutting down playing thread\n");
// XXX minor race condition between please_shutdown and signal delivery
please_shutdown = 1;
pthread_kill(playing_thread, SIGUSR1);
@@ -168,7 +168,7 @@ static int msg_handle_line(rtsp_message **pmsg, char *line) {
*pmsg = msg;
char *sp, *p;
debug("received request: %s\n", line);
debug(1, "received request: %s\n", line);
p = strtok_r(line, " ", &sp);
if (!p)
@@ -198,7 +198,7 @@ static int msg_handle_line(rtsp_message **pmsg, char *line) {
*p = 0;
p += 2;
msg_add_header(msg, line, p);
debug(" %s: %s\n", line, p);
debug(2, " %s: %s\n", line, p);
return -1;
} else {
char *cl = msg_get_header(msg, "Content-Length");
@@ -226,12 +226,12 @@ static rtsp_message * rtsp_read_request(int fd) {
while (msg_size < 0) {
if (please_shutdown) {
debug("RTSP shutdown requested\n");
debug(1, "RTSP shutdown requested\n");
goto shutdown;
}
nread = read(fd, buf+inbuf, buflen - inbuf);
if (!nread) {
debug("RTSP connection closed\n");
debug(1, "RTSP connection closed\n");
goto shutdown;
}
if (nread < 0) {
@@ -298,10 +298,10 @@ static void msg_write_response(int fd, rtsp_message *resp) {
"RTSP/1.0 %d %s\r\n", resp->respcode,
resp->respcode==200 ? "OK" : "Error");
write(fd, rbuf, nrbuf);
debug("sending response: %s", rbuf);
debug(1, "sending response: %s", rbuf);
int i;
for (i=0; i<resp->nheaders; i++) {
debug(" %s: %s\n", resp->name[i], resp->value[i]);
debug(2, " %s: %s\n", resp->name[i], resp->value[i]);
write(fd, resp->name[i], strlen(resp->name[i]));
write(fd, ": ", 2);
write(fd, resp->value[i], strlen(resp->value[i]));
@@ -368,6 +368,8 @@ static void handle_setup(rtsp_conn_info *conn,
sprintf(resphdr + strlen(resphdr), ";server_port=%d", sport);
msg_add_header(resp, "Transport", resphdr);
msg_add_header(resp, "Session", "1");
resp->respcode = 200;
}
@@ -376,6 +378,32 @@ static void handle_ignore(rtsp_conn_info *conn,
resp->respcode = 200;
}
static void handle_set_parameter(rtsp_conn_info *conn,
rtsp_message *req, rtsp_message *resp) {
if (!req->contentlength)
return;
char *cp = req->content;
int cp_left = req->contentlength;
char *next;
while (cp_left && cp) {
next = nextline(cp, cp_left);
cp_left -= next-cp;
if (!strncmp(cp, "volume: ", 8)) {
float volume = atof(cp + 8);
debug(1, "volume: %f\n", volume);
player_volume(volume);
} else {
debug(1, "unrecognised parameter: >>%s<< (%d)\n", cp, strlen(cp));
}
cp = next;
}
resp->respcode = 200;
}
static void handle_announce(rtsp_conn_info *conn,
rtsp_message *req, rtsp_message *resp) {
@@ -385,7 +413,7 @@ static void handle_announce(rtsp_conn_info *conn,
char *cp = req->content;
int cp_left = req->contentlength;
char *next;
while (cp) {
while (cp_left && cp) {
next = nextline(cp, cp_left);
cp_left -= next-cp;
@@ -446,7 +474,7 @@ static struct method_handler {
{"TEARDOWN", handle_teardown},
{"SETUP", handle_setup},
{"GET_PARAMETER", handle_ignore},
{"SET_PARAMETER", handle_ignore}, // XXX
{"SET_PARAMETER", handle_set_parameter}, // XXX
{"RECORD", handle_ignore},
{NULL, NULL}
};
@@ -484,11 +512,8 @@ static void apple_challenge(int fd, rtsp_message *req, rtsp_message *resp) {
#endif
{
struct sockaddr_in *sa = (struct sockaddr_in*)(&fdsa);
unsigned int ip = sa->sin_addr.s_addr;
for (i=0; i<4; i++) {
*bp++ = ip & 0xff;
ip >>= 8;
}
memcpy(bp, &sa->sin_addr.s_addr, 4);
bp += 4;
}
for (i=0; i<6; i++)
+1 -1
View File
@@ -73,7 +73,7 @@ void usage(char *progname) {
}
int parse_options(int argc, char **argv) {
char opt;
int opt;
while ((opt = getopt(argc, argv, "+hvp:a:o:b:")) > 0) {
switch (opt) {
default: