Commit 5ecb6fec authored by Max Kellermann's avatar Max Kellermann

Merge branch 'v0.16.x'

parents eea72674 b3df4dc2
...@@ -23,6 +23,8 @@ ver 0.16.4 (2011/??/??) ...@@ -23,6 +23,8 @@ ver 0.16.4 (2011/??/??)
* fix memory leaks * fix memory leaks
* don't resume playback when seeking to another song while paused * don't resume playback when seeking to another song while paused
* apply follow_inside_symlinks to absolute symlinks * apply follow_inside_symlinks to absolute symlinks
* input:
- curl: limit the receive buffer size
* decoder: * decoder:
- ffmpeg: workaround for semantic API change in recent ffmpeg versions - ffmpeg: workaround for semantic API change in recent ffmpeg versions
- flac: validate the sample rate when scanning the tag - flac: validate the sample rate when scanning the tag
...@@ -31,6 +33,9 @@ ver 0.16.4 (2011/??/??) ...@@ -31,6 +33,9 @@ ver 0.16.4 (2011/??/??)
- vorbis: don't send end-of-stream on flush - vorbis: don't send end-of-stream on flush
* output: * output:
- alsa: fix SIGFPE when alsa announces a period size of 0 - alsa: fix SIGFPE when alsa announces a period size of 0
- httpd: don't warn on client disconnect
- pulse: fix deadlock when resuming the stream
- pulse: fix deadlock when the stream was suspended
ver 0.16.3 (2011/06/04) ver 0.16.3 (2011/06/04)
......
...@@ -153,7 +153,6 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is) ...@@ -153,7 +153,6 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
mpc_uint32_t ret; mpc_uint32_t ret;
int32_t chunk[G_N_ELEMENTS(sample_buffer)]; int32_t chunk[G_N_ELEMENTS(sample_buffer)];
long bit_rate = 0; long bit_rate = 0;
mpc_uint32_t vbr_update_acc;
mpc_uint32_t vbr_update_bits; mpc_uint32_t vbr_update_bits;
enum decoder_command cmd = DECODE_COMMAND_NONE; enum decoder_command cmd = DECODE_COMMAND_NONE;
...@@ -243,10 +242,11 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is) ...@@ -243,10 +242,11 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
decoder_seek_error(mpd_decoder); decoder_seek_error(mpd_decoder);
} }
vbr_update_acc = 0;
vbr_update_bits = 0; vbr_update_bits = 0;
#ifdef MPC_IS_OLD_API #ifdef MPC_IS_OLD_API
mpc_uint32_t vbr_update_acc = 0;
ret = mpc_decoder_decode(&decoder, sample_buffer, ret = mpc_decoder_decode(&decoder, sample_buffer,
&vbr_update_acc, &vbr_update_bits); &vbr_update_acc, &vbr_update_bits);
if (ret == 0 || ret == (mpc_uint32_t)-1) if (ret == 0 || ret == (mpc_uint32_t)-1)
......
...@@ -43,6 +43,13 @@ ...@@ -43,6 +43,13 @@
#define G_LOG_DOMAIN "input_curl" #define G_LOG_DOMAIN "input_curl"
/** /**
* Do not buffer more than this number of bytes. It should be a
* reasonable limit that doesn't make low-end machines suffer too
* much, but doesn't cause stuttering on high-latency lines.
*/
static const size_t CURL_MAX_BUFFERED = 512 * 1024;
/**
* Buffers created by input_curl_writefunction(). * Buffers created by input_curl_writefunction().
*/ */
struct buffer { struct buffer {
...@@ -144,6 +151,25 @@ input_curl_finish(void) ...@@ -144,6 +151,25 @@ input_curl_finish(void)
curl_global_cleanup(); curl_global_cleanup();
} }
/**
* Determine the total sizes of all buffers, including portions that
* have already been consumed.
*/
G_GNUC_PURE
static size_t
curl_total_buffer_size(const struct input_curl *c)
{
size_t total = 0;
for (GList *i = g_queue_peek_head_link(c->buffers);
i != NULL; i = g_list_next(i)) {
struct buffer *buffer = i->data;
total += buffer->size;
}
return total;
}
static void static void
buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
{ {
...@@ -473,6 +499,10 @@ static int ...@@ -473,6 +499,10 @@ static int
input_curl_buffer(struct input_stream *is, GError **error_r) input_curl_buffer(struct input_stream *is, GError **error_r)
{ {
struct input_curl *c = (struct input_curl *)is; struct input_curl *c = (struct input_curl *)is;
if (curl_total_buffer_size(c) >= CURL_MAX_BUFFERED)
return 0;
CURLMcode mcode; CURLMcode mcode;
int running_handles; int running_handles;
bool ret; bool ret;
......
...@@ -143,6 +143,8 @@ httpd_client_unref_page(gpointer data, G_GNUC_UNUSED gpointer user_data) ...@@ -143,6 +143,8 @@ httpd_client_unref_page(gpointer data, G_GNUC_UNUSED gpointer user_data)
void void
httpd_client_free(struct httpd_client *client) httpd_client_free(struct httpd_client *client)
{ {
assert(client != NULL);
if (client->state == RESPONSE) { if (client->state == RESPONSE) {
if (client->write_source_id != 0) if (client->write_source_id != 0)
g_source_remove(client->write_source_id); g_source_remove(client->write_source_id);
...@@ -169,6 +171,8 @@ httpd_client_free(struct httpd_client *client) ...@@ -169,6 +171,8 @@ httpd_client_free(struct httpd_client *client)
static void static void
httpd_client_close(struct httpd_client *client) httpd_client_close(struct httpd_client *client)
{ {
assert(client != NULL);
httpd_output_remove_client(client->httpd, client); httpd_output_remove_client(client->httpd, client);
httpd_client_free(client); httpd_client_free(client);
} }
...@@ -179,6 +183,9 @@ httpd_client_close(struct httpd_client *client) ...@@ -179,6 +183,9 @@ httpd_client_close(struct httpd_client *client)
static void static void
httpd_client_begin_response(struct httpd_client *client) httpd_client_begin_response(struct httpd_client *client)
{ {
assert(client != NULL);
assert(client->state != RESPONSE);
client->state = RESPONSE; client->state = RESPONSE;
client->write_source_id = 0; client->write_source_id = 0;
client->pages = g_queue_new(); client->pages = g_queue_new();
...@@ -239,6 +246,9 @@ httpd_client_handle_line(struct httpd_client *client, const char *line) ...@@ -239,6 +246,9 @@ httpd_client_handle_line(struct httpd_client *client, const char *line)
static char * static char *
httpd_client_read_line(struct httpd_client *client) httpd_client_read_line(struct httpd_client *client)
{ {
assert(client != NULL);
assert(client->state != RESPONSE);
const char *p, *newline; const char *p, *newline;
size_t length; size_t length;
char *line; char *line;
...@@ -271,6 +281,7 @@ httpd_client_send_response(struct httpd_client *client) ...@@ -271,6 +281,7 @@ httpd_client_send_response(struct httpd_client *client)
GIOStatus status; GIOStatus status;
gsize bytes_written; gsize bytes_written;
assert(client != NULL);
assert(client->state == RESPONSE); assert(client->state == RESPONSE);
if (!client->metadata_requested) { if (!client->metadata_requested) {
...@@ -334,14 +345,19 @@ httpd_client_send_response(struct httpd_client *client) ...@@ -334,14 +345,19 @@ httpd_client_send_response(struct httpd_client *client)
static bool static bool
httpd_client_received(struct httpd_client *client) httpd_client_received(struct httpd_client *client)
{ {
assert(client != NULL);
assert(client->state != RESPONSE);
char *line; char *line;
bool success; bool success;
while ((line = httpd_client_read_line(client)) != NULL) { while ((line = httpd_client_read_line(client)) != NULL) {
success = httpd_client_handle_line(client, line); success = httpd_client_handle_line(client, line);
g_free(line); g_free(line);
if (!success) if (!success) {
assert(client->state != RESPONSE);
return false; return false;
}
if (client->state == RESPONSE) { if (client->state == RESPONSE) {
if (!fifo_buffer_is_empty(client->input)) { if (!fifo_buffer_is_empty(client->input)) {
...@@ -370,7 +386,14 @@ httpd_client_read(struct httpd_client *client) ...@@ -370,7 +386,14 @@ httpd_client_read(struct httpd_client *client)
if (client->state == RESPONSE) { if (client->state == RESPONSE) {
/* the client has already sent the request, and he /* the client has already sent the request, and he
must not send more */ must not send more */
g_warning("unexpected input from client"); char buffer[1];
status = g_io_channel_read_chars(client->channel, buffer,
sizeof(buffer), &bytes_read,
NULL);
if (status == G_IO_STATUS_NORMAL)
g_warning("unexpected input from client");
return false; return false;
} }
......
...@@ -207,6 +207,9 @@ pulse_output_subscribe_cb(pa_context *context, ...@@ -207,6 +207,9 @@ pulse_output_subscribe_cb(pa_context *context,
static bool static bool
pulse_output_connect(struct pulse_output *po, GError **error_r) pulse_output_connect(struct pulse_output *po, GError **error_r)
{ {
assert(po != NULL);
assert(po->context != NULL);
int error; int error;
error = pa_context_connect(po->context, po->server, error = pa_context_connect(po->context, po->server,
...@@ -229,6 +232,9 @@ pulse_output_connect(struct pulse_output *po, GError **error_r) ...@@ -229,6 +232,9 @@ pulse_output_connect(struct pulse_output *po, GError **error_r)
static bool static bool
pulse_output_setup_context(struct pulse_output *po, GError **error_r) pulse_output_setup_context(struct pulse_output *po, GError **error_r)
{ {
assert(po != NULL);
assert(po->mainloop != NULL);
po->context = pa_context_new(pa_threaded_mainloop_get_api(po->mainloop), po->context = pa_context_new(pa_threaded_mainloop_get_api(po->mainloop),
MPD_PULSE_NAME); MPD_PULSE_NAME);
if (po->context == NULL) { if (po->context == NULL) {
...@@ -257,6 +263,9 @@ pulse_output_setup_context(struct pulse_output *po, GError **error_r) ...@@ -257,6 +263,9 @@ pulse_output_setup_context(struct pulse_output *po, GError **error_r)
static void static void
pulse_output_delete_context(struct pulse_output *po) pulse_output_delete_context(struct pulse_output *po)
{ {
assert(po != NULL);
assert(po->context != NULL);
pa_context_disconnect(po->context); pa_context_disconnect(po->context);
pa_context_unref(po->context); pa_context_unref(po->context);
po->context = NULL; po->context = NULL;
...@@ -347,6 +356,8 @@ pulse_output_disable(void *data) ...@@ -347,6 +356,8 @@ pulse_output_disable(void *data)
{ {
struct pulse_output *po = data; struct pulse_output *po = data;
assert(po->mainloop != NULL);
pa_threaded_mainloop_stop(po->mainloop); pa_threaded_mainloop_stop(po->mainloop);
if (po->context != NULL) if (po->context != NULL)
pulse_output_delete_context(po); pulse_output_delete_context(po);
...@@ -363,6 +374,8 @@ pulse_output_disable(void *data) ...@@ -363,6 +374,8 @@ pulse_output_disable(void *data)
static bool static bool
pulse_output_wait_connection(struct pulse_output *po, GError **error_r) pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
{ {
assert(po->mainloop != NULL);
pa_context_state_t state; pa_context_state_t state;
pa_threaded_mainloop_lock(po->mainloop); pa_threaded_mainloop_lock(po->mainloop);
...@@ -399,11 +412,32 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r) ...@@ -399,11 +412,32 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
} }
} }
#if PA_CHECK_VERSION(0,9,8)
static void
pulse_output_stream_suspended_cb(G_GNUC_UNUSED pa_stream *stream, void *userdata)
{
struct pulse_output *po = userdata;
assert(stream == po->stream || po->stream == NULL);
assert(po->mainloop != NULL);
/* wake up the main loop to break out of the loop in
pulse_output_play() */
pa_threaded_mainloop_signal(po->mainloop, 0);
}
#endif
static void static void
pulse_output_stream_state_cb(pa_stream *stream, void *userdata) pulse_output_stream_state_cb(pa_stream *stream, void *userdata)
{ {
struct pulse_output *po = userdata; struct pulse_output *po = userdata;
assert(stream == po->stream || po->stream == NULL);
assert(po->mainloop != NULL);
assert(po->context != NULL);
switch (pa_stream_get_state(stream)) { switch (pa_stream_get_state(stream)) {
case PA_STREAM_READY: case PA_STREAM_READY:
if (po->mixer != NULL) if (po->mixer != NULL)
...@@ -432,6 +466,8 @@ pulse_output_stream_write_cb(G_GNUC_UNUSED pa_stream *stream, size_t nbytes, ...@@ -432,6 +466,8 @@ pulse_output_stream_write_cb(G_GNUC_UNUSED pa_stream *stream, size_t nbytes,
{ {
struct pulse_output *po = userdata; struct pulse_output *po = userdata;
assert(po->mainloop != NULL);
po->writable = nbytes; po->writable = nbytes;
pa_threaded_mainloop_signal(po->mainloop, 0); pa_threaded_mainloop_signal(po->mainloop, 0);
} }
...@@ -444,6 +480,8 @@ pulse_output_open(void *data, struct audio_format *audio_format, ...@@ -444,6 +480,8 @@ pulse_output_open(void *data, struct audio_format *audio_format,
pa_sample_spec ss; pa_sample_spec ss;
int error; int error;
assert(po->mainloop != NULL);
if (po->context != NULL) { if (po->context != NULL) {
switch (pa_context_get_state(po->context)) { switch (pa_context_get_state(po->context)) {
case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_UNCONNECTED:
...@@ -487,6 +525,11 @@ pulse_output_open(void *data, struct audio_format *audio_format, ...@@ -487,6 +525,11 @@ pulse_output_open(void *data, struct audio_format *audio_format,
return false; return false;
} }
#if PA_CHECK_VERSION(0,9,8)
pa_stream_set_suspended_callback(po->stream,
pulse_output_stream_suspended_cb, po);
#endif
pa_stream_set_state_callback(po->stream, pa_stream_set_state_callback(po->stream,
pulse_output_stream_state_cb, po); pulse_output_stream_state_cb, po);
pa_stream_set_write_callback(po->stream, pa_stream_set_write_callback(po->stream,
...@@ -522,6 +565,8 @@ pulse_output_close(void *data) ...@@ -522,6 +565,8 @@ pulse_output_close(void *data)
struct pulse_output *po = data; struct pulse_output *po = data;
pa_operation *o; pa_operation *o;
assert(po->mainloop != NULL);
pa_threaded_mainloop_lock(po->mainloop); pa_threaded_mainloop_lock(po->mainloop);
if (pa_stream_get_state(po->stream) == PA_STREAM_READY) { if (pa_stream_get_state(po->stream) == PA_STREAM_READY) {
...@@ -556,6 +601,8 @@ pulse_output_check_stream(struct pulse_output *po) ...@@ -556,6 +601,8 @@ pulse_output_check_stream(struct pulse_output *po)
{ {
pa_stream_state_t state = pa_stream_get_state(po->stream); pa_stream_state_t state = pa_stream_get_state(po->stream);
assert(po->mainloop != NULL);
switch (state) { switch (state) {
case PA_STREAM_READY: case PA_STREAM_READY:
case PA_STREAM_FAILED: case PA_STREAM_FAILED:
...@@ -637,6 +684,8 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause, ...@@ -637,6 +684,8 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause,
{ {
pa_operation *o; pa_operation *o;
assert(po->mainloop != NULL);
assert(po->context != NULL);
assert(po->stream != NULL); assert(po->stream != NULL);
o = pa_stream_cork(po->stream, pause, o = pa_stream_cork(po->stream, pause,
...@@ -667,6 +716,7 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r) ...@@ -667,6 +716,7 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r)
struct pulse_output *po = data; struct pulse_output *po = data;
int error; int error;
assert(po->mainloop != NULL);
assert(po->stream != NULL); assert(po->stream != NULL);
pa_threaded_mainloop_lock(po->mainloop); pa_threaded_mainloop_lock(po->mainloop);
...@@ -683,19 +733,30 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r) ...@@ -683,19 +733,30 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r)
/* unpause if previously paused */ /* unpause if previously paused */
if (pulse_output_stream_is_paused(po) && if (pulse_output_stream_is_paused(po) &&
!pulse_output_stream_pause(po, false, error_r)) !pulse_output_stream_pause(po, false, error_r)) {
pa_threaded_mainloop_unlock(po->mainloop);
return 0; return 0;
}
/* wait until the server allows us to write */ /* wait until the server allows us to write */
while (po->writable == 0) { while (po->writable == 0) {
#if PA_CHECK_VERSION(0,9,8)
if (pa_stream_is_suspended(po->stream)) {
pa_threaded_mainloop_unlock(po->mainloop);
g_set_error(error_r, pulse_output_quark(), 0,
"suspended");
return 0;
}
#endif
pa_threaded_mainloop_wait(po->mainloop); pa_threaded_mainloop_wait(po->mainloop);
if (pa_stream_get_state(po->stream) != PA_STREAM_READY) { if (pa_stream_get_state(po->stream) != PA_STREAM_READY) {
pa_threaded_mainloop_unlock(po->mainloop); pa_threaded_mainloop_unlock(po->mainloop);
g_set_error(error_r, pulse_output_quark(), 0, g_set_error(error_r, pulse_output_quark(), 0,
"disconnected"); "disconnected");
return false; return 0;
} }
} }
...@@ -725,6 +786,7 @@ pulse_output_cancel(void *data) ...@@ -725,6 +786,7 @@ pulse_output_cancel(void *data)
struct pulse_output *po = data; struct pulse_output *po = data;
pa_operation *o; pa_operation *o;
assert(po->mainloop != NULL);
assert(po->stream != NULL); assert(po->stream != NULL);
pa_threaded_mainloop_lock(po->mainloop); pa_threaded_mainloop_lock(po->mainloop);
...@@ -756,6 +818,7 @@ pulse_output_pause(void *data) ...@@ -756,6 +818,7 @@ pulse_output_pause(void *data)
struct pulse_output *po = data; struct pulse_output *po = data;
GError *error = NULL; GError *error = NULL;
assert(po->mainloop != NULL);
assert(po->stream != NULL); assert(po->stream != NULL);
pa_threaded_mainloop_lock(po->mainloop); pa_threaded_mainloop_lock(po->mainloop);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment