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:
parent
806496dfc9
commit
6ef428af2e
|
@ -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 */
|
||||
|
|
|
@ -22,131 +22,134 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
struct input_stream;
|
||||
|
||||
struct decoder {
|
||||
struct decoder_control *dc;
|
||||
|
||||
struct pcm_convert_state conv_state;
|
||||
|
||||
bool seeking;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue