decoder_control: removed the global variable "dc"

Allocate a decoder_control object where needed, and pass it around.
This will allow more than one decoder thread one day.
This commit is contained in:
Max Kellermann 2009-10-31 19:22:56 +01:00
parent 806496dfc9
commit 6ef428af2e
11 changed files with 377 additions and 317 deletions

View File

@ -37,12 +37,15 @@
#undef G_LOG_DOMAIN #undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "decoder" #define G_LOG_DOMAIN "decoder"
void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder, void
decoder_initialized(struct decoder *decoder,
const struct audio_format *audio_format, const struct audio_format *audio_format,
bool seekable, float total_time) bool seekable, float total_time)
{ {
assert(dc.state == DECODE_STATE_START); struct decoder_control *dc = decoder->dc;
assert(dc.pipe != NULL);
assert(dc->state == DECODE_STATE_START);
assert(dc->pipe != NULL);
assert(decoder != NULL); assert(decoder != NULL);
assert(decoder->stream_tag == NULL); assert(decoder->stream_tag == NULL);
assert(decoder->decoder_tag == NULL); assert(decoder->decoder_tag == NULL);
@ -51,86 +54,98 @@ void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder,
assert(audio_format_defined(audio_format)); assert(audio_format_defined(audio_format));
assert(audio_format_valid(audio_format)); assert(audio_format_valid(audio_format));
dc.in_audio_format = *audio_format; dc->in_audio_format = *audio_format;
getOutputAudioFormat(audio_format, &dc.out_audio_format); getOutputAudioFormat(audio_format, &dc->out_audio_format);
dc.seekable = seekable; dc->seekable = seekable;
dc.total_time = total_time; dc->total_time = total_time;
decoder_lock(); decoder_lock(dc);
dc.state = DECODE_STATE_DECODE; dc->state = DECODE_STATE_DECODE;
decoder_unlock(); decoder_unlock(dc);
player_lock_signal(); player_lock_signal();
g_debug("audio_format=%u:%u:%u, seekable=%s", g_debug("audio_format=%u:%u:%u, seekable=%s",
dc.in_audio_format.sample_rate, dc.in_audio_format.bits, dc->in_audio_format.sample_rate,
dc.in_audio_format.channels, dc->in_audio_format.bits,
dc->in_audio_format.channels,
seekable ? "true" : "false"); seekable ? "true" : "false");
if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) if (!audio_format_equals(&dc->in_audio_format,
&dc->out_audio_format))
g_debug("converting to %u:%u:%u", g_debug("converting to %u:%u:%u",
dc.out_audio_format.sample_rate, dc->out_audio_format.sample_rate,
dc.out_audio_format.bits, dc->out_audio_format.bits,
dc.out_audio_format.channels); dc->out_audio_format.channels);
} }
char *decoder_get_uri(G_GNUC_UNUSED struct decoder *decoder) char *decoder_get_uri(G_GNUC_UNUSED struct decoder *decoder)
{ {
assert(dc.pipe != NULL); const struct decoder_control *dc = decoder->dc;
return song_get_uri(dc.current_song); assert(dc->pipe != NULL);
return song_get_uri(dc->current_song);
} }
enum decoder_command decoder_get_command(G_GNUC_UNUSED struct decoder * decoder) enum decoder_command decoder_get_command(G_GNUC_UNUSED struct decoder * decoder)
{ {
assert(dc.pipe != NULL); const struct decoder_control *dc = decoder->dc;
return dc.command; assert(dc->pipe != NULL);
return dc->command;
} }
void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder) void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder)
{ {
decoder_lock(); struct decoder_control *dc = decoder->dc;
assert(dc.command != DECODE_COMMAND_NONE); decoder_lock(dc);
assert(dc.command != DECODE_COMMAND_SEEK ||
dc.seek_error || decoder->seeking);
assert(dc.pipe != NULL);
if (dc.command == DECODE_COMMAND_SEEK) { assert(dc->command != DECODE_COMMAND_NONE);
assert(dc->command != DECODE_COMMAND_SEEK ||
dc->seek_error || decoder->seeking);
assert(dc->pipe != NULL);
if (dc->command == DECODE_COMMAND_SEEK) {
/* delete frames from the old song position */ /* delete frames from the old song position */
if (decoder->chunk != NULL) { if (decoder->chunk != NULL) {
music_buffer_return(dc.buffer, decoder->chunk); music_buffer_return(dc->buffer, decoder->chunk);
decoder->chunk = NULL; decoder->chunk = NULL;
} }
music_pipe_clear(dc.pipe, dc.buffer); music_pipe_clear(dc->pipe, dc->buffer);
} }
dc.command = DECODE_COMMAND_NONE; dc->command = DECODE_COMMAND_NONE;
decoder_unlock(); decoder_unlock(dc);
player_lock_signal(); player_lock_signal();
} }
double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder) double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
{ {
assert(dc.command == DECODE_COMMAND_SEEK); const struct decoder_control *dc = decoder->dc;
assert(dc.pipe != NULL);
assert(dc->command == DECODE_COMMAND_SEEK);
assert(dc->pipe != NULL);
decoder->seeking = true; decoder->seeking = true;
return dc.seek_where; return dc->seek_where;
} }
void decoder_seek_error(struct decoder * decoder) void decoder_seek_error(struct decoder * decoder)
{ {
assert(dc.command == DECODE_COMMAND_SEEK); struct decoder_control *dc = decoder->dc;
assert(dc.pipe != NULL);
dc.seek_error = true; assert(dc->command == DECODE_COMMAND_SEEK);
assert(dc->pipe != NULL);
dc->seek_error = true;
decoder_command_finished(decoder); decoder_command_finished(decoder);
} }
@ -138,11 +153,12 @@ size_t decoder_read(struct decoder *decoder,
struct input_stream *is, struct input_stream *is,
void *buffer, size_t length) void *buffer, size_t length)
{ {
const struct decoder_control *dc = decoder->dc;
size_t nbytes; size_t nbytes;
assert(decoder == NULL || assert(decoder == NULL ||
dc.state == DECODE_STATE_START || dc->state == DECODE_STATE_START ||
dc.state == DECODE_STATE_DECODE); dc->state == DECODE_STATE_DECODE);
assert(is != NULL); assert(is != NULL);
assert(buffer != NULL); assert(buffer != NULL);
@ -155,9 +171,9 @@ size_t decoder_read(struct decoder *decoder,
/* ignore the SEEK command during initialization, /* ignore the SEEK command during initialization,
the plugin should handle that after it has the plugin should handle that after it has
initialized successfully */ initialized successfully */
(dc.command != DECODE_COMMAND_SEEK || (dc->command != DECODE_COMMAND_SEEK ||
(dc.state != DECODE_STATE_START && !decoder->seeking)) && (dc->state != DECODE_STATE_START && !decoder->seeking)) &&
dc.command != DECODE_COMMAND_NONE) dc->command != DECODE_COMMAND_NONE)
return 0; return 0;
nbytes = input_stream_read(is, buffer, length); nbytes = input_stream_read(is, buffer, length);
@ -191,8 +207,8 @@ do_send_tag(struct decoder *decoder, struct input_stream *is,
chunk = decoder_get_chunk(decoder, is); chunk = decoder_get_chunk(decoder, is);
if (chunk == NULL) { if (chunk == NULL) {
assert(dc.command != DECODE_COMMAND_NONE); assert(decoder->dc->command != DECODE_COMMAND_NONE);
return dc.command; return decoder->dc->command;
} }
chunk->tag = tag_dup(tag); chunk->tag = tag_dup(tag);
@ -231,17 +247,18 @@ decoder_data(struct decoder *decoder,
float data_time, uint16_t bitRate, float data_time, uint16_t bitRate,
struct replay_gain_info *replay_gain_info) struct replay_gain_info *replay_gain_info)
{ {
struct decoder_control *dc = decoder->dc;
const char *data = _data; const char *data = _data;
GError *error = NULL; GError *error = NULL;
enum decoder_command cmd; enum decoder_command cmd;
assert(dc.state == DECODE_STATE_DECODE); assert(dc->state == DECODE_STATE_DECODE);
assert(dc.pipe != NULL); assert(dc->pipe != NULL);
assert(length % audio_format_frame_size(&dc.in_audio_format) == 0); assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);
decoder_lock(); decoder_lock(dc);
cmd = dc.command; cmd = dc->command;
decoder_unlock(); decoder_unlock(dc);
if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK || if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
length == 0) length == 0)
@ -266,10 +283,10 @@ decoder_data(struct decoder *decoder,
return cmd; return cmd;
} }
if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) { if (!audio_format_equals(&dc->in_audio_format, &dc->out_audio_format)) {
data = pcm_convert(&decoder->conv_state, data = pcm_convert(&decoder->conv_state,
&dc.in_audio_format, data, length, &dc->in_audio_format, data, length,
&dc.out_audio_format, &length, &dc->out_audio_format, &length,
&error); &error);
if (data == NULL) { if (data == NULL) {
/* the PCM conversion has failed - stop /* the PCM conversion has failed - stop
@ -288,11 +305,11 @@ decoder_data(struct decoder *decoder,
chunk = decoder_get_chunk(decoder, is); chunk = decoder_get_chunk(decoder, is);
if (chunk == NULL) { if (chunk == NULL) {
assert(dc.command != DECODE_COMMAND_NONE); assert(dc->command != DECODE_COMMAND_NONE);
return dc.command; return dc->command;
} }
dest = music_chunk_write(chunk, &dc.out_audio_format, dest = music_chunk_write(chunk, &dc->out_audio_format,
data_time, bitRate, &nbytes); data_time, bitRate, &nbytes);
if (dest == NULL) { if (dest == NULL) {
/* the chunk is full, flush it */ /* the chunk is full, flush it */
@ -314,13 +331,13 @@ decoder_data(struct decoder *decoder,
if (replay_gain_mode != REPLAY_GAIN_OFF) if (replay_gain_mode != REPLAY_GAIN_OFF)
replay_gain_apply(replay_gain_info, dest, nbytes, replay_gain_apply(replay_gain_info, dest, nbytes,
&dc.out_audio_format); &dc->out_audio_format);
else if (normalizationEnabled) else if (normalizationEnabled)
normalizeData(dest, nbytes, &dc.out_audio_format); normalizeData(dest, nbytes, &dc->out_audio_format);
/* expand the music pipe chunk */ /* expand the music pipe chunk */
full = music_chunk_expand(chunk, &dc.out_audio_format, nbytes); full = music_chunk_expand(chunk, &dc->out_audio_format, nbytes);
if (full) { if (full) {
/* the chunk is full, flush it */ /* the chunk is full, flush it */
decoder_flush_chunk(decoder); decoder_flush_chunk(decoder);
@ -338,10 +355,11 @@ enum decoder_command
decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is, decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
const struct tag *tag) const struct tag *tag)
{ {
const struct decoder_control *dc = decoder->dc;
enum decoder_command cmd; enum decoder_command cmd;
assert(dc.state == DECODE_STATE_DECODE); assert(dc->state == DECODE_STATE_DECODE);
assert(dc.pipe != NULL); assert(dc->pipe != NULL);
assert(tag != NULL); assert(tag != NULL);
/* save the tag */ /* save the tag */

View File

@ -22,131 +22,134 @@
#include <assert.h> #include <assert.h>
struct decoder_control dc; void
dc_init(struct decoder_control *dc)
void dc_init(void)
{ {
dc.mutex = g_mutex_new(); dc->thread = NULL;
dc.cond = g_cond_new();
dc.state = DECODE_STATE_STOP; dc->mutex = g_mutex_new();
dc.command = DECODE_COMMAND_NONE; dc->cond = g_cond_new();
dc->state = DECODE_STATE_STOP;
dc->command = DECODE_COMMAND_NONE;
} }
void dc_deinit(void) void
dc_deinit(struct decoder_control *dc)
{ {
g_cond_free(dc.cond); g_cond_free(dc->cond);
g_mutex_free(dc.mutex); g_mutex_free(dc->mutex);
} }
static void static void
dc_command_wait_locked(void) dc_command_wait_locked(struct decoder_control *dc)
{ {
while (dc.command != DECODE_COMMAND_NONE) { while (dc->command != DECODE_COMMAND_NONE) {
decoder_signal(); decoder_signal(dc);
player_wait_decoder(); player_wait_decoder(dc);
} }
} }
void void
dc_command_wait(void) dc_command_wait(struct decoder_control *dc)
{ {
decoder_lock(); decoder_lock(dc);
dc_command_wait_locked(); dc_command_wait_locked(dc);
decoder_unlock(); decoder_unlock(dc);
} }
static void static void
dc_command_locked(enum decoder_command cmd) dc_command_locked(struct decoder_control *dc, enum decoder_command cmd)
{ {
dc.command = cmd; dc->command = cmd;
dc_command_wait_locked(); dc_command_wait_locked(dc);
} }
static void static void
dc_command(enum decoder_command cmd) dc_command(struct decoder_control *dc, enum decoder_command cmd)
{ {
decoder_lock(); decoder_lock(dc);
dc_command_locked(cmd); dc_command_locked(dc, cmd);
decoder_unlock(); decoder_unlock(dc);
} }
static void dc_command_async(enum decoder_command cmd) static void
dc_command_async(struct decoder_control *dc, enum decoder_command cmd)
{ {
decoder_lock(); decoder_lock(dc);
dc.command = cmd; dc->command = cmd;
decoder_signal(); decoder_signal(dc);
decoder_unlock(); decoder_unlock(dc);
} }
void void
dc_start(struct song *song) dc_start(struct decoder_control *dc, struct song *song)
{ {
assert(dc.pipe != NULL); assert(dc->pipe != NULL);
assert(song != NULL); assert(song != NULL);
dc.next_song = song; dc->next_song = song;
dc_command(DECODE_COMMAND_START); dc_command(dc, DECODE_COMMAND_START);
} }
void void
dc_start_async(struct song *song) dc_start_async(struct decoder_control *dc, struct song *song)
{ {
assert(dc.pipe != NULL); assert(dc->pipe != NULL);
assert(song != NULL); assert(song != NULL);
dc.next_song = song; dc->next_song = song;
dc_command_async(DECODE_COMMAND_START); dc_command_async(dc, DECODE_COMMAND_START);
} }
void void
dc_stop(void) dc_stop(struct decoder_control *dc)
{ {
decoder_lock(); decoder_lock(dc);
if (dc.command != DECODE_COMMAND_NONE) if (dc->command != DECODE_COMMAND_NONE)
/* Attempt to cancel the current command. If it's too /* Attempt to cancel the current command. If it's too
late and the decoder thread is already executing late and the decoder thread is already executing
the old command, we'll call STOP again in this the old command, we'll call STOP again in this
function (see below). */ function (see below). */
dc_command_locked(DECODE_COMMAND_STOP); dc_command_locked(dc, DECODE_COMMAND_STOP);
if (dc.state != DECODE_STATE_STOP && dc.state != DECODE_STATE_ERROR) if (dc->state != DECODE_STATE_STOP && dc->state != DECODE_STATE_ERROR)
dc_command_locked(DECODE_COMMAND_STOP); dc_command_locked(dc, DECODE_COMMAND_STOP);
decoder_unlock(); decoder_unlock(dc);
} }
bool bool
dc_seek(double where) dc_seek(struct decoder_control *dc, double where)
{ {
assert(dc.state != DECODE_STATE_START); assert(dc->state != DECODE_STATE_START);
assert(where >= 0.0); assert(where >= 0.0);
if (dc.state == DECODE_STATE_STOP || if (dc->state == DECODE_STATE_STOP ||
dc.state == DECODE_STATE_ERROR || !dc.seekable) dc->state == DECODE_STATE_ERROR || !dc->seekable)
return false; return false;
dc.seek_where = where; dc->seek_where = where;
dc.seek_error = false; dc->seek_error = false;
dc_command(DECODE_COMMAND_SEEK); dc_command(dc, DECODE_COMMAND_SEEK);
if (dc.seek_error) if (dc->seek_error)
return false; return false;
return true; return true;
} }
void void
dc_quit(void) dc_quit(struct decoder_control *dc)
{ {
assert(dc.thread != NULL); assert(dc->thread != NULL);
dc.quit = true; dc->quit = true;
dc_command_async(DECODE_COMMAND_STOP); dc_command_async(dc, DECODE_COMMAND_STOP);
g_thread_join(dc.thread); g_thread_join(dc->thread);
dc.thread = NULL; dc->thread = NULL;
} }

View File

@ -83,28 +83,28 @@ struct decoder_control {
struct music_pipe *pipe; struct music_pipe *pipe;
}; };
extern struct decoder_control dc; void
dc_init(struct decoder_control *dc);
void dc_init(void); void
dc_deinit(struct decoder_control *dc);
void dc_deinit(void);
/** /**
* Locks the #decoder_control object. * Locks the #decoder_control object.
*/ */
static inline void static inline void
decoder_lock(void) decoder_lock(struct decoder_control *dc)
{ {
g_mutex_lock(dc.mutex); g_mutex_lock(dc->mutex);
} }
/** /**
* Unlocks the #decoder_control object. * Unlocks the #decoder_control object.
*/ */
static inline void static inline void
decoder_unlock(void) decoder_unlock(struct decoder_control *dc)
{ {
g_mutex_unlock(dc.mutex); g_mutex_unlock(dc->mutex);
} }
/** /**
@ -113,9 +113,9 @@ decoder_unlock(void)
* prior to calling this function. * prior to calling this function.
*/ */
static inline void static inline void
decoder_wait(void) decoder_wait(struct decoder_control *dc)
{ {
g_cond_wait(dc.cond, dc.mutex); g_cond_wait(dc->cond, dc->mutex);
} }
/** /**
@ -124,75 +124,81 @@ decoder_wait(void)
* this function. * this function.
*/ */
static inline void static inline void
decoder_signal(void) decoder_signal(struct decoder_control *dc)
{ {
g_cond_signal(dc.cond); g_cond_signal(dc->cond);
} }
static inline bool decoder_is_idle(void) static inline bool
decoder_is_idle(const struct decoder_control *dc)
{ {
return (dc.state == DECODE_STATE_STOP || return (dc->state == DECODE_STATE_STOP ||
dc.state == DECODE_STATE_ERROR) && dc->state == DECODE_STATE_ERROR) &&
dc.command != DECODE_COMMAND_START; dc->command != DECODE_COMMAND_START;
} }
static inline bool decoder_is_starting(void) static inline bool
decoder_is_starting(const struct decoder_control *dc)
{ {
return dc.command == DECODE_COMMAND_START || return dc->command == DECODE_COMMAND_START ||
dc.state == DECODE_STATE_START; dc->state == DECODE_STATE_START;
} }
static inline bool decoder_has_failed(void) static inline bool
decoder_has_failed(const struct decoder_control *dc)
{ {
assert(dc.command == DECODE_COMMAND_NONE); assert(dc->command == DECODE_COMMAND_NONE);
return dc.state == DECODE_STATE_ERROR; return dc->state == DECODE_STATE_ERROR;
} }
static inline bool decoder_lock_is_idle(void) static inline bool
decoder_lock_is_idle(struct decoder_control *dc)
{ {
bool ret; bool ret;
decoder_lock(); decoder_lock(dc);
ret = decoder_is_idle(); ret = decoder_is_idle(dc);
decoder_unlock(); decoder_unlock(dc);
return ret; return ret;
} }
static inline bool decoder_lock_is_starting(void) static inline bool
decoder_lock_is_starting(struct decoder_control *dc)
{ {
bool ret; bool ret;
decoder_lock(); decoder_lock(dc);
ret = decoder_is_starting(); ret = decoder_is_starting(dc);
decoder_unlock(); decoder_unlock(dc);
return ret; return ret;
} }
static inline bool decoder_lock_has_failed(void) static inline bool
decoder_lock_has_failed(struct decoder_control *dc)
{ {
bool ret; bool ret;
decoder_lock(); decoder_lock(dc);
ret = decoder_has_failed(); ret = decoder_has_failed(dc);
decoder_unlock(); decoder_unlock(dc);
return ret; return ret;
} }
static inline struct song * static inline const struct song *
decoder_current_song(void) decoder_current_song(const struct decoder_control *dc)
{ {
switch (dc.state) { switch (dc->state) {
case DECODE_STATE_STOP: case DECODE_STATE_STOP:
case DECODE_STATE_ERROR: case DECODE_STATE_ERROR:
return NULL; return NULL;
case DECODE_STATE_START: case DECODE_STATE_START:
case DECODE_STATE_DECODE: case DECODE_STATE_DECODE:
return dc.current_song; return dc->current_song;
} }
assert(false); assert(false);
@ -200,21 +206,21 @@ decoder_current_song(void)
} }
void void
dc_command_wait(void); dc_command_wait(struct decoder_control *dc);
void void
dc_start(struct song *song); dc_start(struct decoder_control *dc, struct song *song);
void void
dc_start_async(struct song *song); dc_start_async(struct decoder_control *dc, struct song *song);
void void
dc_stop(void); dc_stop(struct decoder_control *dc);
bool bool
dc_seek(double where); dc_seek(struct decoder_control *dc, double where);
void void
dc_quit(void); dc_quit(struct decoder_control *dc);
#endif #endif

View File

@ -34,13 +34,13 @@
* potentially blocking operation. * potentially blocking operation.
*/ */
static int static int
decoder_input_buffer(struct input_stream *is) decoder_input_buffer(struct decoder_control *dc, struct input_stream *is)
{ {
int ret; int ret;
decoder_unlock(); decoder_unlock(dc);
ret = input_stream_buffer(is) > 0; ret = input_stream_buffer(is) > 0;
decoder_lock(); decoder_lock(dc);
return ret; return ret;
} }
@ -50,17 +50,17 @@ decoder_input_buffer(struct input_stream *is)
* one. * one.
*/ */
static enum decoder_command static enum decoder_command
need_chunks(struct input_stream *is, bool do_wait) need_chunks(struct decoder_control *dc, struct input_stream *is, bool do_wait)
{ {
if (dc.command == DECODE_COMMAND_STOP || if (dc->command == DECODE_COMMAND_STOP ||
dc.command == DECODE_COMMAND_SEEK) dc->command == DECODE_COMMAND_SEEK)
return dc.command; return dc->command;
if ((is == NULL || decoder_input_buffer(is) <= 0) && do_wait) { if ((is == NULL || decoder_input_buffer(dc, is) <= 0) && do_wait) {
decoder_wait(); decoder_wait(dc);
player_signal(); player_signal();
return dc.command; return dc->command;
} }
return DECODE_COMMAND_NONE; return DECODE_COMMAND_NONE;
@ -69,6 +69,7 @@ need_chunks(struct input_stream *is, bool do_wait)
struct music_chunk * struct music_chunk *
decoder_get_chunk(struct decoder *decoder, struct input_stream *is) decoder_get_chunk(struct decoder *decoder, struct input_stream *is)
{ {
struct decoder_control *dc = decoder->dc;
enum decoder_command cmd; enum decoder_command cmd;
assert(decoder != NULL); assert(decoder != NULL);
@ -77,13 +78,13 @@ decoder_get_chunk(struct decoder *decoder, struct input_stream *is)
return decoder->chunk; return decoder->chunk;
do { do {
decoder->chunk = music_buffer_allocate(dc.buffer); decoder->chunk = music_buffer_allocate(dc->buffer);
if (decoder->chunk != NULL) if (decoder->chunk != NULL)
return decoder->chunk; return decoder->chunk;
decoder_lock(); decoder_lock(dc);
cmd = need_chunks(is, true); cmd = need_chunks(dc, is, true);
decoder_unlock(); decoder_unlock(dc);
} while (cmd == DECODE_COMMAND_NONE); } while (cmd == DECODE_COMMAND_NONE);
return NULL; return NULL;
@ -92,13 +93,15 @@ decoder_get_chunk(struct decoder *decoder, struct input_stream *is)
void void
decoder_flush_chunk(struct decoder *decoder) decoder_flush_chunk(struct decoder *decoder)
{ {
struct decoder_control *dc = decoder->dc;
assert(decoder != NULL); assert(decoder != NULL);
assert(decoder->chunk != NULL); assert(decoder->chunk != NULL);
if (music_chunk_is_empty(decoder->chunk)) if (music_chunk_is_empty(decoder->chunk))
music_buffer_return(dc.buffer, decoder->chunk); music_buffer_return(dc->buffer, decoder->chunk);
else else
music_pipe_push(dc.pipe, decoder->chunk); music_pipe_push(dc->pipe, decoder->chunk);
decoder->chunk = NULL; decoder->chunk = NULL;
} }

View File

@ -26,6 +26,8 @@
struct input_stream; struct input_stream;
struct decoder { struct decoder {
struct decoder_control *dc;
struct pcm_convert_state conv_state; struct pcm_convert_state conv_state;
bool seeking; bool seeking;

View File

@ -47,21 +47,21 @@ decoder_stream_decode(const struct decoder_plugin *plugin,
assert(decoder->decoder_tag == NULL); assert(decoder->decoder_tag == NULL);
assert(input_stream != NULL); assert(input_stream != NULL);
assert(input_stream->ready); assert(input_stream->ready);
assert(dc.state == DECODE_STATE_START); assert(decoder->dc->state == DECODE_STATE_START);
decoder_unlock(); decoder_unlock(decoder->dc);
/* rewind the stream, so each plugin gets a fresh start */ /* rewind the stream, so each plugin gets a fresh start */
input_stream_seek(input_stream, 0, SEEK_SET); input_stream_seek(input_stream, 0, SEEK_SET);
decoder_plugin_stream_decode(plugin, decoder, input_stream); decoder_plugin_stream_decode(plugin, decoder, input_stream);
decoder_lock(); decoder_lock(decoder->dc);
assert(dc.state == DECODE_STATE_START || assert(decoder->dc->state == DECODE_STATE_START ||
dc.state == DECODE_STATE_DECODE); decoder->dc->state == DECODE_STATE_DECODE);
return dc.state != DECODE_STATE_START; return decoder->dc->state != DECODE_STATE_START;
} }
static bool static bool
@ -75,30 +75,34 @@ decoder_file_decode(const struct decoder_plugin *plugin,
assert(decoder->decoder_tag == NULL); assert(decoder->decoder_tag == NULL);
assert(path != NULL); assert(path != NULL);
assert(g_path_is_absolute(path)); assert(g_path_is_absolute(path));
assert(dc.state == DECODE_STATE_START); assert(decoder->dc->state == DECODE_STATE_START);
decoder_unlock(); decoder_unlock(decoder->dc);
decoder_plugin_file_decode(plugin, decoder, path); decoder_plugin_file_decode(plugin, decoder, path);
decoder_lock(); decoder_lock(decoder->dc);
assert(dc.state == DECODE_STATE_START || assert(decoder->dc->state == DECODE_STATE_START ||
dc.state == DECODE_STATE_DECODE); decoder->dc->state == DECODE_STATE_DECODE);
return dc.state != DECODE_STATE_START; return decoder->dc->state != DECODE_STATE_START;
} }
static void decoder_run_song(const struct song *song, const char *uri) static void
decoder_run_song(struct decoder_control *dc,
const struct song *song, const char *uri)
{ {
struct decoder decoder; struct decoder decoder = {
.dc = dc,
};
int ret; int ret;
bool close_instream = true; bool close_instream = true;
struct input_stream input_stream; struct input_stream input_stream;
const struct decoder_plugin *plugin; const struct decoder_plugin *plugin;
if (!input_stream_open(&input_stream, uri)) { if (!input_stream_open(&input_stream, uri)) {
dc.state = DECODE_STATE_ERROR; dc->state = DECODE_STATE_ERROR;
return; return;
} }
@ -109,8 +113,8 @@ static void decoder_run_song(const struct song *song, const char *uri)
decoder.decoder_tag = NULL; decoder.decoder_tag = NULL;
decoder.chunk = NULL; decoder.chunk = NULL;
dc.state = DECODE_STATE_START; dc->state = DECODE_STATE_START;
dc.command = DECODE_COMMAND_NONE; dc->command = DECODE_COMMAND_NONE;
player_signal(); player_signal();
@ -118,32 +122,32 @@ static void decoder_run_song(const struct song *song, const char *uri)
will be available then */ will be available then */
while (!input_stream.ready) { while (!input_stream.ready) {
if (dc.command == DECODE_COMMAND_STOP) { if (dc->command == DECODE_COMMAND_STOP) {
decoder_unlock(); decoder_unlock(dc);
input_stream_close(&input_stream); input_stream_close(&input_stream);
decoder_lock(); decoder_lock(dc);
dc.state = DECODE_STATE_STOP; dc->state = DECODE_STATE_STOP;
return; return;
} }
decoder_unlock(); decoder_unlock(dc);
ret = input_stream_buffer(&input_stream); ret = input_stream_buffer(&input_stream);
if (ret < 0) { if (ret < 0) {
input_stream_close(&input_stream); input_stream_close(&input_stream);
decoder_lock(); decoder_lock(dc);
dc.state = DECODE_STATE_ERROR; dc->state = DECODE_STATE_ERROR;
return; return;
} }
decoder_lock(); decoder_lock(dc);
} }
if (dc.command == DECODE_COMMAND_STOP) { if (dc->command == DECODE_COMMAND_STOP) {
decoder_unlock(); decoder_unlock(dc);
input_stream_close(&input_stream); input_stream_close(&input_stream);
decoder_lock(); decoder_lock(dc);
dc.state = DECODE_STATE_STOP; dc->state = DECODE_STATE_STOP;
return; return;
} }
@ -162,7 +166,7 @@ static void decoder_run_song(const struct song *song, const char *uri)
if (ret) if (ret)
break; break;
assert(dc.state == DECODE_STATE_START); assert(dc->state == DECODE_STATE_START);
} }
/* if that fails, try suffix matching the URL: */ /* if that fails, try suffix matching the URL: */
@ -177,7 +181,7 @@ static void decoder_run_song(const struct song *song, const char *uri)
if (ret) if (ret)
break; break;
assert(dc.state == DECODE_STATE_START); assert(dc->state == DECODE_STATE_START);
} }
} }
/* fallback to mp3: */ /* fallback to mp3: */
@ -196,9 +200,9 @@ static void decoder_run_song(const struct song *song, const char *uri)
const char *s = uri_get_suffix(uri); const char *s = uri_get_suffix(uri);
while ((plugin = decoder_plugin_from_suffix(s, next++))) { while ((plugin = decoder_plugin_from_suffix(s, next++))) {
if (plugin->file_decode != NULL) { if (plugin->file_decode != NULL) {
decoder_unlock(); decoder_unlock(dc);
input_stream_close(&input_stream); input_stream_close(&input_stream);
decoder_lock(); decoder_lock(dc);
close_instream = false; close_instream = false;
ret = decoder_file_decode(plugin, ret = decoder_file_decode(plugin,
@ -213,9 +217,9 @@ static void decoder_run_song(const struct song *song, const char *uri)
reopen it */ reopen it */
bool success; bool success;
decoder_unlock(); decoder_unlock(dc);
success = input_stream_open(&input_stream, uri); success = input_stream_open(&input_stream, uri);
decoder_lock(); decoder_lock(dc);
if (success) if (success)
close_instream = true; close_instream = true;
@ -231,7 +235,7 @@ static void decoder_run_song(const struct song *song, const char *uri)
} }
} }
decoder_unlock(); decoder_unlock(dc);
pcm_convert_deinit(&decoder.conv_state); pcm_convert_deinit(&decoder.conv_state);
@ -251,14 +255,15 @@ static void decoder_run_song(const struct song *song, const char *uri)
if (decoder.decoder_tag != NULL) if (decoder.decoder_tag != NULL)
tag_free(decoder.decoder_tag); tag_free(decoder.decoder_tag);
decoder_lock(); decoder_lock(dc);
dc.state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR; dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR;
} }
static void decoder_run(void) static void
decoder_run(struct decoder_control *dc)
{ {
struct song *song = dc.next_song; struct song *song = dc->next_song;
char *uri; char *uri;
if (song_is_file(song)) if (song_is_file(song))
@ -267,58 +272,62 @@ static void decoder_run(void)
uri = song_get_uri(song); uri = song_get_uri(song);
if (uri == NULL) { if (uri == NULL) {
dc.state = DECODE_STATE_ERROR; dc->state = DECODE_STATE_ERROR;
return; return;
} }
dc.current_song = dc.next_song; /* NEED LOCK */ dc->current_song = dc->next_song; /* NEED LOCK */
decoder_run_song(song, uri); decoder_run_song(dc, song, uri);
g_free(uri); g_free(uri);
} }
static gpointer decoder_task(G_GNUC_UNUSED gpointer arg) static gpointer
decoder_task(gpointer arg)
{ {
decoder_lock(); struct decoder_control *dc = arg;
decoder_lock(dc);
do { do {
assert(dc.state == DECODE_STATE_STOP || assert(dc->state == DECODE_STATE_STOP ||
dc.state == DECODE_STATE_ERROR); dc->state == DECODE_STATE_ERROR);
switch (dc.command) { switch (dc->command) {
case DECODE_COMMAND_START: case DECODE_COMMAND_START:
case DECODE_COMMAND_SEEK: case DECODE_COMMAND_SEEK:
decoder_run(); decoder_run(dc);
dc.command = DECODE_COMMAND_NONE; dc->command = DECODE_COMMAND_NONE;
player_signal(); player_signal();
break; break;
case DECODE_COMMAND_STOP: case DECODE_COMMAND_STOP:
dc.command = DECODE_COMMAND_NONE; dc->command = DECODE_COMMAND_NONE;
player_signal(); player_signal();
break; break;
case DECODE_COMMAND_NONE: case DECODE_COMMAND_NONE:
decoder_wait(); decoder_wait(dc);
break; break;
} }
} while (dc.command != DECODE_COMMAND_NONE || !dc.quit); } while (dc->command != DECODE_COMMAND_NONE || !dc->quit);
decoder_unlock(); decoder_unlock(dc);
return NULL; return NULL;
} }
void decoder_thread_start(void) void
decoder_thread_start(struct decoder_control *dc)
{ {
GError *e = NULL; GError *e = NULL;
assert(dc.thread == NULL); assert(dc->thread == NULL);
dc.thread = g_thread_create(decoder_task, NULL, true, &e); dc->thread = g_thread_create(decoder_task, dc, true, &e);
if (dc.thread == NULL) if (dc->thread == NULL)
g_error("Failed to spawn decoder task: %s", e->message); g_error("Failed to spawn decoder task: %s", e->message);
} }

View File

@ -20,6 +20,9 @@
#ifndef MPD_DECODER_THREAD_H #ifndef MPD_DECODER_THREAD_H
#define MPD_DECODER_THREAD_H #define MPD_DECODER_THREAD_H
void decoder_thread_start(void); struct decoder_control;
void
decoder_thread_start(struct decoder_control *dc);
#endif #endif

View File

@ -33,7 +33,6 @@
#include "path.h" #include "path.h"
#include "mapper.h" #include "mapper.h"
#include "chunk.h" #include "chunk.h"
#include "decoder_control.h"
#include "player_control.h" #include "player_control.h"
#include "stats.h" #include "stats.h"
#include "sig_handlers.h" #include "sig_handlers.h"
@ -254,7 +253,6 @@ initialize_decoder_and_player(void)
buffered_before_play = buffered_chunks; buffered_before_play = buffered_chunks;
pc_init(buffered_chunks, buffered_before_play); pc_init(buffered_chunks, buffered_before_play);
dc_init();
} }
/** /**
@ -426,7 +424,6 @@ int main(int argc, char *argv[])
mapper_finish(); mapper_finish();
path_global_finish(); path_global_finish();
finishPermissions(); finishPermissions();
dc_deinit();
pc_deinit(); pc_deinit();
command_finish(); command_finish();
update_global_finish(); update_global_finish();

View File

@ -53,11 +53,11 @@ void pc_deinit(void)
} }
void void
player_wait_decoder(void) player_wait_decoder(struct decoder_control *dc)
{ {
/* during this function, the decoder lock is held, because /* during this function, the decoder lock is held, because
we're waiting for the decoder thread */ we're waiting for the decoder thread */
g_cond_wait(pc.cond, dc.mutex); g_cond_wait(pc.cond, dc->mutex);
} }
void void

View File

@ -25,6 +25,8 @@
#include <stdint.h> #include <stdint.h>
struct decoder_control;
enum player_state { enum player_state {
PLAYER_STATE_STOP = 0, PLAYER_STATE_STOP = 0,
PLAYER_STATE_PAUSE, PLAYER_STATE_PAUSE,
@ -155,7 +157,7 @@ player_wait(void)
* Note the small difference to the player_wait() function! * Note the small difference to the player_wait() function!
*/ */
void void
player_wait_decoder(void); player_wait_decoder(struct decoder_control *dc);
/** /**
* Signals the #player_control object. The object should be locked * Signals the #player_control object. The object should be locked

View File

@ -46,6 +46,8 @@ enum xfade_state {
}; };
struct player { struct player {
struct decoder_control *dc;
struct music_pipe *pipe; struct music_pipe *pipe;
/** /**
@ -129,17 +131,19 @@ static void player_command_finished(void)
static void static void
player_dc_stop(struct player *player) player_dc_stop(struct player *player)
{ {
dc_stop(); struct decoder_control *dc = player->dc;
if (dc.pipe != NULL) { dc_stop(dc);
if (dc->pipe != NULL) {
/* clear and free the decoder pipe */ /* clear and free the decoder pipe */
music_pipe_clear(dc.pipe, player_buffer); music_pipe_clear(dc->pipe, player_buffer);
if (dc.pipe != player->pipe) if (dc->pipe != player->pipe)
music_pipe_free(dc.pipe); music_pipe_free(dc->pipe);
dc.pipe = NULL; dc->pipe = NULL;
} }
} }
@ -153,13 +157,15 @@ player_dc_stop(struct player *player)
static bool static bool
player_wait_for_decoder(struct player *player) player_wait_for_decoder(struct player *player)
{ {
dc_command_wait(); struct decoder_control *dc = player->dc;
if (decoder_lock_has_failed()) { dc_command_wait(dc);
assert(dc.next_song == NULL || dc.next_song->uri != NULL);
if (decoder_lock_has_failed(dc)) {
assert(dc->next_song == NULL || dc->next_song->uri != NULL);
player_lock(); player_lock();
pc.errored_song = dc.next_song; pc.errored_song = dc->next_song;
pc.error = PLAYER_ERROR_FILE; pc.error = PLAYER_ERROR_FILE;
pc.next_song = NULL; pc.next_song = NULL;
player_unlock(); player_unlock();
@ -198,24 +204,26 @@ player_wait_for_decoder(struct player *player)
static bool static bool
player_check_decoder_startup(struct player *player) player_check_decoder_startup(struct player *player)
{ {
struct decoder_control *dc = player->dc;
assert(player->decoder_starting); assert(player->decoder_starting);
decoder_lock(); decoder_lock(dc);
if (decoder_has_failed()) { if (decoder_has_failed(dc)) {
/* the decoder failed */ /* the decoder failed */
assert(dc.next_song == NULL || dc.next_song->uri != NULL); assert(dc->next_song == NULL || dc->next_song->uri != NULL);
decoder_unlock(); decoder_unlock(dc);
pc.errored_song = dc.next_song; pc.errored_song = dc->next_song;
pc.error = PLAYER_ERROR_FILE; pc.error = PLAYER_ERROR_FILE;
return false; return false;
} else if (!decoder_is_starting()) { } else if (!decoder_is_starting(dc)) {
/* the decoder is ready and ok */ /* the decoder is ready and ok */
decoder_unlock(); decoder_unlock(dc);
if (audio_format_defined(&player->play_audio_format) && if (audio_format_defined(&player->play_audio_format) &&
!audio_output_all_wait(1)) !audio_output_all_wait(1))
@ -223,21 +231,21 @@ player_check_decoder_startup(struct player *player)
all chunks yet - wait for that */ all chunks yet - wait for that */
return true; return true;
pc.total_time = dc.total_time; pc.total_time = dc->total_time;
pc.audio_format = dc.in_audio_format; pc.audio_format = dc->in_audio_format;
player->play_audio_format = dc.out_audio_format; player->play_audio_format = dc->out_audio_format;
player->decoder_starting = false; player->decoder_starting = false;
if (!player->paused && if (!player->paused &&
!audio_output_all_open(&dc.out_audio_format, !audio_output_all_open(&dc->out_audio_format,
player_buffer)) { player_buffer)) {
char *uri = song_get_uri(dc.next_song); char *uri = song_get_uri(dc->next_song);
g_warning("problems opening audio device " g_warning("problems opening audio device "
"while playing \"%s\"", uri); "while playing \"%s\"", uri);
g_free(uri); g_free(uri);
assert(dc.next_song == NULL || dc.next_song->uri != NULL); assert(dc->next_song == NULL || dc->next_song->uri != NULL);
pc.errored_song = dc.next_song; pc.errored_song = dc->next_song;
pc.error = PLAYER_ERROR_AUDIO; pc.error = PLAYER_ERROR_AUDIO;
/* pause: the user may resume playback as soon /* pause: the user may resume playback as soon
@ -251,8 +259,8 @@ player_check_decoder_startup(struct player *player)
} else { } else {
/* the decoder is not yet ready; wait /* the decoder is not yet ready; wait
some more */ some more */
player_wait_decoder(); player_wait_decoder(dc);
decoder_unlock(); decoder_unlock(dc);
return true; return true;
} }
@ -305,12 +313,13 @@ player_send_silence(struct player *player)
*/ */
static bool player_seek_decoder(struct player *player) static bool player_seek_decoder(struct player *player)
{ {
struct decoder_control *dc = player->dc;
double where; double where;
bool ret; bool ret;
assert(pc.next_song != NULL); assert(pc.next_song != NULL);
if (decoder_current_song() != pc.next_song) { if (decoder_current_song(dc) != pc.next_song) {
/* the decoder is already decoding the "next" song - /* the decoder is already decoding the "next" song -
stop it and start the previous song again */ stop it and start the previous song again */
@ -319,10 +328,10 @@ static bool player_seek_decoder(struct player *player)
/* clear music chunks which might still reside in the /* clear music chunks which might still reside in the
pipe */ pipe */
music_pipe_clear(player->pipe, player_buffer); music_pipe_clear(player->pipe, player_buffer);
dc.pipe = player->pipe; dc->pipe = player->pipe;
/* re-start the decoder */ /* re-start the decoder */
dc_start_async(pc.next_song); dc_start_async(dc, pc.next_song);
ret = player_wait_for_decoder(player); ret = player_wait_for_decoder(player);
if (!ret) { if (!ret) {
/* decoder failure */ /* decoder failure */
@ -353,7 +362,7 @@ static bool player_seek_decoder(struct player *player)
if (where < 0.0) if (where < 0.0)
where = 0.0; where = 0.0;
ret = dc_seek(where); ret = dc_seek(dc, where);
if (!ret) { if (!ret) {
/* decoder failure */ /* decoder failure */
player_command_finished(); player_command_finished();
@ -379,6 +388,8 @@ static bool player_seek_decoder(struct player *player)
*/ */
static void player_process_command(struct player *player) static void player_process_command(struct player *player)
{ {
struct decoder_control *dc = player->dc;
switch (pc.command) { switch (pc.command) {
case PLAYER_COMMAND_NONE: case PLAYER_COMMAND_NONE:
case PLAYER_COMMAND_STOP: case PLAYER_COMMAND_STOP:
@ -396,7 +407,7 @@ static void player_process_command(struct player *player)
case PLAYER_COMMAND_QUEUE: case PLAYER_COMMAND_QUEUE:
assert(pc.next_song != NULL); assert(pc.next_song != NULL);
assert(!player->queued); assert(!player->queued);
assert(dc.pipe == NULL || dc.pipe == player->pipe); assert(dc->pipe == NULL || dc->pipe == player->pipe);
player->queued = true; player->queued = true;
player_command_finished_locked(); player_command_finished_locked();
@ -425,8 +436,8 @@ static void player_process_command(struct player *player)
} else { } else {
/* the audio device has failed - rollback to /* the audio device has failed - rollback to
pause mode */ pause mode */
assert(dc.next_song == NULL || dc.next_song->uri != NULL); assert(dc->next_song == NULL || dc->next_song->uri != NULL);
pc.errored_song = dc.next_song; pc.errored_song = dc->next_song;
pc.error = PLAYER_ERROR_AUDIO; pc.error = PLAYER_ERROR_AUDIO;
player->paused = true; player->paused = true;
@ -452,7 +463,7 @@ static void player_process_command(struct player *player)
return; return;
} }
if (dc.pipe != NULL && dc.pipe != player->pipe) { if (dc->pipe != NULL && dc->pipe != player->pipe) {
/* the decoder is already decoding the song - /* the decoder is already decoding the song -
stop it and reset the position */ stop it and reset the position */
player_unlock(); player_unlock();
@ -549,6 +560,7 @@ play_chunk(struct song *song, struct music_chunk *chunk,
static bool static bool
play_next_chunk(struct player *player) play_next_chunk(struct player *player)
{ {
struct decoder_control *dc = player->dc;
struct music_chunk *chunk = NULL; struct music_chunk *chunk = NULL;
unsigned cross_fade_position; unsigned cross_fade_position;
bool success; bool success;
@ -559,12 +571,12 @@ play_next_chunk(struct player *player)
return true; return true;
if (player->xfade == XFADE_ENABLED && if (player->xfade == XFADE_ENABLED &&
dc.pipe != NULL && dc.pipe != player->pipe && dc->pipe != NULL && dc->pipe != player->pipe &&
(cross_fade_position = music_pipe_size(player->pipe)) (cross_fade_position = music_pipe_size(player->pipe))
<= player->cross_fade_chunks) { <= player->cross_fade_chunks) {
/* perform cross fade */ /* perform cross fade */
struct music_chunk *other_chunk = struct music_chunk *other_chunk =
music_pipe_shift(dc.pipe); music_pipe_shift(dc->pipe);
if (!player->cross_fading) { if (!player->cross_fading) {
/* beginning of the cross fade - adjust /* beginning of the cross fade - adjust
@ -580,26 +592,26 @@ play_next_chunk(struct player *player)
assert(chunk != NULL); assert(chunk != NULL);
cross_fade_apply(chunk, other_chunk, cross_fade_apply(chunk, other_chunk,
&dc.out_audio_format, &dc->out_audio_format,
cross_fade_position, cross_fade_position,
player->cross_fade_chunks); player->cross_fade_chunks);
music_buffer_return(player_buffer, other_chunk); music_buffer_return(player_buffer, other_chunk);
} else { } else {
/* there are not enough decoded chunks yet */ /* there are not enough decoded chunks yet */
decoder_lock(); decoder_lock(dc);
if (decoder_is_idle()) { if (decoder_is_idle(dc)) {
/* the decoder isn't running, abort /* the decoder isn't running, abort
cross fading */ cross fading */
decoder_unlock(); decoder_unlock(dc);
player->xfade = XFADE_DISABLED; player->xfade = XFADE_DISABLED;
} else { } else {
/* wait for the decoder */ /* wait for the decoder */
decoder_signal(); decoder_signal(dc);
player_wait_decoder(); player_wait_decoder(dc);
decoder_unlock(); decoder_unlock(dc);
return true; return true;
} }
@ -635,12 +647,12 @@ play_next_chunk(struct player *player)
/* this formula should prevent that the decoder gets woken up /* this formula should prevent that the decoder gets woken up
with each chunk; it is more efficient to make it decode a with each chunk; it is more efficient to make it decode a
larger block at a time */ larger block at a time */
decoder_lock(); decoder_lock(dc);
if (!decoder_is_idle() && if (!decoder_is_idle(dc) &&
music_pipe_size(dc.pipe) <= (pc.buffered_before_play + music_pipe_size(dc->pipe) <= (pc.buffered_before_play +
music_buffer_size(player_buffer) * 3) / 4) music_buffer_size(player_buffer) * 3) / 4)
decoder_signal(); decoder_signal(dc);
decoder_unlock(); decoder_unlock(dc);
return true; return true;
} }
@ -666,7 +678,7 @@ player_song_border(struct player *player)
g_free(uri); g_free(uri);
music_pipe_free(player->pipe); music_pipe_free(player->pipe);
player->pipe = dc.pipe; player->pipe = player->dc->pipe;
if (!player_wait_for_decoder(player)) if (!player_wait_for_decoder(player))
return false; return false;
@ -679,9 +691,10 @@ player_song_border(struct player *player)
* basically a state machine, which multiplexes data between the * basically a state machine, which multiplexes data between the
* decoder thread and the output threads. * decoder thread and the output threads.
*/ */
static void do_play(void) static void do_play(struct decoder_control *dc)
{ {
struct player player = { struct player player = {
.dc = dc,
.buffering = true, .buffering = true,
.decoder_starting = false, .decoder_starting = false,
.paused = false, .paused = false,
@ -697,9 +710,9 @@ static void do_play(void)
player.pipe = music_pipe_new(); player.pipe = music_pipe_new();
dc.buffer = player_buffer; dc->buffer = player_buffer;
dc.pipe = player.pipe; dc->pipe = player.pipe;
dc_start(pc.next_song); dc_start(dc, pc.next_song);
if (!player_wait_for_decoder(&player)) { if (!player_wait_for_decoder(&player)) {
player_dc_stop(&player); player_dc_stop(&player);
player_command_finished(); player_command_finished();
@ -731,7 +744,7 @@ static void do_play(void)
prevent stuttering on slow machines */ prevent stuttering on slow machines */
if (music_pipe_size(player.pipe) < pc.buffered_before_play && if (music_pipe_size(player.pipe) < pc.buffered_before_play &&
!decoder_lock_is_idle()) { !decoder_lock_is_idle(dc)) {
/* not enough decoded buffer space yet */ /* not enough decoded buffer space yet */
if (!player.paused && if (!player.paused &&
@ -740,10 +753,10 @@ static void do_play(void)
!player_send_silence(&player)) !player_send_silence(&player))
break; break;
decoder_lock(); decoder_lock(dc);
/* XXX race condition: check decoder again */ /* XXX race condition: check decoder again */
player_wait_decoder(); player_wait_decoder(dc);
decoder_unlock(); decoder_unlock(dc);
player_lock(); player_lock();
continue; continue;
} else { } else {
@ -768,30 +781,30 @@ static void do_play(void)
/* /*
music_pipe_check_format(&play_audio_format, music_pipe_check_format(&play_audio_format,
player.next_song_chunk, player.next_song_chunk,
&dc.out_audio_format); &dc->out_audio_format);
*/ */
#endif #endif
if (decoder_lock_is_idle() && player.queued) { if (decoder_lock_is_idle(dc) && player.queued) {
/* the decoder has finished the current song; /* the decoder has finished the current song;
make it decode the next song */ make it decode the next song */
assert(pc.next_song != NULL); assert(pc.next_song != NULL);
assert(dc.pipe == NULL || dc.pipe == player.pipe); assert(dc->pipe == NULL || dc->pipe == player.pipe);
player.queued = false; player.queued = false;
dc.pipe = music_pipe_new(); dc->pipe = music_pipe_new();
dc_start_async(pc.next_song); dc_start_async(dc, pc.next_song);
} }
if (dc.pipe != NULL && dc.pipe != player.pipe && if (dc->pipe != NULL && dc->pipe != player.pipe &&
player.xfade == XFADE_UNKNOWN && player.xfade == XFADE_UNKNOWN &&
!decoder_lock_is_starting()) { !decoder_lock_is_starting(dc)) {
/* enable cross fading in this song? if yes, /* enable cross fading in this song? if yes,
calculate how many chunks will be required calculate how many chunks will be required
for it */ for it */
player.cross_fade_chunks = player.cross_fade_chunks =
cross_fade_calc(pc.cross_fade_seconds, dc.total_time, cross_fade_calc(pc.cross_fade_seconds, dc->total_time,
&dc.out_audio_format, &dc->out_audio_format,
&player.play_audio_format, &player.play_audio_format,
music_buffer_size(player_buffer) - music_buffer_size(player_buffer) -
pc.buffered_before_play); pc.buffered_before_play);
@ -820,12 +833,12 @@ static void do_play(void)
/* XXX synchronize in a better way */ /* XXX synchronize in a better way */
g_usleep(10000); g_usleep(10000);
} else if (dc.pipe != NULL && dc.pipe != player.pipe) { } else if (dc->pipe != NULL && dc->pipe != player.pipe) {
/* at the beginning of a new song */ /* at the beginning of a new song */
if (!player_song_border(&player)) if (!player_song_border(&player))
break; break;
} else if (decoder_lock_is_idle()) { } else if (decoder_lock_is_idle(dc)) {
/* check the size of the pipe again, because /* check the size of the pipe again, because
the decoder thread may have added something the decoder thread may have added something
since we last checked */ since we last checked */
@ -861,7 +874,10 @@ static void do_play(void)
static gpointer player_task(G_GNUC_UNUSED gpointer arg) static gpointer player_task(G_GNUC_UNUSED gpointer arg)
{ {
decoder_thread_start(); struct decoder_control dc;
dc_init(&dc);
decoder_thread_start(&dc);
player_buffer = music_buffer_new(pc.buffer_chunks); player_buffer = music_buffer_new(pc.buffer_chunks);
@ -872,7 +888,7 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
case PLAYER_COMMAND_QUEUE: case PLAYER_COMMAND_QUEUE:
assert(pc.next_song != NULL); assert(pc.next_song != NULL);
do_play(); do_play(&dc);
break; break;
case PLAYER_COMMAND_STOP: case PLAYER_COMMAND_STOP:
@ -916,7 +932,8 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
case PLAYER_COMMAND_EXIT: case PLAYER_COMMAND_EXIT:
player_unlock(); player_unlock();
dc_quit(); dc_quit(&dc);
dc_deinit(&dc);
audio_output_all_close(); audio_output_all_close();
music_buffer_free(player_buffer); music_buffer_free(player_buffer);