From 1b3144727158b18f080732ff35ef1f6a123a946d Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Tue, 23 Apr 2013 09:20:10 +0200 Subject: music chunk: switch to AVFrame. --- src/Makefile | 1 - src/buffer.c | 137 ------------------------------------------------- src/buffer.h | 67 ------------------------ src/chunk.c | 78 +++++++++------------------- src/chunk.h | 45 +++------------- src/decoder_api.c | 106 +++++++++----------------------------- src/decoder_control.c | 10 ++-- src/decoder_control.h | 11 ++-- src/decoder_internal.c | 58 ++++++++------------- src/decoder_internal.h | 9 ---- src/decoder_thread.c | 6 --- src/output_all.c | 40 +++------------ src/output_all.h | 17 +++--- src/output_internal.h | 7 ++- src/output_thread.c | 10 ++-- src/pipe.c | 26 +++++----- src/pipe.h | 18 ++----- src/player_thread.c | 94 +++++++++++++++++---------------- 18 files changed, 170 insertions(+), 570 deletions(-) delete mode 100644 src/buffer.c delete mode 100644 src/buffer.h diff --git a/src/Makefile b/src/Makefile index d62f0b9b..61b8fc6b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,7 +7,6 @@ OBJS = aiff.o \ audio_config.o \ audio_format.o \ audio_parser.o \ - buffer.o \ chunk.o \ client.o \ client_event.o \ diff --git a/src/buffer.c b/src/buffer.c deleted file mode 100644 index 559f39a9..00000000 --- a/src/buffer.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include "buffer.h" -#include "chunk.h" -#include "poison.h" - -#include - -#include - -struct music_buffer { - struct music_chunk *chunks; - unsigned num_chunks; - - struct music_chunk *available; - - /** a mutex which protects #available */ - GMutex *mutex; - -#ifndef NDEBUG - unsigned num_allocated; -#endif -}; - -struct music_buffer * -music_buffer_new(unsigned num_chunks) -{ - struct music_buffer *buffer; - struct music_chunk *chunk; - - assert(num_chunks > 0); - - buffer = g_new(struct music_buffer, 1); - - buffer->chunks = g_new(struct music_chunk, num_chunks); - buffer->num_chunks = num_chunks; - - chunk = buffer->available = buffer->chunks; - poison_undefined(chunk, sizeof(*chunk)); - - for (unsigned i = 1; i < num_chunks; ++i) { - chunk->next = &buffer->chunks[i]; - chunk = chunk->next; - poison_undefined(chunk, sizeof(*chunk)); - } - - chunk->next = NULL; - - buffer->mutex = g_mutex_new(); - -#ifndef NDEBUG - buffer->num_allocated = 0; -#endif - - return buffer; -} - -void -music_buffer_free(struct music_buffer *buffer) -{ - assert(buffer->chunks != NULL); - assert(buffer->num_chunks > 0); - assert(buffer->num_allocated == 0); - - g_mutex_free(buffer->mutex); - g_free(buffer->chunks); - g_free(buffer); -} - -unsigned -music_buffer_size(const struct music_buffer *buffer) -{ - return buffer->num_chunks; -} - -struct music_chunk * -music_buffer_allocate(struct music_buffer *buffer) -{ - struct music_chunk *chunk; - - g_mutex_lock(buffer->mutex); - - chunk = buffer->available; - if (chunk != NULL) { - buffer->available = chunk->next; - music_chunk_init(chunk); - -#ifndef NDEBUG - ++buffer->num_allocated; -#endif - } - - g_mutex_unlock(buffer->mutex); - return chunk; -} - -void -music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk) -{ - assert(buffer != NULL); - assert(chunk != NULL); - - if (chunk->other != NULL) - music_buffer_return(buffer, chunk->other); - - g_mutex_lock(buffer->mutex); - - music_chunk_free(chunk); - poison_undefined(chunk, sizeof(*chunk)); - - chunk->next = buffer->available; - buffer->available = chunk; - -#ifndef NDEBUG - --buffer->num_allocated; -#endif - - g_mutex_unlock(buffer->mutex); -} diff --git a/src/buffer.h b/src/buffer.h deleted file mode 100644 index f860231e..00000000 --- a/src/buffer.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2003-2011 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_MUSIC_BUFFER_H -#define MPD_MUSIC_BUFFER_H - -/** - * An allocator for #music_chunk objects. - */ -struct music_buffer; - -/** - * Creates a new #music_buffer object. - * - * @param num_chunks the number of #music_chunk reserved in this - * buffer - */ -struct music_buffer * -music_buffer_new(unsigned num_chunks); - -/** - * Frees the #music_buffer object - */ -void -music_buffer_free(struct music_buffer *buffer); - -/** - * Returns the total number of reserved chunks in this buffer. This - * is the same value which was passed to the constructor - * music_buffer_new(). - */ -unsigned -music_buffer_size(const struct music_buffer *buffer); - -/** - * Allocates a chunk from the buffer. When it is not used anymore, - * call music_buffer_return(). - * - * @return an empty chunk or NULL if there are no chunks available - */ -struct music_chunk * -music_buffer_allocate(struct music_buffer *buffer); - -/** - * Returns a chunk to the buffer. It can be reused by - * music_buffer_allocate() then. - */ -void -music_buffer_return(struct music_buffer *buffer, struct music_chunk *chunk); - -#endif diff --git a/src/chunk.c b/src/chunk.c index 1eb96f4b..a7f11c3f 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -22,81 +22,53 @@ #include "audio_format.h" #include "tag.h" +#include +#include + #include void music_chunk_init(struct music_chunk *chunk) { chunk->other = NULL; - chunk->length = 0; + chunk->frame = NULL; chunk->tag = NULL; chunk->replay_gain_serial = 0; } -void -music_chunk_free(struct music_chunk *chunk) +struct music_chunk *music_chunk_alloc(void) { - if (chunk->tag != NULL) - tag_free(chunk->tag); -} + struct music_chunk *ret = av_mallocz(sizeof(*ret)); -#ifndef NDEBUG -bool -music_chunk_check_format(const struct music_chunk *chunk, - const struct audio_format *audio_format) -{ - assert(chunk != NULL); - assert(audio_format != NULL); - assert(audio_format_valid(audio_format)); + if (!ret) + return NULL; - return chunk->length == 0 || - audio_format_equals(&chunk->audio_format, audio_format); + music_chunk_init(ret); + return ret; } -#endif -void * -music_chunk_write(struct music_chunk *chunk, - const struct audio_format *audio_format, - float data_time, uint16_t bit_rate, - size_t *max_length_r) +void music_chunk_free(struct music_chunk *chunk) { - const size_t frame_size = audio_format_frame_size(audio_format); - size_t num_frames; - - assert(music_chunk_check_format(chunk, audio_format)); - assert(chunk->length == 0 || audio_format_valid(&chunk->audio_format)); + if (!chunk) + return; - if (chunk->length == 0) { - /* if the chunk is empty, nobody has set bitRate and - times yet */ + if (chunk->tag != NULL) + tag_free(chunk->tag); - chunk->bit_rate = bit_rate; - chunk->times = data_time; - } - - num_frames = (sizeof(chunk->data) - chunk->length) / frame_size; - if (num_frames == 0) - return NULL; - -#ifndef NDEBUG - chunk->audio_format = *audio_format; -#endif - - *max_length_r = num_frames * frame_size; - return chunk->data + chunk->length; + av_frame_free(&chunk->frame); + av_freep(&chunk); } +#ifndef NDEBUG bool -music_chunk_expand(struct music_chunk *chunk, - const struct audio_format *audio_format, size_t length) +music_chunk_check_format(const struct music_chunk *chunk, + const struct audio_format *audio_format) { - const size_t frame_size = audio_format_frame_size(audio_format); - assert(chunk != NULL); - assert(chunk->length + length <= sizeof(chunk->data)); - assert(audio_format_equals(&chunk->audio_format, audio_format)); - - chunk->length += length; + assert(audio_format != NULL); + assert(audio_format_valid(audio_format)); - return chunk->length + frame_size > sizeof(chunk->data); + return !chunk->frame || + audio_format_equals(&chunk->audio_format, audio_format); } +#endif diff --git a/src/chunk.h b/src/chunk.h index a06a203e..c92e1fd1 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -30,6 +30,8 @@ #include #include +#include + enum { CHUNK_SIZE = 4096, }; @@ -56,9 +58,6 @@ struct music_chunk { */ float mix_ratio; - /** number of bytes stored in this chunk */ - uint16_t length; - /** current bit rate of the source file */ uint16_t bit_rate; @@ -86,8 +85,8 @@ struct music_chunk { */ unsigned replay_gain_serial; - /** the data (probably PCM) */ - char data[CHUNK_SIZE]; + /** the audio frame */ + AVFrame *frame; #ifndef NDEBUG struct audio_format audio_format; @@ -97,13 +96,15 @@ struct music_chunk { void music_chunk_init(struct music_chunk *chunk); +struct music_chunk *music_chunk_alloc(void); + void music_chunk_free(struct music_chunk *chunk); static inline bool music_chunk_is_empty(const struct music_chunk *chunk) { - return chunk->length == 0 && chunk->tag == NULL; + return !chunk->frame && chunk->tag == NULL; } #ifndef NDEBUG @@ -116,36 +117,4 @@ music_chunk_check_format(const struct music_chunk *chunk, const struct audio_format *audio_format); #endif -/** - * Prepares appending to the music chunk. Returns a buffer where you - * may write into. After you are finished, call music_chunk_expand(). - * - * @param chunk the music_chunk object - * @param audio_format the audio format for the appended data; must - * stay the same for the life cycle of this chunk - * @param data_time the time within the song - * @param bit_rate the current bit rate of the source file - * @param max_length_r the maximum write length is returned here - * @return a writable buffer, or NULL if the chunk is full - */ -void * -music_chunk_write(struct music_chunk *chunk, - const struct audio_format *audio_format, - float data_time, uint16_t bit_rate, - size_t *max_length_r); - -/** - * Increases the length of the chunk after the caller has written to - * the buffer returned by music_chunk_write(). - * - * @param chunk the music_chunk object - * @param audio_format the audio format for the appended data; must - * stay the same for the life cycle of this chunk - * @param length the number of bytes which were appended - * @return true if the chunk is full - */ -bool -music_chunk_expand(struct music_chunk *chunk, - const struct audio_format *audio_format, size_t length); - #endif diff --git a/src/decoder_api.c b/src/decoder_api.c index bcab6010..35683d95 100644 --- a/src/decoder_api.c +++ b/src/decoder_api.c @@ -159,7 +159,6 @@ void decoder_command_finished(struct decoder *decoder) if (decoder->initial_seek_running) { assert(!decoder->seeking); - assert(decoder->chunk == NULL); assert(music_pipe_empty(dc->pipe)); decoder->initial_seek_running = false; @@ -171,14 +170,7 @@ void decoder_command_finished(struct decoder *decoder) if (decoder->seeking) { decoder->seeking = false; - /* delete frames from the old song position */ - - if (decoder->chunk != NULL) { - music_buffer_return(dc->buffer, decoder->chunk); - decoder->chunk = NULL; - } - - music_pipe_clear(dc->pipe, dc->buffer); + music_pipe_clear(dc->pipe); decoder->timestamp = dc->seek_where; } @@ -308,15 +300,6 @@ static enum decoder_command do_send_tag(struct decoder *decoder, const struct ta { struct music_chunk *chunk; - if (decoder->chunk != NULL) { - /* there is a partial chunk - flush it, we want the - tag in a new chunk */ - decoder_flush_chunk(decoder); - g_cond_signal(decoder->dc->client_cond); - } - - assert(decoder->chunk == NULL); - chunk = decoder_get_chunk(decoder); if (chunk == NULL) { assert(decoder->dc->command != DECODE_COMMAND_NONE); @@ -356,8 +339,8 @@ enum decoder_command decoder_data(struct decoder *decoder, { struct decoder_control *dc = decoder->dc; enum decoder_command cmd = DECODE_COMMAND_NONE; - uint8_t *data; - int length; + int nb_samples = frame->nb_samples; + struct music_chunk *chunk; assert(dc->state == DECODE_STATE_DECODE); assert(dc->pipe != NULL); @@ -404,69 +387,36 @@ enum decoder_command decoder_data(struct decoder *decoder, frame = tmp; } - length = frame->nb_samples * av_get_bytes_per_sample(frame->format) * - dc->out_audio_format.channels; - data = frame->data[0]; - while (length > 0) { - struct music_chunk *chunk; - char *dest; - size_t nbytes; - bool full; - - chunk = decoder_get_chunk(decoder); - if (chunk == NULL) { - assert(dc->command != DECODE_COMMAND_NONE); - cmd = dc->command; - goto finish; - } - - dest = music_chunk_write(chunk, &dc->out_audio_format, - decoder->timestamp - - dc->song->start_ms / 1000.0, - kbit_rate, &nbytes); - if (dest == NULL) { - /* the chunk is full, flush it */ - decoder_flush_chunk(decoder); - g_cond_signal(dc->client_cond); - continue; - } - - assert(nbytes > 0); - - if (nbytes > length) - nbytes = length; - - /* copy the buffer */ + // FIXME support planar formats everywhere + assert(!av_sample_fmt_is_planar(frame->format)); - memcpy(dest, data, nbytes); + chunk = decoder_get_chunk(decoder); + if (!chunk) { + cmd = (dc->command == DECODE_COMMAND_NONE) ? + DECODE_COMMAND_STOP : dc->command; + goto finish; + } + chunk->frame = frame; + chunk->bit_rate = kbit_rate; + chunk->times = decoder->timestamp - dc->song->start_ms / 1000.0; - /* expand the music pipe chunk */ + frame = NULL; - full = music_chunk_expand(chunk, &dc->out_audio_format, nbytes); - if (full) { - /* the chunk is full, flush it */ - decoder_flush_chunk(decoder); - g_cond_signal(dc->client_cond); - } - - data += nbytes; - length -= nbytes; + music_pipe_push(dc->pipe, chunk); + g_cond_signal(dc->client_cond); - decoder->timestamp += (double)nbytes / - audio_format_time_to_size(&dc->out_audio_format); + decoder->timestamp += (double)nb_samples / dc->in_audio_format.sample_rate; - if (dc->end_ms > 0 && - decoder->timestamp >= dc->end_ms / 1000.0) { - /* the end of this range has been reached: - stop decoding */ - cmd = DECODE_COMMAND_STOP; - break; - } + if (dc->end_ms > 0 && + decoder->timestamp >= dc->end_ms / 1000.0) { + /* the end of this range has been reached: + stop decoding */ + cmd = DECODE_COMMAND_STOP; } finish: av_frame_free(&frame); - return DECODE_COMMAND_NONE; + return cmd; } enum decoder_command decoder_tag(struct decoder *decoder, struct input_stream *is, @@ -534,14 +484,6 @@ float decoder_replay_gain(struct decoder *decoder, decoder->replay_gain_info = *replay_gain_info; decoder->replay_gain_serial = serial; - - if (decoder->chunk != NULL) { - /* flush the current chunk because the new - replay gain values affect the following - samples */ - decoder_flush_chunk(decoder); - g_cond_signal(decoder->dc->client_cond); - } } else decoder->replay_gain_serial = 0; diff --git a/src/decoder_control.c b/src/decoder_control.c index 70f34b33..5c952d9d 100644 --- a/src/decoder_control.c +++ b/src/decoder_control.c @@ -94,20 +94,18 @@ dc_command_async(struct decoder_control *dc, enum decoder_command cmd) decoder_unlock(dc); } -void -dc_start(struct decoder_control *dc, struct song *song, - unsigned start_ms, unsigned end_ms, - struct music_buffer *buffer, struct music_pipe *pipe) +void dc_start(struct decoder_control *dc, struct song *song, + unsigned start_ms, unsigned end_ms, + int buffer_samples, struct music_pipe *pipe) { assert(song != NULL); - assert(buffer != NULL); assert(pipe != NULL); assert(music_pipe_empty(pipe)); dc->song = song; dc->start_ms = start_ms; dc->end_ms = end_ms; - dc->buffer = buffer; + dc->buffer_samples = buffer_samples; dc->pipe = pipe; dc_command(dc, DECODE_COMMAND_START); } diff --git a/src/decoder_control.h b/src/decoder_control.h index 566b153e..963e73ea 100644 --- a/src/decoder_control.h +++ b/src/decoder_control.h @@ -104,8 +104,8 @@ struct decoder_control { float total_time; - /** the #music_chunk allocator */ - struct music_buffer *buffer; + /** maximum number of samples to keep buffered */ + int buffer_samples; /** * The destination pipe for decoded chunks. The caller thread @@ -251,10 +251,9 @@ decoder_current_song(const struct decoder_control *dc) * @param pipe the pipe which receives the decoded chunks (owned by * the caller) */ -void -dc_start(struct decoder_control *dc, struct song *song, - unsigned start_ms, unsigned end_ms, - struct music_buffer *buffer, struct music_pipe *pipe); +void dc_start(struct decoder_control *dc, struct song *song, + unsigned start_ms, unsigned end_ms, + int buffer_samples, struct music_pipe *pipe); void dc_stop(struct decoder_control *dc); diff --git a/src/decoder_internal.c b/src/decoder_internal.c index bc349f2f..30089d17 100644 --- a/src/decoder_internal.c +++ b/src/decoder_internal.c @@ -48,49 +48,31 @@ need_chunks(struct decoder_control *dc, bool do_wait) return DECODE_COMMAND_NONE; } -struct music_chunk * -decoder_get_chunk(struct decoder *decoder) +struct music_chunk *decoder_get_chunk(struct decoder *decoder) { - struct decoder_control *dc = decoder->dc; - enum decoder_command cmd; + struct decoder_control *dc = decoder->dc; + struct music_chunk *chunk; + enum decoder_command cmd; - assert(decoder != NULL); + assert(decoder != NULL); - if (decoder->chunk != NULL) - return decoder->chunk; + do { + if (music_pipe_size(dc->pipe) >= dc->buffer_samples) { + chunk = music_chunk_alloc(); + if (!chunk) + return NULL; - do { - decoder->chunk = music_buffer_allocate(dc->buffer); - if (decoder->chunk != NULL) { - decoder->chunk->replay_gain_serial = - decoder->replay_gain_serial; - if (decoder->replay_gain_serial != 0) - decoder->chunk->replay_gain_info = - decoder->replay_gain_info; + chunk->replay_gain_serial = decoder->replay_gain_serial; + if (decoder->replay_gain_serial != 0) + chunk->replay_gain_info = decoder->replay_gain_info; - return decoder->chunk; - } + return chunk; + } - decoder_lock(dc); - cmd = need_chunks(dc, true); - decoder_unlock(dc); - } while (cmd == DECODE_COMMAND_NONE); + decoder_lock(dc); + cmd = need_chunks(dc, true); + decoder_unlock(dc); + } while (cmd == DECODE_COMMAND_NONE); - return NULL; -} - -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); - else - music_pipe_push(dc->pipe, decoder->chunk); - - decoder->chunk = NULL; + return NULL; } diff --git a/src/decoder_internal.h b/src/decoder_internal.h index d89e68cf..1f331aa1 100644 --- a/src/decoder_internal.h +++ b/src/decoder_internal.h @@ -70,9 +70,6 @@ struct decoder { /** the last tag received from the decoder plugin */ struct tag *decoder_tag; - /** the chunk currently being written to */ - struct music_chunk *chunk; - struct replay_gain_info replay_gain_info; /** @@ -91,10 +88,4 @@ struct decoder { struct music_chunk * decoder_get_chunk(struct decoder *decoder); -/** - * Flushes the current chunk. - */ -void -decoder_flush_chunk(struct decoder *decoder); - #endif diff --git a/src/decoder_thread.c b/src/decoder_thread.c index 1440fc27..964f1318 100644 --- a/src/decoder_thread.c +++ b/src/decoder_thread.c @@ -396,7 +396,6 @@ decoder_run_song(struct decoder_control *dc, ? tag_dup(song->tag) : NULL; decoder.stream_tag = NULL; decoder.decoder_tag = NULL; - decoder.chunk = NULL; dc->state = DECODE_STATE_START; @@ -412,11 +411,6 @@ decoder_run_song(struct decoder_control *dc, pcm_convert_deinit(&decoder.conv_state); - /* flush the last chunk */ - - if (decoder.chunk != NULL) - decoder_flush_chunk(&decoder); - if (decoder.song_tag != NULL) tag_free(decoder.song_tag); diff --git a/src/output_all.c b/src/output_all.c index f56cd04e..bf80a159 100644 --- a/src/output_all.c +++ b/src/output_all.c @@ -24,7 +24,6 @@ #include "chunk.h" #include "conf.h" #include "pipe.h" -#include "buffer.h" #include "player_control.h" #include "mpd_error.h" #include "notify.h" @@ -44,11 +43,6 @@ static struct audio_format input_audio_format; static struct audio_output **audio_outputs; static unsigned int num_audio_outputs; -/** - * The #music_buffer object where consumed chunks are returned. - */ -static struct music_buffer *g_music_buffer; - /** * The #music_pipe object which feeds all audio outputs. It is filled * by audio_output_all_play(). @@ -275,7 +269,6 @@ audio_output_all_play(struct music_chunk *chunk) bool ret; unsigned int i; - assert(g_music_buffer != NULL); assert(g_mp != NULL); assert(chunk != NULL); assert(music_chunk_check_format(chunk, &input_audio_format)); @@ -293,18 +286,12 @@ audio_output_all_play(struct music_chunk *chunk) } bool -audio_output_all_open(const struct audio_format *audio_format, - struct music_buffer *buffer) +audio_output_all_open(const struct audio_format *audio_format) { bool ret = false, enabled = false; unsigned int i; assert(audio_format != NULL); - assert(buffer != NULL); - assert(g_music_buffer == NULL || g_music_buffer == buffer); - assert((g_mp == NULL) == (g_music_buffer == NULL)); - - g_music_buffer = buffer; /* the audio format must be the same as existing chunks in the pipe */ @@ -424,7 +411,6 @@ audio_output_all_check(void) struct music_chunk *shifted; bool locked[num_audio_outputs]; - assert(g_music_buffer != NULL); assert(g_mp != NULL); while ((chunk = music_pipe_peek(g_mp)) != NULL) { @@ -435,7 +421,7 @@ audio_output_all_check(void) this chunk */ return music_pipe_size(g_mp); - if (chunk->length > 0 && chunk->times >= 0.0) + if (chunk->frame && chunk->times >= 0.0) /* only update elapsed_time if the chunk provides a defined value */ audio_output_all_elapsed_time = chunk->times; @@ -457,8 +443,7 @@ audio_output_all_check(void) if (locked[i]) g_mutex_unlock(audio_outputs[i]->mutex); - /* return the chunk to the buffer */ - music_buffer_return(g_music_buffer, shifted); + music_chunk_free(shifted); } return 0; @@ -514,10 +499,9 @@ audio_output_all_cancel(void) audio_output_wait_all(); - /* clear the music pipe and return all chunks to the buffer */ - - if (g_mp != NULL) - music_pipe_clear(g_mp, g_music_buffer); + /* clear the music pipe */ + if (g_mp != NULL) + music_pipe_clear(g_mp); /* the audio outputs are now waiting for a signal, to synchronize the cleared music pipe */ @@ -538,15 +522,11 @@ audio_output_all_close(void) audio_output_close(audio_outputs[i]); if (g_mp != NULL) { - assert(g_music_buffer != NULL); - - music_pipe_clear(g_mp, g_music_buffer); + music_pipe_clear(g_mp); music_pipe_free(g_mp); g_mp = NULL; } - g_music_buffer = NULL; - audio_format_clear(&input_audio_format); audio_output_all_elapsed_time = -1.0; @@ -561,15 +541,11 @@ audio_output_all_release(void) audio_output_release(audio_outputs[i]); if (g_mp != NULL) { - assert(g_music_buffer != NULL); - - music_pipe_clear(g_mp, g_music_buffer); + music_pipe_clear(g_mp); music_pipe_free(g_mp); g_mp = NULL; } - g_music_buffer = NULL; - audio_format_clear(&input_audio_format); audio_output_all_elapsed_time = -1.0; diff --git a/src/output_all.h b/src/output_all.h index 4eeb94f1..17b5a9b7 100644 --- a/src/output_all.h +++ b/src/output_all.h @@ -30,7 +30,6 @@ #include struct audio_format; -struct music_buffer; struct music_chunk; struct player_control; @@ -78,13 +77,10 @@ audio_output_all_enable_disable(void); * * @param audio_format the preferred audio format, or NULL to reuse * the previous format - * @param buffer the #music_buffer where consumed #music_chunk objects * should be returned * @return true on success, false on failure */ -bool -audio_output_all_open(const struct audio_format *audio_format, - struct music_buffer *buffer); +bool audio_output_all_open(const struct audio_format *audio_format); /** * Closes all audio outputs. @@ -112,20 +108,19 @@ audio_output_all_play(struct music_chunk *chunk); /** * Checks if the output devices have drained their music pipe, and - * returns the consumed music chunks to the #music_buffer. + * frees the consumed music chunks. * - * @return the number of chunks to play left in the #music_pipe + * @return the number of samples to play left in the #music_pipe */ -unsigned -audio_output_all_check(void); +unsigned audio_output_all_check(void); /** * Checks if the size of the #music_pipe is below the #threshold. If * not, it attempts to synchronize with all output threads, and waits * until another #music_chunk is finished. * - * @param threshold the maximum number of chunks in the pipe - * @return true if there are less than #threshold chunks in the pipe + * @param threshold the maximum number of samples in the pipe + * @return true if there are less than #threshold samples in the pipe */ bool audio_output_all_wait(struct player_control *pc, unsigned threshold); diff --git a/src/output_internal.h b/src/output_internal.h index 9d975d78..3e737127 100644 --- a/src/output_internal.h +++ b/src/output_internal.h @@ -219,10 +219,9 @@ struct audio_output { struct player_control *player_control; /** - * The #music_chunk which is currently being played. All - * chunks before this one may be returned to the - * #music_buffer, because they are not going to be used by - * this output anymore. + * The #music_chunk which is currently being played. All chunks before this + * one may be freed because they are not going to be used by this output + * anymore. */ const struct music_chunk *chunk; diff --git a/src/output_thread.c b/src/output_thread.c index 4eef2ccd..78592d6e 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -325,12 +325,10 @@ ao_chunk_data(struct audio_output *ao, const struct music_chunk *chunk, assert(!music_chunk_is_empty(chunk)); assert(music_chunk_check_format(chunk, &ao->in_audio_format)); - const char *data = chunk->data; - size_t length = chunk->length; - - (void)ao; - - assert(length % audio_format_frame_size(&ao->in_audio_format) == 0); + const char *data = chunk->frame ? chunk->frame->data[0] : NULL; + size_t length = data ? av_samples_get_buffer_size(NULL, ao->in_audio_format.channels, + chunk->frame->nb_samples, + chunk->frame->format, 1) : 0; if (length > 0 && replay_gain_filter != NULL) { if (chunk->replay_gain_serial != *replay_gain_serial_p) { diff --git a/src/pipe.c b/src/pipe.c index d8131432..43d1ba1e 100644 --- a/src/pipe.c +++ b/src/pipe.c @@ -19,7 +19,6 @@ #include "config.h" #include "pipe.h" -#include "buffer.h" #include "chunk.h" #include @@ -33,7 +32,7 @@ struct music_pipe { /** a pointer to the tail of the chunk */ struct music_chunk **tail_r; - /** the current number of chunks */ + /** the current number of samples */ unsigned size; /** a mutex which protects #head and #tail_r */ @@ -123,7 +122,7 @@ music_pipe_shift(struct music_pipe *mp) assert(!music_chunk_is_empty(chunk)); mp->head = chunk->next; - --mp->size; + mp->size -= chunk->frame->nb_samples; if (mp->head == NULL) { assert(mp->size == 0); @@ -149,20 +148,22 @@ music_pipe_shift(struct music_pipe *mp) return chunk; } -void -music_pipe_clear(struct music_pipe *mp, struct music_buffer *buffer) +void music_pipe_clear(struct music_pipe *mp) { - struct music_chunk *chunk; + struct music_chunk *chunk; - while ((chunk = music_pipe_shift(mp)) != NULL) - music_buffer_return(buffer, chunk); + while ((chunk = music_pipe_shift(mp)) != NULL) { + if (chunk->other) + music_chunk_free(chunk->other); + music_chunk_free(chunk); + } } void music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk) { assert(!music_chunk_is_empty(chunk)); - assert(chunk->length == 0 || audio_format_valid(&chunk->audio_format)); + assert(!chunk->frame || audio_format_valid(&chunk->audio_format)); g_mutex_lock(mp->mutex); @@ -171,7 +172,7 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk) music_chunk_check_format(chunk, &mp->audio_format)); #ifndef NDEBUG - if (!audio_format_defined(&mp->audio_format) && chunk->length > 0) + if (!audio_format_defined(&mp->audio_format) && chunk->frame) mp->audio_format = chunk->audio_format; #endif @@ -179,13 +180,12 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk) *mp->tail_r = chunk; mp->tail_r = &chunk->next; - ++mp->size; + mp->size += chunk->frame->nb_samples; g_mutex_unlock(mp->mutex); } -unsigned -music_pipe_size(const struct music_pipe *mp) +unsigned music_pipe_size(struct music_pipe *mp) { g_mutex_lock(mp->mutex); unsigned size = mp->size; diff --git a/src/pipe.h b/src/pipe.h index 84b9869e..c8adfd6e 100644 --- a/src/pipe.h +++ b/src/pipe.h @@ -28,7 +28,6 @@ struct audio_format; #endif struct music_chunk; -struct music_buffer; /** * A queue of #music_chunk objects. One party appends chunks at the @@ -83,12 +82,9 @@ struct music_chunk * music_pipe_shift(struct music_pipe *mp); /** - * Clears the whole pipe and returns the chunks to the buffer. - * - * @param buffer the buffer object to return the chunks to + * Clears the whole pipe and frees all the chunks. */ -void -music_pipe_clear(struct music_pipe *mp, struct music_buffer *buffer); +void music_pipe_clear(struct music_pipe *mp); /** * Pushes a chunk to the tail of the pipe. @@ -97,15 +93,11 @@ void music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk); /** - * Returns the number of chunks currently in this pipe. + * Returns the number of samples currently in this pipe. */ -G_GNUC_PURE -unsigned -music_pipe_size(const struct music_pipe *mp); +unsigned music_pipe_size(struct music_pipe *mp); -G_GNUC_PURE -static inline bool -music_pipe_empty(const struct music_pipe *mp) +static inline bool music_pipe_empty(struct music_pipe *mp) { return music_pipe_size(mp) == 0; } diff --git a/src/player_thread.c b/src/player_thread.c index 3995ce34..8efff1ad 100644 --- a/src/player_thread.c +++ b/src/player_thread.c @@ -32,11 +32,14 @@ #include "chunk.h" #include "idle.h" #include "main.h" -#include "buffer.h" #include "mpd_error.h" #include +#include +#include +#include + #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "player_thread" @@ -124,8 +127,6 @@ struct player { float elapsed_time; }; -static struct music_buffer *player_buffer; - static void player_command_finished_locked(struct player_control *pc) { @@ -148,8 +149,7 @@ player_command_finished(struct player_control *pc) * * Player lock is not held. */ -static void -player_dc_start(struct player *player, struct music_pipe *pipe) +static void player_dc_start(struct player *player, struct music_pipe *pipe) { struct player_control *pc = player->pc; struct decoder_control *dc = player->dc; @@ -162,8 +162,8 @@ player_dc_start(struct player *player, struct music_pipe *pipe) start_ms += (unsigned)(pc->seek_where * 1000); dc_start(dc, pc->next_song, - start_ms, pc->next_song->end_ms, - player_buffer, pipe); + start_ms, pc->next_song->end_ms, + pc->buffer_chunks * 2048, pipe); } /** @@ -207,7 +207,7 @@ player_dc_stop(struct player *player) if (dc->pipe != NULL) { /* clear and free the decoder pipe */ - music_pipe_clear(dc->pipe, player_buffer); + music_pipe_clear(dc->pipe); if (dc->pipe != player->pipe) music_pipe_free(dc->pipe); @@ -304,7 +304,7 @@ player_open_output(struct player *player) assert(pc->state == PLAYER_STATE_PLAY || pc->state == PLAYER_STATE_PAUSE); - if (audio_output_all_open(&player->play_audio_format, player_buffer)) { + if (audio_output_all_open(&player->play_audio_format)) { player->output_open = true; player->paused = false; @@ -405,11 +405,15 @@ player_check_decoder_startup(struct player *player) static bool player_send_silence(struct player *player) { + struct music_chunk *chunk = NULL; + AVFrame *frame; + int ret; + assert(player->output_open); assert(audio_format_defined(&player->play_audio_format)); - struct music_chunk *chunk = music_buffer_allocate(player_buffer); - if (chunk == NULL) { + chunk = music_chunk_alloc(); + if (!chunk) { g_warning("Failed to allocate silence buffer"); return false; } @@ -418,22 +422,30 @@ player_send_silence(struct player *player) chunk->audio_format = player->play_audio_format; #endif - size_t frame_size = - audio_format_frame_size(&player->play_audio_format); - /* this formula ensures that we don't send - partial frames */ - unsigned num_frames = sizeof(chunk->data) / frame_size; + chunk->frame = frame = av_frame_alloc(); + if (!chunk->frame) + goto fail; + frame->nb_samples = 2048; + frame->format = sample_fmt_native_to_libav(player->play_audio_format.format); + frame->sample_rate = player->play_audio_format.sample_rate; + frame->channel_layout = av_get_default_channel_layout(player->play_audio_format.channels); + + ret = av_frame_get_buffer(frame, 32); + if (ret < 0) + goto fail; + + av_samples_set_silence(frame->extended_data, 0, frame->nb_samples, + player->play_audio_format.channels, frame->format); chunk->times = -1.0; /* undefined time stamp */ - chunk->length = num_frames * frame_size; - memset(chunk->data, 0, chunk->length); - if (!audio_output_all_play(chunk)) { - music_buffer_return(player_buffer, chunk); - return false; - } + if (!audio_output_all_play(chunk)) + goto fail; return true; +fail: + music_chunk_free(chunk); + return false; } /** @@ -457,7 +469,7 @@ 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); + music_pipe_clear(player->pipe); /* re-start the decoder */ player_dc_start(player, player->pipe); @@ -470,7 +482,7 @@ static bool player_seek_decoder(struct player *player) if (!player_dc_at_current_song(player)) { /* the decoder is already decoding the "next" song, but it is the same song file; exchange the pipe */ - music_pipe_clear(player->pipe, player_buffer); + music_pipe_clear(player->pipe); music_pipe_free(player->pipe); player->pipe = dc->pipe; } @@ -656,8 +668,8 @@ play_chunk(struct player_control *pc, if (chunk->tag != NULL) update_song_tag(song, chunk->tag); - if (chunk->length == 0) { - music_buffer_return(player_buffer, chunk); + if (!chunk->frame) { + music_chunk_free(chunk); return true; } @@ -670,8 +682,7 @@ play_chunk(struct player_control *pc, if (!audio_output_all_play(chunk)) return false; - pc->total_play_time += (double)chunk->length / - audio_format_time_to_size(format); + pc->total_play_time += (double)chunk->frame->nb_samples / chunk->frame->sample_rate; return true; } @@ -687,7 +698,7 @@ play_next_chunk(struct player *player) struct player_control *pc = player->pc; struct decoder_control *dc = player->dc; - if (!audio_output_all_wait(pc, 64)) + if (!audio_output_all_wait(pc, 64 * 2048)) /* the output pipe is still large enough, don't send another chunk */ return true; @@ -739,8 +750,7 @@ play_next_chunk(struct player *player) beginning of the new song, we can easily recover by throwing it away now */ - music_buffer_return(player_buffer, - other_chunk); + music_chunk_free(other_chunk); other_chunk = NULL; } @@ -784,7 +794,7 @@ play_next_chunk(struct player *player) if (!play_chunk(player->pc, player->song, chunk, &player->play_audio_format)) { - music_buffer_return(player_buffer, chunk); + music_chunk_free(chunk); player_lock(pc); @@ -806,7 +816,7 @@ play_next_chunk(struct player *player) decoder_lock(dc); if (!decoder_is_idle(dc) && music_pipe_size(dc->pipe) <= (pc->buffered_before_play + - music_buffer_size(player_buffer) * 3) / 4) + pc->buffer_chunks * 3) / 4) decoder_signal(dc); decoder_unlock(dc); @@ -910,7 +920,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc) if (!player.paused && player.output_open && - audio_output_all_check() < 4 && + audio_output_all_check() < 4 * 2048 && !player_send_silence(&player)) break; @@ -970,8 +980,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc) dc->mixramp_prev_end, &dc->out_audio_format, &player.play_audio_format, - music_buffer_size(player_buffer) - - pc->buffered_before_play); + pc->buffer_chunks - pc->buffered_before_play); if (player.cross_fade_chunks > 0) { player.xfade = XFADE_ENABLED; player.cross_fading = false; @@ -1027,7 +1036,7 @@ static void do_play(struct player_control *pc, struct decoder_control *dc) player_dc_stop(&player); - music_pipe_clear(player.pipe, player_buffer); + music_pipe_clear(player.pipe); music_pipe_free(player.pipe); if (player.cross_fade_tag != NULL) @@ -1057,8 +1066,6 @@ player_task(gpointer arg) struct decoder_control *dc = dc_new(pc->cond); decoder_thread_start(dc); - player_buffer = music_buffer_new(pc->buffer_chunks); - player_lock(pc); while (1) { @@ -1090,14 +1097,6 @@ player_task(gpointer arg) player_lock(pc); player_command_finished_locked(pc); -#ifndef NDEBUG - /* in the DEBUG build, check for leaked - music_chunk objects by freeing the - music_buffer */ - music_buffer_free(player_buffer); - player_buffer = music_buffer_new(pc->buffer_chunks); -#endif - break; case PLAYER_COMMAND_UPDATE_AUDIO: @@ -1113,7 +1112,6 @@ player_task(gpointer arg) dc_quit(dc); dc_free(dc); audio_output_all_close(); - music_buffer_free(player_buffer); player_command_finished(pc); return NULL; -- cgit v1.2.3