Commit 0ea4c970 authored by Max Kellermann's avatar Max Kellermann

Merge branch 'v0.16.x'

Conflicts: src/player_thread.c src/playlist_control.c
parents 57936a13 838f7cd2
...@@ -21,9 +21,16 @@ ver 0.17 (2011/??/??) ...@@ -21,9 +21,16 @@ ver 0.17 (2011/??/??)
ver 0.16.4 (2011/??/??) ver 0.16.4 (2011/??/??)
* fix memory leaks * fix memory leaks
* don't resume playback when seeking to another song while paused
* apply follow_inside_symlinks to absolute symlinks
* 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
- wavpack: obey all decoder commands, stop at CUE track border
* encoder:
- vorbis: don't send end-of-stream on flush
* output:
- alsa: fix SIGFPE when alsa announces a period size of 0
ver 0.16.3 (2011/06/04) ver 0.16.3 (2011/06/04)
......
...@@ -34,9 +34,6 @@ ...@@ -34,9 +34,6 @@
#undef G_LOG_DOMAIN #undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "wavpack" #define G_LOG_DOMAIN "wavpack"
/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */
#define CHUNK_SIZE 1020
#define ERRORLEN 80 #define ERRORLEN 80
static struct { static struct {
...@@ -162,8 +159,6 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek) ...@@ -162,8 +159,6 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
enum sample_format sample_format; enum sample_format sample_format;
struct audio_format audio_format; struct audio_format audio_format;
format_samples_t format_samples; format_samples_t format_samples;
char chunk[CHUNK_SIZE];
int samples_requested, samples_got;
float total_time; float total_time;
int bytes_per_sample, output_sample_size; int bytes_per_sample, output_sample_size;
...@@ -193,12 +188,15 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek) ...@@ -193,12 +188,15 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
output_sample_size = audio_format_frame_size(&audio_format); output_sample_size = audio_format_frame_size(&audio_format);
/* wavpack gives us all kind of samples in a 32-bit space */ /* wavpack gives us all kind of samples in a 32-bit space */
samples_requested = sizeof(chunk) / (4 * audio_format.channels); int32_t chunk[1024];
const uint32_t samples_requested = G_N_ELEMENTS(chunk) /
audio_format.channels;
decoder_initialized(decoder, &audio_format, can_seek, total_time); decoder_initialized(decoder, &audio_format, can_seek, total_time);
do { enum decoder_command cmd = decoder_get_command(decoder);
if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) { while (cmd != DECODE_COMMAND_STOP) {
if (cmd == DECODE_COMMAND_SEEK) {
if (can_seek) { if (can_seek) {
unsigned where = decoder_seek_where(decoder) * unsigned where = decoder_seek_where(decoder) *
audio_format.sample_rate; audio_format.sample_rate;
...@@ -213,29 +211,20 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek) ...@@ -213,29 +211,20 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
} }
} }
if (decoder_get_command(decoder) == DECODE_COMMAND_STOP) { uint32_t samples_got = WavpackUnpackSamples(wpc, chunk,
samples_requested);
if (samples_got == 0)
break; break;
}
samples_got = WavpackUnpackSamples( int bitrate = (int)(WavpackGetInstantBitrate(wpc) / 1000 +
wpc, (int32_t *)chunk, samples_requested 0.5);
); format_samples(bytes_per_sample, chunk,
if (samples_got > 0) { samples_got * audio_format.channels);
int bitrate = (int)(WavpackGetInstantBitrate(wpc) /
1000 + 0.5); cmd = decoder_data(decoder, NULL, chunk,
samples_got * output_sample_size,
format_samples( bitrate);
bytes_per_sample, chunk, }
samples_got * audio_format.channels
);
decoder_data(
decoder, NULL, chunk,
samples_got * output_sample_size,
bitrate
);
}
} while (samples_got > 0);
} }
/** /**
......
...@@ -266,6 +266,15 @@ vorbis_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error) ...@@ -266,6 +266,15 @@ vorbis_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
{ {
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
encoder->flush = true;
return true;
}
static bool
vorbis_encoder_pre_tag(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
{
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
vorbis_analysis_wrote(&encoder->vd, 0); vorbis_analysis_wrote(&encoder->vd, 0);
vorbis_encoder_blockout(encoder); vorbis_encoder_blockout(encoder);
...@@ -366,6 +375,7 @@ vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length) ...@@ -366,6 +375,7 @@ vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
if (ret == 0 && encoder->flush) { if (ret == 0 && encoder->flush) {
encoder->flush = false; encoder->flush = false;
ret = ogg_stream_flush(&encoder->os, &page); ret = ogg_stream_flush(&encoder->os, &page);
} }
if (ret == 0) if (ret == 0)
...@@ -398,6 +408,7 @@ const struct encoder_plugin vorbis_encoder_plugin = { ...@@ -398,6 +408,7 @@ const struct encoder_plugin vorbis_encoder_plugin = {
.open = vorbis_encoder_open, .open = vorbis_encoder_open,
.close = vorbis_encoder_close, .close = vorbis_encoder_close,
.flush = vorbis_encoder_flush, .flush = vorbis_encoder_flush,
.pre_tag = vorbis_encoder_pre_tag,
.tag = vorbis_encoder_tag, .tag = vorbis_encoder_tag,
.write = vorbis_encoder_write, .write = vorbis_encoder_write,
.read = vorbis_encoder_read, .read = vorbis_encoder_read,
......
...@@ -50,6 +50,8 @@ struct encoder_plugin { ...@@ -50,6 +50,8 @@ struct encoder_plugin {
bool (*flush)(struct encoder *encoder, GError **error); bool (*flush)(struct encoder *encoder, GError **error);
bool (*pre_tag)(struct encoder *encoder, GError **error);
bool (*tag)(struct encoder *encoder, const struct tag *tag, bool (*tag)(struct encoder *encoder, const struct tag *tag,
GError **error); GError **error);
...@@ -148,8 +150,30 @@ encoder_flush(struct encoder *encoder, GError **error) ...@@ -148,8 +150,30 @@ encoder_flush(struct encoder *encoder, GError **error)
} }
/** /**
* Prepare for sending a tag to the encoder. This is used by some
* encoders to flush the previous sub-stream, in preparation to begin
* a new one.
*
* @param encoder the encoder
* @param tag the tag object
* @param error location to store the error occuring, or NULL to ignore errors.
* @return true on success
*/
static inline bool
encoder_pre_tag(struct encoder *encoder, GError **error)
{
/* this method is optional */
return encoder->plugin->pre_tag != NULL
? encoder->plugin->pre_tag(encoder, error)
: true;
}
/**
* Sends a tag to the encoder. * Sends a tag to the encoder.
* *
* Instructions: call encoder_pre_tag(); then obtain flushed data with
* encoder_read(); finally call encoder_tag().
*
* @param encoder the encoder * @param encoder the encoder
* @param tag the tag object * @param tag the tag object
* @param error location to store the error occurring, or NULL to ignore errors. * @param error location to store the error occurring, or NULL to ignore errors.
......
...@@ -508,6 +508,14 @@ configure_hw: ...@@ -508,6 +508,14 @@ configure_hw:
g_debug("buffer_size=%u period_size=%u", g_debug("buffer_size=%u period_size=%u",
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size); (unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
if (alsa_period_size == 0)
/* this works around a SIGFPE bug that occurred when
an ALSA driver indicated period_size==0; this
caused a division by zero in alsa_play(). By using
the fallback "1", we make sure that this won't
happen again. */
alsa_period_size = 1;
ad->period_frames = alsa_period_size; ad->period_frames = alsa_period_size;
ad->period_position = 0; ad->period_position = 0;
......
...@@ -493,7 +493,8 @@ httpd_output_pause(void *data) ...@@ -493,7 +493,8 @@ httpd_output_pause(void *data)
if (has_clients) { if (has_clients) {
static const char silence[1020]; static const char silence[1020];
return httpd_output_play(data, silence, sizeof(silence), NULL); return httpd_output_play(data, silence, sizeof(silence),
NULL) > 0;
} else { } else {
g_usleep(100000); g_usleep(100000);
return true; return true;
...@@ -522,7 +523,7 @@ httpd_output_tag(void *data, const struct tag *tag) ...@@ -522,7 +523,7 @@ httpd_output_tag(void *data, const struct tag *tag)
/* flush the current stream, and end it */ /* flush the current stream, and end it */
encoder_flush(httpd->encoder, NULL); encoder_pre_tag(httpd->encoder, NULL);
httpd_output_encoder_to_clients(httpd); httpd_output_encoder_to_clients(httpd);
/* send the tag to the encoder - which starts a new /* send the tag to the encoder - which starts a new
......
...@@ -518,7 +518,7 @@ static void my_shout_set_tag(void *data, ...@@ -518,7 +518,7 @@ static void my_shout_set_tag(void *data,
if (sd->encoder->plugin->tag != NULL) { if (sd->encoder->plugin->tag != NULL) {
/* encoder plugin supports stream tags */ /* encoder plugin supports stream tags */
ret = encoder_flush(sd->encoder, &error); ret = encoder_pre_tag(sd->encoder, &error);
if (!ret) { if (!ret) {
g_warning("%s", error->message); g_warning("%s", error->message);
g_error_free(error); g_error_free(error);
......
...@@ -641,8 +641,13 @@ static gpointer audio_output_task(gpointer arg) ...@@ -641,8 +641,13 @@ static gpointer audio_output_task(gpointer arg)
case AO_COMMAND_CANCEL: case AO_COMMAND_CANCEL:
ao->chunk = NULL; ao->chunk = NULL;
if (ao->open)
if (ao->open) {
g_mutex_unlock(ao->mutex);
ao_plugin_cancel(ao->plugin, ao->data); ao_plugin_cancel(ao->plugin, ao->data);
g_mutex_lock(ao->mutex);
}
ao_command_finished(ao); ao_command_finished(ao);
/* the player thread will now clear our music /* the player thread will now clear our music
......
...@@ -187,5 +187,8 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk) ...@@ -187,5 +187,8 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk)
unsigned unsigned
music_pipe_size(const struct music_pipe *mp) music_pipe_size(const struct music_pipe *mp)
{ {
return mp->size; g_mutex_lock(mp->mutex);
unsigned size = mp->size;
g_mutex_unlock(mp->mutex);
return size;
} }
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#ifndef MPD_PIPE_H #ifndef MPD_PIPE_H
#define MPD_PIPE_H #define MPD_PIPE_H
#include <glib.h>
#include <stdbool.h> #include <stdbool.h>
#ifndef NDEBUG #ifndef NDEBUG
...@@ -38,6 +39,7 @@ struct music_pipe; ...@@ -38,6 +39,7 @@ struct music_pipe;
/** /**
* Creates a new #music_pipe object. It is empty. * Creates a new #music_pipe object. It is empty.
*/ */
G_GNUC_MALLOC
struct music_pipe * struct music_pipe *
music_pipe_new(void); music_pipe_new(void);
...@@ -70,6 +72,7 @@ music_pipe_contains(const struct music_pipe *mp, ...@@ -70,6 +72,7 @@ music_pipe_contains(const struct music_pipe *mp,
* Returns the first #music_chunk from the pipe. Returns NULL if the * Returns the first #music_chunk from the pipe. Returns NULL if the
* pipe is empty. * pipe is empty.
*/ */
G_GNUC_PURE
const struct music_chunk * const struct music_chunk *
music_pipe_peek(const struct music_pipe *mp); music_pipe_peek(const struct music_pipe *mp);
...@@ -96,9 +99,11 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk); ...@@ -96,9 +99,11 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk);
/** /**
* Returns the number of chunks currently in this pipe. * Returns the number of chunks currently in this pipe.
*/ */
G_GNUC_PURE
unsigned unsigned
music_pipe_size(const struct music_pipe *mp); music_pipe_size(const struct music_pipe *mp);
G_GNUC_PURE
static inline bool static inline bool
music_pipe_empty(const struct music_pipe *mp) music_pipe_empty(const struct music_pipe *mp)
{ {
......
...@@ -628,7 +628,9 @@ play_chunk(struct player_control *pc, ...@@ -628,7 +628,9 @@ play_chunk(struct player_control *pc,
return true; return true;
} }
player_lock(pc);
pc->bit_rate = chunk->bit_rate; pc->bit_rate = chunk->bit_rate;
player_unlock(pc);
/* send the chunk to the audio outputs */ /* send the chunk to the audio outputs */
......
...@@ -230,10 +230,12 @@ playlist_seek_song(struct playlist *playlist, struct player_control *pc, ...@@ -230,10 +230,12 @@ playlist_seek_song(struct playlist *playlist, struct player_control *pc,
playlist->error_count = 0; playlist->error_count = 0;
if (!playlist->playing || (unsigned)playlist->current != i) { if (!playlist->playing || (unsigned)playlist->current != i) {
/* seeking is not within the current song - first /* seeking is not within the current song - prepare
start playing the new song */ song change */
playlist->playing = true;
playlist->current = i;
playlist_play_order(playlist, pc, i);
queued = NULL; queued = NULL;
} }
......
...@@ -714,8 +714,14 @@ skip_symlink(const struct directory *directory, const char *utf8_name) ...@@ -714,8 +714,14 @@ skip_symlink(const struct directory *directory, const char *utf8_name)
return false; return false;
} }
if (buffer[0] == '/') if (g_path_is_absolute(buffer)) {
return !follow_outside_symlinks; /* if the symlink points to an absolute path, see if
that path is inside the music directory */
const char *relative = map_to_relative_path(buffer);
return relative > buffer
? !follow_inside_symlinks
: !follow_outside_symlinks;
}
p = buffer; p = buffer;
while (*p == '.') { while (*p == '.') {
......
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