aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2013-04-23 09:20:10 +0200
committerAnton Khirnov <anton@khirnov.net>2015-02-20 08:56:03 +0100
commit1b3144727158b18f080732ff35ef1f6a123a946d (patch)
tree3cff858317d498ffb02e27364d6a2afde53943c3
parente41776f458722d778b5a0db49816f775ef4f9eee (diff)
music chunk: switch to AVFrame.
-rw-r--r--src/Makefile1
-rw-r--r--src/buffer.c137
-rw-r--r--src/buffer.h67
-rw-r--r--src/chunk.c78
-rw-r--r--src/chunk.h45
-rw-r--r--src/decoder_api.c106
-rw-r--r--src/decoder_control.c10
-rw-r--r--src/decoder_control.h11
-rw-r--r--src/decoder_internal.c58
-rw-r--r--src/decoder_internal.h9
-rw-r--r--src/decoder_thread.c6
-rw-r--r--src/output_all.c40
-rw-r--r--src/output_all.h17
-rw-r--r--src/output_internal.h7
-rw-r--r--src/output_thread.c10
-rw-r--r--src/pipe.c26
-rw-r--r--src/pipe.h18
-rw-r--r--src/player_thread.c94
18 files changed, 170 insertions, 570 deletions
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 <glib.h>
-
-#include <assert.h>
-
-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 <libavutil/frame.h>
+#include <libavutil/mem.h>
+
#include <assert.h>
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 <stdint.h>
#include <stddef.h>
+#include <libavutil/frame.h>
+
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"
@@ -45,11 +44,6 @@ 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 <stddef.h>
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 <glib.h>
@@ -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 <glib.h>
+#include <libavutil/channel_layout.h>
+#include <libavutil/frame.h>
+#include <libavutil/samplefmt.h>
+
#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;