diff --git a/src/decoder_api.c b/src/decoder_api.c index 070151a67..f1ce0f5f7 100644 --- a/src/decoder_api.c +++ b/src/decoder_api.c @@ -37,12 +37,15 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "decoder" -void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder, - const struct audio_format *audio_format, - bool seekable, float total_time) +void +decoder_initialized(struct decoder *decoder, + const struct audio_format *audio_format, + bool seekable, float total_time) { - assert(dc.state == DECODE_STATE_START); - assert(dc.pipe != NULL); + struct decoder_control *dc = decoder->dc; + + assert(dc->state == DECODE_STATE_START); + assert(dc->pipe != NULL); assert(decoder != NULL); assert(decoder->stream_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_valid(audio_format)); - dc.in_audio_format = *audio_format; - getOutputAudioFormat(audio_format, &dc.out_audio_format); + dc->in_audio_format = *audio_format; + getOutputAudioFormat(audio_format, &dc->out_audio_format); - dc.seekable = seekable; - dc.total_time = total_time; + dc->seekable = seekable; + dc->total_time = total_time; - decoder_lock(); - dc.state = DECODE_STATE_DECODE; - decoder_unlock(); + decoder_lock(dc); + dc->state = DECODE_STATE_DECODE; + decoder_unlock(dc); player_lock_signal(); g_debug("audio_format=%u:%u:%u, seekable=%s", - dc.in_audio_format.sample_rate, dc.in_audio_format.bits, - dc.in_audio_format.channels, + dc->in_audio_format.sample_rate, + dc->in_audio_format.bits, + dc->in_audio_format.channels, 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", - dc.out_audio_format.sample_rate, - dc.out_audio_format.bits, - dc.out_audio_format.channels); + dc->out_audio_format.sample_rate, + dc->out_audio_format.bits, + dc->out_audio_format.channels); } 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) { - 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) { - decoder_lock(); + struct decoder_control *dc = decoder->dc; - assert(dc.command != DECODE_COMMAND_NONE); - assert(dc.command != DECODE_COMMAND_SEEK || - dc.seek_error || decoder->seeking); - assert(dc.pipe != NULL); + decoder_lock(dc); - 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 */ if (decoder->chunk != NULL) { - music_buffer_return(dc.buffer, decoder->chunk); + music_buffer_return(dc->buffer, decoder->chunk); decoder->chunk = NULL; } - music_pipe_clear(dc.pipe, dc.buffer); + music_pipe_clear(dc->pipe, dc->buffer); } - dc.command = DECODE_COMMAND_NONE; - decoder_unlock(); + dc->command = DECODE_COMMAND_NONE; + decoder_unlock(dc); player_lock_signal(); } double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder) { - assert(dc.command == DECODE_COMMAND_SEEK); - assert(dc.pipe != NULL); + const struct decoder_control *dc = decoder->dc; + + assert(dc->command == DECODE_COMMAND_SEEK); + assert(dc->pipe != NULL); decoder->seeking = true; - return dc.seek_where; + return dc->seek_where; } void decoder_seek_error(struct decoder * decoder) { - assert(dc.command == DECODE_COMMAND_SEEK); - assert(dc.pipe != NULL); + struct decoder_control *dc = decoder->dc; - dc.seek_error = true; + assert(dc->command == DECODE_COMMAND_SEEK); + assert(dc->pipe != NULL); + + dc->seek_error = true; decoder_command_finished(decoder); } @@ -138,11 +153,12 @@ size_t decoder_read(struct decoder *decoder, struct input_stream *is, void *buffer, size_t length) { + const struct decoder_control *dc = decoder->dc; size_t nbytes; assert(decoder == NULL || - dc.state == DECODE_STATE_START || - dc.state == DECODE_STATE_DECODE); + dc->state == DECODE_STATE_START || + dc->state == DECODE_STATE_DECODE); assert(is != NULL); assert(buffer != NULL); @@ -155,9 +171,9 @@ size_t decoder_read(struct decoder *decoder, /* ignore the SEEK command during initialization, the plugin should handle that after it has initialized successfully */ - (dc.command != DECODE_COMMAND_SEEK || - (dc.state != DECODE_STATE_START && !decoder->seeking)) && - dc.command != DECODE_COMMAND_NONE) + (dc->command != DECODE_COMMAND_SEEK || + (dc->state != DECODE_STATE_START && !decoder->seeking)) && + dc->command != DECODE_COMMAND_NONE) return 0; 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); if (chunk == NULL) { - assert(dc.command != DECODE_COMMAND_NONE); - return dc.command; + assert(decoder->dc->command != DECODE_COMMAND_NONE); + return decoder->dc->command; } chunk->tag = tag_dup(tag); @@ -231,17 +247,18 @@ decoder_data(struct decoder *decoder, float data_time, uint16_t bitRate, struct replay_gain_info *replay_gain_info) { + struct decoder_control *dc = decoder->dc; const char *data = _data; GError *error = NULL; enum decoder_command cmd; - assert(dc.state == DECODE_STATE_DECODE); - assert(dc.pipe != NULL); - assert(length % audio_format_frame_size(&dc.in_audio_format) == 0); + assert(dc->state == DECODE_STATE_DECODE); + assert(dc->pipe != NULL); + assert(length % audio_format_frame_size(&dc->in_audio_format) == 0); - decoder_lock(); - cmd = dc.command; - decoder_unlock(); + decoder_lock(dc); + cmd = dc->command; + decoder_unlock(dc); if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK || length == 0) @@ -266,10 +283,10 @@ decoder_data(struct decoder *decoder, 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, - &dc.in_audio_format, data, length, - &dc.out_audio_format, &length, + &dc->in_audio_format, data, length, + &dc->out_audio_format, &length, &error); if (data == NULL) { /* the PCM conversion has failed - stop @@ -288,11 +305,11 @@ decoder_data(struct decoder *decoder, chunk = decoder_get_chunk(decoder, is); if (chunk == NULL) { - assert(dc.command != DECODE_COMMAND_NONE); - return dc.command; + assert(dc->command != DECODE_COMMAND_NONE); + 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); if (dest == NULL) { /* the chunk is full, flush it */ @@ -314,13 +331,13 @@ decoder_data(struct decoder *decoder, if (replay_gain_mode != REPLAY_GAIN_OFF) replay_gain_apply(replay_gain_info, dest, nbytes, - &dc.out_audio_format); + &dc->out_audio_format); else if (normalizationEnabled) - normalizeData(dest, nbytes, &dc.out_audio_format); + normalizeData(dest, nbytes, &dc->out_audio_format); /* 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) { /* the chunk is full, flush it */ decoder_flush_chunk(decoder); @@ -338,10 +355,11 @@ enum decoder_command decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is, const struct tag *tag) { + const struct decoder_control *dc = decoder->dc; enum decoder_command cmd; - assert(dc.state == DECODE_STATE_DECODE); - assert(dc.pipe != NULL); + assert(dc->state == DECODE_STATE_DECODE); + assert(dc->pipe != NULL); assert(tag != NULL); /* save the tag */ diff --git a/src/decoder_control.c b/src/decoder_control.c index d7f5808fc..10f576c5c 100644 --- a/src/decoder_control.c +++ b/src/decoder_control.c @@ -22,131 +22,134 @@ #include -struct decoder_control dc; - -void dc_init(void) +void +dc_init(struct decoder_control *dc) { - dc.mutex = g_mutex_new(); - dc.cond = g_cond_new(); + dc->thread = NULL; - dc.state = DECODE_STATE_STOP; - dc.command = DECODE_COMMAND_NONE; + dc->mutex = g_mutex_new(); + 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_mutex_free(dc.mutex); + g_cond_free(dc->cond); + g_mutex_free(dc->mutex); } static void -dc_command_wait_locked(void) +dc_command_wait_locked(struct decoder_control *dc) { - while (dc.command != DECODE_COMMAND_NONE) { - decoder_signal(); - player_wait_decoder(); + while (dc->command != DECODE_COMMAND_NONE) { + decoder_signal(dc); + player_wait_decoder(dc); } } void -dc_command_wait(void) +dc_command_wait(struct decoder_control *dc) { - decoder_lock(); - dc_command_wait_locked(); - decoder_unlock(); + decoder_lock(dc); + dc_command_wait_locked(dc); + decoder_unlock(dc); } 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_wait_locked(); + dc->command = cmd; + dc_command_wait_locked(dc); } static void -dc_command(enum decoder_command cmd) +dc_command(struct decoder_control *dc, enum decoder_command cmd) { - decoder_lock(); - dc_command_locked(cmd); - decoder_unlock(); + decoder_lock(dc); + dc_command_locked(dc, cmd); + 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; - decoder_signal(); + dc->command = cmd; + decoder_signal(dc); - decoder_unlock(); + decoder_unlock(dc); } 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); - dc.next_song = song; - dc_command(DECODE_COMMAND_START); + dc->next_song = song; + dc_command(dc, DECODE_COMMAND_START); } 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); - dc.next_song = song; - dc_command_async(DECODE_COMMAND_START); + dc->next_song = song; + dc_command_async(dc, DECODE_COMMAND_START); } 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 late and the decoder thread is already executing the old command, we'll call STOP again in this 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) - dc_command_locked(DECODE_COMMAND_STOP); + if (dc->state != DECODE_STATE_STOP && dc->state != DECODE_STATE_ERROR) + dc_command_locked(dc, DECODE_COMMAND_STOP); - decoder_unlock(); + decoder_unlock(dc); } 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); - if (dc.state == DECODE_STATE_STOP || - dc.state == DECODE_STATE_ERROR || !dc.seekable) + if (dc->state == DECODE_STATE_STOP || + dc->state == DECODE_STATE_ERROR || !dc->seekable) return false; - dc.seek_where = where; - dc.seek_error = false; - dc_command(DECODE_COMMAND_SEEK); + dc->seek_where = where; + dc->seek_error = false; + dc_command(dc, DECODE_COMMAND_SEEK); - if (dc.seek_error) + if (dc->seek_error) return false; return true; } void -dc_quit(void) +dc_quit(struct decoder_control *dc) { - assert(dc.thread != NULL); + assert(dc->thread != NULL); - dc.quit = true; - dc_command_async(DECODE_COMMAND_STOP); + dc->quit = true; + dc_command_async(dc, DECODE_COMMAND_STOP); - g_thread_join(dc.thread); - dc.thread = NULL; + g_thread_join(dc->thread); + dc->thread = NULL; } diff --git a/src/decoder_control.h b/src/decoder_control.h index cab6c69c7..d03c7a805 100644 --- a/src/decoder_control.h +++ b/src/decoder_control.h @@ -83,28 +83,28 @@ struct decoder_control { struct music_pipe *pipe; }; -extern struct decoder_control dc; +void +dc_init(struct decoder_control *dc); -void dc_init(void); - -void dc_deinit(void); +void +dc_deinit(struct decoder_control *dc); /** * Locks the #decoder_control object. */ 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. */ 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. */ 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. */ 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 || - dc.state == DECODE_STATE_ERROR) && - dc.command != DECODE_COMMAND_START; + return (dc->state == DECODE_STATE_STOP || + dc->state == DECODE_STATE_ERROR) && + 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 || - dc.state == DECODE_STATE_START; + return dc->command == DECODE_COMMAND_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; - decoder_lock(); - ret = decoder_is_idle(); - decoder_unlock(); + decoder_lock(dc); + ret = decoder_is_idle(dc); + decoder_unlock(dc); return ret; } -static inline bool decoder_lock_is_starting(void) +static inline bool +decoder_lock_is_starting(struct decoder_control *dc) { bool ret; - decoder_lock(); - ret = decoder_is_starting(); - decoder_unlock(); + decoder_lock(dc); + ret = decoder_is_starting(dc); + decoder_unlock(dc); return ret; } -static inline bool decoder_lock_has_failed(void) +static inline bool +decoder_lock_has_failed(struct decoder_control *dc) { bool ret; - decoder_lock(); - ret = decoder_has_failed(); - decoder_unlock(); + decoder_lock(dc); + ret = decoder_has_failed(dc); + decoder_unlock(dc); return ret; } -static inline struct song * -decoder_current_song(void) +static inline const struct song * +decoder_current_song(const struct decoder_control *dc) { - switch (dc.state) { + switch (dc->state) { case DECODE_STATE_STOP: case DECODE_STATE_ERROR: return NULL; case DECODE_STATE_START: case DECODE_STATE_DECODE: - return dc.current_song; + return dc->current_song; } assert(false); @@ -200,21 +206,21 @@ decoder_current_song(void) } void -dc_command_wait(void); +dc_command_wait(struct decoder_control *dc); void -dc_start(struct song *song); +dc_start(struct decoder_control *dc, struct song *song); void -dc_start_async(struct song *song); +dc_start_async(struct decoder_control *dc, struct song *song); void -dc_stop(void); +dc_stop(struct decoder_control *dc); bool -dc_seek(double where); +dc_seek(struct decoder_control *dc, double where); void -dc_quit(void); +dc_quit(struct decoder_control *dc); #endif diff --git a/src/decoder_internal.c b/src/decoder_internal.c index db90333b4..d40ef89d7 100644 --- a/src/decoder_internal.c +++ b/src/decoder_internal.c @@ -34,13 +34,13 @@ * potentially blocking operation. */ static int -decoder_input_buffer(struct input_stream *is) +decoder_input_buffer(struct decoder_control *dc, struct input_stream *is) { int ret; - decoder_unlock(); + decoder_unlock(dc); ret = input_stream_buffer(is) > 0; - decoder_lock(); + decoder_lock(dc); return ret; } @@ -50,17 +50,17 @@ decoder_input_buffer(struct input_stream *is) * one. */ 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 || - dc.command == DECODE_COMMAND_SEEK) - return dc.command; + if (dc->command == DECODE_COMMAND_STOP || + dc->command == DECODE_COMMAND_SEEK) + return dc->command; - if ((is == NULL || decoder_input_buffer(is) <= 0) && do_wait) { - decoder_wait(); + if ((is == NULL || decoder_input_buffer(dc, is) <= 0) && do_wait) { + decoder_wait(dc); player_signal(); - return dc.command; + return dc->command; } return DECODE_COMMAND_NONE; @@ -69,6 +69,7 @@ need_chunks(struct input_stream *is, bool do_wait) struct music_chunk * decoder_get_chunk(struct decoder *decoder, struct input_stream *is) { + struct decoder_control *dc = decoder->dc; enum decoder_command cmd; assert(decoder != NULL); @@ -77,13 +78,13 @@ decoder_get_chunk(struct decoder *decoder, struct input_stream *is) return decoder->chunk; do { - decoder->chunk = music_buffer_allocate(dc.buffer); + decoder->chunk = music_buffer_allocate(dc->buffer); if (decoder->chunk != NULL) return decoder->chunk; - decoder_lock(); - cmd = need_chunks(is, true); - decoder_unlock(); + decoder_lock(dc); + cmd = need_chunks(dc, is, true); + decoder_unlock(dc); } while (cmd == DECODE_COMMAND_NONE); return NULL; @@ -92,13 +93,15 @@ decoder_get_chunk(struct decoder *decoder, struct input_stream *is) void decoder_flush_chunk(struct decoder *decoder) { + struct decoder_control *dc = decoder->dc; + assert(decoder != NULL); assert(decoder->chunk != NULL); if (music_chunk_is_empty(decoder->chunk)) - music_buffer_return(dc.buffer, decoder->chunk); + music_buffer_return(dc->buffer, decoder->chunk); else - music_pipe_push(dc.pipe, decoder->chunk); + music_pipe_push(dc->pipe, decoder->chunk); decoder->chunk = NULL; } diff --git a/src/decoder_internal.h b/src/decoder_internal.h index cf54dbf6d..9d422d253 100644 --- a/src/decoder_internal.h +++ b/src/decoder_internal.h @@ -26,6 +26,8 @@ struct input_stream; struct decoder { + struct decoder_control *dc; + struct pcm_convert_state conv_state; bool seeking; diff --git a/src/decoder_thread.c b/src/decoder_thread.c index 712e4d20c..7308795c3 100644 --- a/src/decoder_thread.c +++ b/src/decoder_thread.c @@ -47,21 +47,21 @@ decoder_stream_decode(const struct decoder_plugin *plugin, assert(decoder->decoder_tag == NULL); assert(input_stream != NULL); 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 */ input_stream_seek(input_stream, 0, SEEK_SET); decoder_plugin_stream_decode(plugin, decoder, input_stream); - decoder_lock(); + decoder_lock(decoder->dc); - assert(dc.state == DECODE_STATE_START || - dc.state == DECODE_STATE_DECODE); + assert(decoder->dc->state == DECODE_STATE_START || + decoder->dc->state == DECODE_STATE_DECODE); - return dc.state != DECODE_STATE_START; + return decoder->dc->state != DECODE_STATE_START; } static bool @@ -75,30 +75,34 @@ decoder_file_decode(const struct decoder_plugin *plugin, assert(decoder->decoder_tag == NULL); assert(path != NULL); 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_lock(); + decoder_lock(decoder->dc); - assert(dc.state == DECODE_STATE_START || - dc.state == DECODE_STATE_DECODE); + assert(decoder->dc->state == DECODE_STATE_START || + 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; bool close_instream = true; struct input_stream input_stream; const struct decoder_plugin *plugin; if (!input_stream_open(&input_stream, uri)) { - dc.state = DECODE_STATE_ERROR; + dc->state = DECODE_STATE_ERROR; return; } @@ -109,8 +113,8 @@ static void decoder_run_song(const struct song *song, const char *uri) decoder.decoder_tag = NULL; decoder.chunk = NULL; - dc.state = DECODE_STATE_START; - dc.command = DECODE_COMMAND_NONE; + dc->state = DECODE_STATE_START; + dc->command = DECODE_COMMAND_NONE; player_signal(); @@ -118,32 +122,32 @@ static void decoder_run_song(const struct song *song, const char *uri) will be available then */ while (!input_stream.ready) { - if (dc.command == DECODE_COMMAND_STOP) { - decoder_unlock(); + if (dc->command == DECODE_COMMAND_STOP) { + decoder_unlock(dc); input_stream_close(&input_stream); - decoder_lock(); - dc.state = DECODE_STATE_STOP; + decoder_lock(dc); + dc->state = DECODE_STATE_STOP; return; } - decoder_unlock(); + decoder_unlock(dc); ret = input_stream_buffer(&input_stream); if (ret < 0) { input_stream_close(&input_stream); - decoder_lock(); - dc.state = DECODE_STATE_ERROR; + decoder_lock(dc); + dc->state = DECODE_STATE_ERROR; return; } - decoder_lock(); + decoder_lock(dc); } - if (dc.command == DECODE_COMMAND_STOP) { - decoder_unlock(); + if (dc->command == DECODE_COMMAND_STOP) { + decoder_unlock(dc); input_stream_close(&input_stream); - decoder_lock(); + decoder_lock(dc); - dc.state = DECODE_STATE_STOP; + dc->state = DECODE_STATE_STOP; return; } @@ -162,7 +166,7 @@ static void decoder_run_song(const struct song *song, const char *uri) if (ret) break; - assert(dc.state == DECODE_STATE_START); + assert(dc->state == DECODE_STATE_START); } /* 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) break; - assert(dc.state == DECODE_STATE_START); + assert(dc->state == DECODE_STATE_START); } } /* 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); while ((plugin = decoder_plugin_from_suffix(s, next++))) { if (plugin->file_decode != NULL) { - decoder_unlock(); + decoder_unlock(dc); input_stream_close(&input_stream); - decoder_lock(); + decoder_lock(dc); close_instream = false; ret = decoder_file_decode(plugin, @@ -213,9 +217,9 @@ static void decoder_run_song(const struct song *song, const char *uri) reopen it */ bool success; - decoder_unlock(); + decoder_unlock(dc); success = input_stream_open(&input_stream, uri); - decoder_lock(); + decoder_lock(dc); if (success) 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); @@ -251,14 +255,15 @@ static void decoder_run_song(const struct song *song, const char *uri) if (decoder.decoder_tag != NULL) 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; if (song_is_file(song)) @@ -267,58 +272,62 @@ static void decoder_run(void) uri = song_get_uri(song); if (uri == NULL) { - dc.state = DECODE_STATE_ERROR; + dc->state = DECODE_STATE_ERROR; return; } - dc.current_song = dc.next_song; /* NEED LOCK */ - decoder_run_song(song, uri); + dc->current_song = dc->next_song; /* NEED LOCK */ + decoder_run_song(dc, song, 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 { - assert(dc.state == DECODE_STATE_STOP || - dc.state == DECODE_STATE_ERROR); + assert(dc->state == DECODE_STATE_STOP || + dc->state == DECODE_STATE_ERROR); - switch (dc.command) { + switch (dc->command) { case DECODE_COMMAND_START: case DECODE_COMMAND_SEEK: - decoder_run(); + decoder_run(dc); - dc.command = DECODE_COMMAND_NONE; + dc->command = DECODE_COMMAND_NONE; player_signal(); break; case DECODE_COMMAND_STOP: - dc.command = DECODE_COMMAND_NONE; + dc->command = DECODE_COMMAND_NONE; player_signal(); break; case DECODE_COMMAND_NONE: - decoder_wait(); + decoder_wait(dc); break; } - } while (dc.command != DECODE_COMMAND_NONE || !dc.quit); + } while (dc->command != DECODE_COMMAND_NONE || !dc->quit); - decoder_unlock(); + decoder_unlock(dc); return NULL; } -void decoder_thread_start(void) +void +decoder_thread_start(struct decoder_control *dc) { GError *e = NULL; - assert(dc.thread == NULL); + assert(dc->thread == NULL); - dc.thread = g_thread_create(decoder_task, NULL, true, &e); - if (dc.thread == NULL) + dc->thread = g_thread_create(decoder_task, dc, true, &e); + if (dc->thread == NULL) g_error("Failed to spawn decoder task: %s", e->message); } diff --git a/src/decoder_thread.h b/src/decoder_thread.h index 50ed7116e..a25564b87 100644 --- a/src/decoder_thread.h +++ b/src/decoder_thread.h @@ -20,6 +20,9 @@ #ifndef 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 diff --git a/src/main.c b/src/main.c index 18647eb80..70166fefb 100644 --- a/src/main.c +++ b/src/main.c @@ -33,7 +33,6 @@ #include "path.h" #include "mapper.h" #include "chunk.h" -#include "decoder_control.h" #include "player_control.h" #include "stats.h" #include "sig_handlers.h" @@ -254,7 +253,6 @@ initialize_decoder_and_player(void) buffered_before_play = buffered_chunks; pc_init(buffered_chunks, buffered_before_play); - dc_init(); } /** @@ -426,7 +424,6 @@ int main(int argc, char *argv[]) mapper_finish(); path_global_finish(); finishPermissions(); - dc_deinit(); pc_deinit(); command_finish(); update_global_finish(); diff --git a/src/player_control.c b/src/player_control.c index aa018ceda..ab162ef26 100644 --- a/src/player_control.c +++ b/src/player_control.c @@ -53,11 +53,11 @@ void pc_deinit(void) } void -player_wait_decoder(void) +player_wait_decoder(struct decoder_control *dc) { /* during this function, the decoder lock is held, because we're waiting for the decoder thread */ - g_cond_wait(pc.cond, dc.mutex); + g_cond_wait(pc.cond, dc->mutex); } void diff --git a/src/player_control.h b/src/player_control.h index 449e98aa5..c1fd52d81 100644 --- a/src/player_control.h +++ b/src/player_control.h @@ -25,6 +25,8 @@ #include +struct decoder_control; + enum player_state { PLAYER_STATE_STOP = 0, PLAYER_STATE_PAUSE, @@ -155,7 +157,7 @@ player_wait(void) * Note the small difference to the player_wait() function! */ void -player_wait_decoder(void); +player_wait_decoder(struct decoder_control *dc); /** * Signals the #player_control object. The object should be locked diff --git a/src/player_thread.c b/src/player_thread.c index 3e822e280..8f8466fc8 100644 --- a/src/player_thread.c +++ b/src/player_thread.c @@ -46,6 +46,8 @@ enum xfade_state { }; struct player { + struct decoder_control *dc; + struct music_pipe *pipe; /** @@ -129,17 +131,19 @@ static void player_command_finished(void) static void 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 */ - music_pipe_clear(dc.pipe, player_buffer); + music_pipe_clear(dc->pipe, player_buffer); - if (dc.pipe != player->pipe) - music_pipe_free(dc.pipe); + if (dc->pipe != player->pipe) + music_pipe_free(dc->pipe); - dc.pipe = NULL; + dc->pipe = NULL; } } @@ -153,13 +157,15 @@ player_dc_stop(struct player *player) static bool player_wait_for_decoder(struct player *player) { - dc_command_wait(); + struct decoder_control *dc = player->dc; - if (decoder_lock_has_failed()) { - assert(dc.next_song == NULL || dc.next_song->uri != NULL); + dc_command_wait(dc); + + if (decoder_lock_has_failed(dc)) { + assert(dc->next_song == NULL || dc->next_song->uri != NULL); player_lock(); - pc.errored_song = dc.next_song; + pc.errored_song = dc->next_song; pc.error = PLAYER_ERROR_FILE; pc.next_song = NULL; player_unlock(); @@ -198,24 +204,26 @@ player_wait_for_decoder(struct player *player) static bool player_check_decoder_startup(struct player *player) { + struct decoder_control *dc = player->dc; + assert(player->decoder_starting); - decoder_lock(); + decoder_lock(dc); - if (decoder_has_failed()) { + if (decoder_has_failed(dc)) { /* 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; return false; - } else if (!decoder_is_starting()) { + } else if (!decoder_is_starting(dc)) { /* the decoder is ready and ok */ - decoder_unlock(); + decoder_unlock(dc); if (audio_format_defined(&player->play_audio_format) && !audio_output_all_wait(1)) @@ -223,21 +231,21 @@ player_check_decoder_startup(struct player *player) all chunks yet - wait for that */ return true; - pc.total_time = dc.total_time; - pc.audio_format = dc.in_audio_format; - player->play_audio_format = dc.out_audio_format; + pc.total_time = dc->total_time; + pc.audio_format = dc->in_audio_format; + player->play_audio_format = dc->out_audio_format; player->decoder_starting = false; if (!player->paused && - !audio_output_all_open(&dc.out_audio_format, + !audio_output_all_open(&dc->out_audio_format, player_buffer)) { - char *uri = song_get_uri(dc.next_song); + char *uri = song_get_uri(dc->next_song); g_warning("problems opening audio device " "while playing \"%s\"", uri); g_free(uri); - assert(dc.next_song == NULL || dc.next_song->uri != NULL); - pc.errored_song = dc.next_song; + assert(dc->next_song == NULL || dc->next_song->uri != NULL); + pc.errored_song = dc->next_song; pc.error = PLAYER_ERROR_AUDIO; /* pause: the user may resume playback as soon @@ -251,8 +259,8 @@ player_check_decoder_startup(struct player *player) } else { /* the decoder is not yet ready; wait some more */ - player_wait_decoder(); - decoder_unlock(); + player_wait_decoder(dc); + decoder_unlock(dc); return true; } @@ -305,12 +313,13 @@ player_send_silence(struct player *player) */ static bool player_seek_decoder(struct player *player) { + struct decoder_control *dc = player->dc; double where; bool ret; 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 - 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 pipe */ music_pipe_clear(player->pipe, player_buffer); - dc.pipe = player->pipe; + dc->pipe = player->pipe; /* re-start the decoder */ - dc_start_async(pc.next_song); + dc_start_async(dc, pc.next_song); ret = player_wait_for_decoder(player); if (!ret) { /* decoder failure */ @@ -353,7 +362,7 @@ static bool player_seek_decoder(struct player *player) if (where < 0.0) where = 0.0; - ret = dc_seek(where); + ret = dc_seek(dc, where); if (!ret) { /* decoder failure */ player_command_finished(); @@ -379,6 +388,8 @@ static bool player_seek_decoder(struct player *player) */ static void player_process_command(struct player *player) { + struct decoder_control *dc = player->dc; + switch (pc.command) { case PLAYER_COMMAND_NONE: case PLAYER_COMMAND_STOP: @@ -396,7 +407,7 @@ static void player_process_command(struct player *player) case PLAYER_COMMAND_QUEUE: assert(pc.next_song != NULL); assert(!player->queued); - assert(dc.pipe == NULL || dc.pipe == player->pipe); + assert(dc->pipe == NULL || dc->pipe == player->pipe); player->queued = true; player_command_finished_locked(); @@ -425,8 +436,8 @@ static void player_process_command(struct player *player) } else { /* the audio device has failed - rollback to pause mode */ - assert(dc.next_song == NULL || dc.next_song->uri != NULL); - pc.errored_song = dc.next_song; + assert(dc->next_song == NULL || dc->next_song->uri != NULL); + pc.errored_song = dc->next_song; pc.error = PLAYER_ERROR_AUDIO; player->paused = true; @@ -452,7 +463,7 @@ static void player_process_command(struct player *player) return; } - if (dc.pipe != NULL && dc.pipe != player->pipe) { + if (dc->pipe != NULL && dc->pipe != player->pipe) { /* the decoder is already decoding the song - stop it and reset the position */ player_unlock(); @@ -549,6 +560,7 @@ play_chunk(struct song *song, struct music_chunk *chunk, static bool play_next_chunk(struct player *player) { + struct decoder_control *dc = player->dc; struct music_chunk *chunk = NULL; unsigned cross_fade_position; bool success; @@ -559,12 +571,12 @@ play_next_chunk(struct player *player) return true; 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)) <= player->cross_fade_chunks) { /* perform cross fade */ struct music_chunk *other_chunk = - music_pipe_shift(dc.pipe); + music_pipe_shift(dc->pipe); if (!player->cross_fading) { /* beginning of the cross fade - adjust @@ -580,26 +592,26 @@ play_next_chunk(struct player *player) assert(chunk != NULL); cross_fade_apply(chunk, other_chunk, - &dc.out_audio_format, + &dc->out_audio_format, cross_fade_position, player->cross_fade_chunks); music_buffer_return(player_buffer, other_chunk); } else { /* 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 cross fading */ - decoder_unlock(); + decoder_unlock(dc); player->xfade = XFADE_DISABLED; } else { /* wait for the decoder */ - decoder_signal(); - player_wait_decoder(); - decoder_unlock(); + decoder_signal(dc); + player_wait_decoder(dc); + decoder_unlock(dc); return true; } @@ -635,12 +647,12 @@ play_next_chunk(struct player *player) /* this formula should prevent that the decoder gets woken up with each chunk; it is more efficient to make it decode a larger block at a time */ - decoder_lock(); - if (!decoder_is_idle() && - music_pipe_size(dc.pipe) <= (pc.buffered_before_play + + decoder_lock(dc); + if (!decoder_is_idle(dc) && + music_pipe_size(dc->pipe) <= (pc.buffered_before_play + music_buffer_size(player_buffer) * 3) / 4) - decoder_signal(); - decoder_unlock(); + decoder_signal(dc); + decoder_unlock(dc); return true; } @@ -666,7 +678,7 @@ player_song_border(struct player *player) g_free(uri); music_pipe_free(player->pipe); - player->pipe = dc.pipe; + player->pipe = player->dc->pipe; if (!player_wait_for_decoder(player)) return false; @@ -679,9 +691,10 @@ player_song_border(struct player *player) * basically a state machine, which multiplexes data between the * decoder thread and the output threads. */ -static void do_play(void) +static void do_play(struct decoder_control *dc) { struct player player = { + .dc = dc, .buffering = true, .decoder_starting = false, .paused = false, @@ -697,9 +710,9 @@ static void do_play(void) player.pipe = music_pipe_new(); - dc.buffer = player_buffer; - dc.pipe = player.pipe; - dc_start(pc.next_song); + dc->buffer = player_buffer; + dc->pipe = player.pipe; + dc_start(dc, pc.next_song); if (!player_wait_for_decoder(&player)) { player_dc_stop(&player); player_command_finished(); @@ -731,7 +744,7 @@ static void do_play(void) prevent stuttering on slow machines */ 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 */ if (!player.paused && @@ -740,10 +753,10 @@ static void do_play(void) !player_send_silence(&player)) break; - decoder_lock(); + decoder_lock(dc); /* XXX race condition: check decoder again */ - player_wait_decoder(); - decoder_unlock(); + player_wait_decoder(dc); + decoder_unlock(dc); player_lock(); continue; } else { @@ -768,30 +781,30 @@ static void do_play(void) /* music_pipe_check_format(&play_audio_format, player.next_song_chunk, - &dc.out_audio_format); + &dc->out_audio_format); */ #endif - if (decoder_lock_is_idle() && player.queued) { + if (decoder_lock_is_idle(dc) && player.queued) { /* the decoder has finished the current song; make it decode the next song */ assert(pc.next_song != NULL); - assert(dc.pipe == NULL || dc.pipe == player.pipe); + assert(dc->pipe == NULL || dc->pipe == player.pipe); player.queued = false; - dc.pipe = music_pipe_new(); - dc_start_async(pc.next_song); + dc->pipe = music_pipe_new(); + 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 && - !decoder_lock_is_starting()) { + !decoder_lock_is_starting(dc)) { /* enable cross fading in this song? if yes, calculate how many chunks will be required for it */ player.cross_fade_chunks = - cross_fade_calc(pc.cross_fade_seconds, dc.total_time, - &dc.out_audio_format, + cross_fade_calc(pc.cross_fade_seconds, dc->total_time, + &dc->out_audio_format, &player.play_audio_format, music_buffer_size(player_buffer) - pc.buffered_before_play); @@ -820,12 +833,12 @@ static void do_play(void) /* XXX synchronize in a better way */ 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 */ if (!player_song_border(&player)) break; - } else if (decoder_lock_is_idle()) { + } else if (decoder_lock_is_idle(dc)) { /* check the size of the pipe again, because the decoder thread may have added something since we last checked */ @@ -861,7 +874,10 @@ static void do_play(void) 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); @@ -872,7 +888,7 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg) case PLAYER_COMMAND_QUEUE: assert(pc.next_song != NULL); - do_play(); + do_play(&dc); break; case PLAYER_COMMAND_STOP: @@ -916,7 +932,8 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg) case PLAYER_COMMAND_EXIT: player_unlock(); - dc_quit(); + dc_quit(&dc); + dc_deinit(&dc); audio_output_all_close(); music_buffer_free(player_buffer);