Merge branch 'v0.16.x'
This commit is contained in:
commit
5ecb6fecc4
5
NEWS
5
NEWS
@ -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/??/??)
|
|||||||
- 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)
|
|||||||
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)
|
|||||||
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)
|
||||||
|
@ -42,6 +42,13 @@
|
|||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#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().
|
||||||
*/
|
*/
|
||||||
@ -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
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
/* 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)
|
|||||||
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)
|
|||||||
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user