aboutsummaryrefslogtreecommitdiff
path: root/src/decoder
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder')
-rw-r--r--src/decoder/AdPlugDecoderPlugin.cxx148
-rw-r--r--src/decoder/AdPlugDecoderPlugin.h25
-rw-r--r--src/decoder/FLACCommon.cxx (renamed from src/decoder/_flac_common.c)67
-rw-r--r--src/decoder/FLACCommon.hxx (renamed from src/decoder/_flac_common.h)30
-rw-r--r--src/decoder/FLACDecoderPlugin.cxx (renamed from src/decoder/flac_decoder_plugin.c)291
-rw-r--r--src/decoder/FLACDecoderPlugin.h26
-rw-r--r--src/decoder/FLACIOHandle.cxx114
-rw-r--r--src/decoder/FLACIOHandle.hxx45
-rw-r--r--src/decoder/FLACInput.cxx149
-rw-r--r--src/decoder/FLACInput.hxx72
-rw-r--r--src/decoder/FLACMetaData.cxx (renamed from src/decoder/flac_metadata.c)136
-rw-r--r--src/decoder/FLACMetaData.hxx140
-rw-r--r--src/decoder/FLAC_PCM.cxx (renamed from src/decoder/flac_pcm.c)4
-rw-r--r--src/decoder/FLAC_PCM.hxx (renamed from src/decoder/flac_pcm.h)6
-rw-r--r--src/decoder/FfmpegDecoderPlugin.cxx (renamed from src/decoder/ffmpeg_decoder_plugin.c)122
-rw-r--r--src/decoder/FfmpegDecoderPlugin.hxx25
-rw-r--r--src/decoder/FfmpegMetaData.cxx (renamed from src/decoder/ffmpeg_metadata.c)18
-rw-r--r--src/decoder/FfmpegMetaData.hxx (renamed from src/decoder/ffmpeg_metadata.h)16
-rw-r--r--src/decoder/OggCodec.cxx (renamed from src/decoder/_ogg_common.c)20
-rw-r--r--src/decoder/OggCodec.hxx (renamed from src/decoder/_ogg_common.h)16
-rw-r--r--src/decoder/OggFind.cxx37
-rw-r--r--src/decoder/OggFind.hxx38
-rw-r--r--src/decoder/OggSyncState.hxx78
-rw-r--r--src/decoder/OggUtil.cxx118
-rw-r--r--src/decoder/OggUtil.hxx87
-rw-r--r--src/decoder/OpusDecoderPlugin.cxx400
-rw-r--r--src/decoder/OpusDecoderPlugin.h25
-rw-r--r--src/decoder/OpusHead.cxx44
-rw-r--r--src/decoder/OpusHead.hxx30
-rw-r--r--src/decoder/OpusReader.hxx98
-rw-r--r--src/decoder/OpusTags.cxx77
-rw-r--r--src/decoder/OpusTags.hxx31
-rw-r--r--src/decoder/VorbisComments.cxx (renamed from src/decoder/vorbis_comments.c)16
-rw-r--r--src/decoder/VorbisComments.hxx (renamed from src/decoder/vorbis_comments.h)6
-rw-r--r--src/decoder/VorbisDecoderPlugin.cxx (renamed from src/decoder/vorbis_decoder_plugin.c)157
-rw-r--r--src/decoder/VorbisDecoderPlugin.h25
-rw-r--r--src/decoder/WavpackDecoderPlugin.cxx (renamed from src/decoder/wavpack_decoder_plugin.c)65
-rw-r--r--src/decoder/WavpackDecoderPlugin.hxx25
-rw-r--r--src/decoder/XiphTags.c28
-rw-r--r--src/decoder/XiphTags.h28
-rw-r--r--src/decoder/audiofile_decoder_plugin.c10
-rw-r--r--src/decoder/dsdiff_decoder_plugin.c151
-rw-r--r--src/decoder/dsdlib.c72
-rw-r--r--src/decoder/dsdlib.h5
-rw-r--r--src/decoder/dsf_decoder_plugin.c30
-rw-r--r--src/decoder/faad_decoder_plugin.c72
-rw-r--r--src/decoder/flac_compat.h114
-rw-r--r--src/decoder/flac_metadata.h64
-rw-r--r--src/decoder/mad_decoder_plugin.c21
-rw-r--r--src/decoder/modplug_decoder_plugin.c13
-rw-r--r--src/decoder/mp4ff_decoder_plugin.c448
-rw-r--r--src/decoder/mpcdec_decoder_plugin.c8
-rw-r--r--src/decoder/pcm_decoder_plugin.c13
-rw-r--r--src/decoder/sidplay_decoder_plugin.cxx10
-rw-r--r--src/decoder/sndfile_decoder_plugin.c6
55 files changed, 2616 insertions, 1304 deletions
diff --git a/src/decoder/AdPlugDecoderPlugin.cxx b/src/decoder/AdPlugDecoderPlugin.cxx
new file mode 100644
index 00000000..6d08fab5
--- /dev/null
+++ b/src/decoder/AdPlugDecoderPlugin.cxx
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2003-2012 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 "AdPlugDecoderPlugin.h"
+#include "tag_handler.h"
+#include "decoder_api.h"
+
+extern "C" {
+#include "audio_check.h"
+}
+
+#include <adplug/adplug.h>
+#include <adplug/emuopl.h>
+
+#include <glib.h>
+
+#include <assert.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "adplug"
+
+static unsigned sample_rate;
+
+static bool
+adplug_init(const struct config_param *param)
+{
+ GError *error = NULL;
+
+ sample_rate = config_get_block_unsigned(param, "sample_rate", 48000);
+ if (!audio_check_sample_rate(sample_rate, &error)) {
+ g_warning("%s\n", error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ return true;
+}
+
+static void
+adplug_file_decode(struct decoder *decoder, const char *path_fs)
+{
+ CEmuopl opl(sample_rate, true, true);
+ opl.init();
+
+ CPlayer *player = CAdPlug::factory(path_fs, &opl);
+ if (player == nullptr)
+ return;
+
+ struct audio_format audio_format;
+ audio_format_init(&audio_format, sample_rate, SAMPLE_FORMAT_S16, 2);
+ assert(audio_format_valid(&audio_format));
+
+ decoder_initialized(decoder, &audio_format, false,
+ player->songlength() / 1000.);
+
+ int16_t buffer[2048];
+ const unsigned frames_per_buffer = G_N_ELEMENTS(buffer) / 2;
+ enum decoder_command cmd;
+
+ do {
+ if (!player->update())
+ break;
+
+ opl.update(buffer, frames_per_buffer);
+ cmd = decoder_data(decoder, NULL,
+ buffer, sizeof(buffer),
+ 0);
+ } while (cmd == DECODE_COMMAND_NONE);
+
+ delete player;
+}
+
+static void
+adplug_scan_tag(enum tag_type type, const std::string &value,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ if (!value.empty())
+ tag_handler_invoke_tag(handler, handler_ctx,
+ type, value.c_str());
+}
+
+static bool
+adplug_scan_file(const char *path_fs,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ CEmuopl opl(sample_rate, true, true);
+ opl.init();
+
+ CPlayer *player = CAdPlug::factory(path_fs, &opl);
+ if (player == nullptr)
+ return false;
+
+ tag_handler_invoke_duration(handler, handler_ctx,
+ player->songlength() / 1000);
+
+ if (handler->tag != nullptr) {
+ adplug_scan_tag(TAG_TITLE, player->gettitle(),
+ handler, handler_ctx);
+ adplug_scan_tag(TAG_ARTIST, player->getauthor(),
+ handler, handler_ctx);
+ adplug_scan_tag(TAG_COMMENT, player->getdesc(),
+ handler, handler_ctx);
+ }
+
+ delete player;
+ return true;
+}
+
+static const char *const adplug_suffixes[] = {
+ "amd",
+ "d00",
+ "hsc",
+ "laa",
+ "rad",
+ "raw",
+ "sa2",
+ nullptr
+};
+
+const struct decoder_plugin adplug_decoder_plugin = {
+ "adplug",
+ adplug_init,
+ nullptr,
+ nullptr,
+ adplug_file_decode,
+ adplug_scan_file,
+ nullptr,
+ nullptr,
+ adplug_suffixes,
+ nullptr,
+};
diff --git a/src/decoder/AdPlugDecoderPlugin.h b/src/decoder/AdPlugDecoderPlugin.h
new file mode 100644
index 00000000..9fdf438a
--- /dev/null
+++ b/src/decoder/AdPlugDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 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_DECODER_ADPLUG_H
+#define MPD_DECODER_ADPLUG_H
+
+extern const struct decoder_plugin adplug_decoder_plugin;
+
+#endif
diff --git a/src/decoder/_flac_common.c b/src/decoder/FLACCommon.cxx
index d7f0c4a8..25fd1f96 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/FLACCommon.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,40 +22,35 @@
*/
#include "config.h"
-#include "_flac_common.h"
-#include "flac_metadata.h"
-#include "flac_pcm.h"
+#include "FLACCommon.hxx"
+#include "FLACMetaData.hxx"
+#include "FLAC_PCM.hxx"
+
+extern "C" {
#include "audio_check.h"
+}
#include <glib.h>
#include <assert.h>
-void
-flac_data_init(struct flac_data *data, struct decoder * decoder,
- struct input_stream *input_stream)
+flac_data::flac_data(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :FLACInput(_input_stream, _decoder),
+ initialized(false), unsupported(false),
+ total_frames(0), first_frame(0), next_frame(0), position(0),
+ decoder(_decoder), input_stream(_input_stream),
+ tag(nullptr)
{
- pcm_buffer_init(&data->buffer);
-
- data->unsupported = false;
- data->initialized = false;
- data->total_frames = 0;
- data->first_frame = 0;
- data->next_frame = 0;
-
- data->position = 0;
- data->decoder = decoder;
- data->input_stream = input_stream;
- data->tag = NULL;
+ pcm_buffer_init(&buffer);
}
-void
-flac_data_deinit(struct flac_data *data)
+flac_data::~flac_data()
{
- pcm_buffer_deinit(&data->buffer);
+ pcm_buffer_deinit(&buffer);
- if (data->tag != NULL)
- tag_free(data->tag);
+ if (tag != nullptr)
+ tag_free(tag);
}
static enum sample_format
@@ -86,7 +81,7 @@ flac_got_stream_info(struct flac_data *data,
if (data->initialized || data->unsupported)
return;
- GError *error = NULL;
+ GError *error = nullptr;
if (!audio_format_init_checked(&data->audio_format,
stream_info->sample_rate,
flac_sample_format(stream_info->bits_per_sample),
@@ -114,7 +109,6 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct replay_gain_info rgi;
char *mixramp_start;
char *mixramp_end;
- float replay_gain_db = 0;
switch (block->type) {
case FLAC__METADATA_TYPE_STREAMINFO:
@@ -123,14 +117,14 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
if (flac_parse_replay_gain(&rgi, block))
- replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
+ decoder_replay_gain(data->decoder, &rgi);
if (flac_parse_mixramp(&mixramp_start, &mixramp_end, block))
- decoder_mixramp(data->decoder, replay_gain_db,
+ decoder_mixramp(data->decoder,
mixramp_start, mixramp_end);
- if (data->tag != NULL)
- flac_vorbis_comments_to_tag(data->tag, NULL,
+ if (data->tag != nullptr)
+ flac_vorbis_comments_to_tag(data->tag,
&block->data.vorbis_comment);
default:
@@ -138,15 +132,6 @@ void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
}
}
-void flac_error_common_cb(const FLAC__StreamDecoderErrorStatus status,
- struct flac_data *data)
-{
- if (decoder_get_command(data->decoder) == DECODE_COMMAND_STOP)
- return;
-
- g_warning("%s", FLAC__StreamDecoderErrorStatusString[status]);
-}
-
/**
* This function attempts to call decoder_initialized() in case there
* was no STREAMINFO block. This is allowed for nonseekable streams,
@@ -160,7 +145,7 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header)
if (data->unsupported)
return false;
- GError *error = NULL;
+ GError *error = nullptr;
if (!audio_format_init_checked(&data->audio_format,
header->sample_rate,
flac_sample_format(header->bits_per_sample),
@@ -199,7 +184,7 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
buffer = pcm_buffer_get(&data->buffer, buffer_size);
flac_convert(buffer, frame->header.channels,
- data->audio_format.format, buf,
+ (enum sample_format)data->audio_format.format, buf,
0, frame->header.blocksize);
if (nbytes > 0)
diff --git a/src/decoder/_flac_common.h b/src/decoder/FLACCommon.hxx
index 0d90ba65..b80372bb 100644
--- a/src/decoder/_flac_common.h
+++ b/src/decoder/FLACCommon.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,13 +21,15 @@
* Common data structures and functions used by FLAC and OggFLAC
*/
-#ifndef MPD_FLAC_COMMON_H
-#define MPD_FLAC_COMMON_H
+#ifndef MPD_FLAC_COMMON_HXX
+#define MPD_FLAC_COMMON_HXX
+#include "FLACInput.hxx"
#include "decoder_api.h"
-#include "pcm_buffer.h"
-#include <glib.h>
+extern "C" {
+#include "pcm_buffer.h"
+}
#include <FLAC/stream_decoder.h>
#include <FLAC/metadata.h>
@@ -35,7 +37,7 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "flac"
-struct flac_data {
+struct flac_data : public FLACInput {
struct pcm_buffer buffer;
/**
@@ -78,25 +80,19 @@ struct flac_data {
FLAC__uint64 next_frame;
FLAC__uint64 position;
+
struct decoder *decoder;
struct input_stream *input_stream;
- struct tag *tag;
-};
-/* initializes a given FlacData struct */
-void
-flac_data_init(struct flac_data *data, struct decoder * decoder,
- struct input_stream *input_stream);
+ struct tag *tag;
-void
-flac_data_deinit(struct flac_data *data);
+ flac_data(struct decoder *decoder, struct input_stream *input_stream);
+ ~flac_data();
+};
void flac_metadata_common_cb(const FLAC__StreamMetadata * block,
struct flac_data *data);
-void flac_error_common_cb(FLAC__StreamDecoderErrorStatus status,
- struct flac_data *data);
-
FLAC__StreamDecoderWriteStatus
flac_common_write(struct flac_data *data, const FLAC__Frame * frame,
const FLAC__int32 *const buf[],
diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/FLACDecoderPlugin.cxx
index fb0b3502..43b60409 100644
--- a/src/decoder/flac_decoder_plugin.c
+++ b/src/decoder/FLACDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,13 +18,10 @@
*/
#include "config.h" /* must be first for large file support */
-#include "_flac_common.h"
-#include "flac_compat.h"
-#include "flac_metadata.h"
-
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-#include "_ogg_common.h"
-#endif
+#include "FLACDecoderPlugin.h"
+#include "FLACCommon.hxx"
+#include "FLACMetaData.hxx"
+#include "OggCodec.hxx"
#include <glib.h>
@@ -34,114 +31,10 @@
#include <sys/stat.h>
#include <sys/types.h>
-/* this code was based on flac123, from flac-tools */
-
-static FLAC__StreamDecoderReadStatus
-flac_read_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__byte buf[], flac_read_status_size_t *bytes,
- void *fdata)
-{
- struct flac_data *data = fdata;
- size_t r;
-
- r = decoder_read(data->decoder, data->input_stream,
- (void *)buf, *bytes);
- *bytes = r;
-
- if (r == 0) {
- if (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE ||
- input_stream_lock_eof(data->input_stream))
- return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
- else
- return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
- }
-
- return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
-}
-
-static FLAC__StreamDecoderSeekStatus
-flac_seek_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 offset, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (!data->input_stream->seekable)
- return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
-
- if (!input_stream_lock_seek(data->input_stream, offset, SEEK_SET,
- NULL))
- return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
-
- return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
-}
-
-static FLAC__StreamDecoderTellStatus
-flac_tell_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 * offset, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (!data->input_stream->seekable)
- return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
-
- *offset = (long)(data->input_stream->offset);
-
- return FLAC__STREAM_DECODER_TELL_STATUS_OK;
-}
-
-static FLAC__StreamDecoderLengthStatus
-flac_length_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__uint64 * length, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (data->input_stream->size < 0)
- return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
-
- *length = (size_t) (data->input_stream->size);
-
- return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
-}
-
-static FLAC__bool
-flac_eof_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- return (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE &&
- decoder_get_command(data->decoder) != DECODE_COMMAND_SEEK) ||
- input_stream_lock_eof(data->input_stream);
-}
-
-static void
-flac_error_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
- FLAC__StreamDecoderErrorStatus status, void *fdata)
-{
- flac_error_common_cb(status, (struct flac_data *) fdata);
-}
-
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-static void flacPrintErroredState(FLAC__SeekableStreamDecoderState state)
-{
- switch (state) {
- case FLAC__SEEKABLE_STREAM_DECODER_OK:
- case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
- case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
- return;
-
- case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
- case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
- case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
- case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
- break;
- }
+#error libFLAC is too old
+#endif
- g_warning("%s\n", FLAC__SeekableStreamDecoderStateString[state]);
-}
-#else /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacPrintErroredState(FLAC__StreamDecoderState state)
{
switch (state) {
@@ -162,7 +55,6 @@ static void flacPrintErroredState(FLAC__StreamDecoderState state)
g_warning("%s\n", FLAC__StreamDecoderStateString[state]);
}
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
static void flacMetadata(G_GNUC_UNUSED const FLAC__StreamDecoder * dec,
const FLAC__StreamMetadata * block, void *vdata)
@@ -195,7 +87,30 @@ static bool
flac_scan_file(const char *file,
const struct tag_handler *handler, void *handler_ctx)
{
- return flac_scan_file2(file, NULL, handler, handler_ctx);
+ FLACMetadataChain chain;
+ if (!chain.Read(file)) {
+ g_debug("Failed to read FLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
+
+ chain.Scan(handler, handler_ctx);
+ return true;
+}
+
+static bool
+flac_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ FLACMetadataChain chain;
+ if (!chain.Read(is)) {
+ g_debug("Failed to read FLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
+
+ chain.Scan(handler, handler_ctx);
+ return true;
}
/**
@@ -205,15 +120,13 @@ static FLAC__StreamDecoder *
flac_decoder_new(void)
{
FLAC__StreamDecoder *sd = FLAC__stream_decoder_new();
- if (sd == NULL) {
+ if (sd == nullptr) {
g_warning("FLAC__stream_decoder_new() failed");
- return NULL;
+ return nullptr;
}
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
if(!FLAC__stream_decoder_set_metadata_respond(sd, FLAC__METADATA_TYPE_VORBIS_COMMENT))
g_debug("FLAC__stream_decoder_set_metadata_respond() has failed");
-#endif
return sd;
}
@@ -259,7 +172,7 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
data->first_frame = t_start;
while (true) {
- if (data->tag != NULL && !tag_is_empty(data->tag)) {
+ if (data->tag != nullptr && !tag_is_empty(data->tag)) {
cmd = decoder_tag(data->decoder, data->input_stream,
data->tag);
tag_free(data->tag);
@@ -300,34 +213,30 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
static FLAC__StreamDecoderInitStatus
stream_init_oggflac(FLAC__StreamDecoder *flac_dec, struct flac_data *data)
{
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return FLAC__stream_decoder_init_ogg_stream(flac_dec,
- flac_read_cb,
- flac_seek_cb,
- flac_tell_cb,
- flac_length_cb,
- flac_eof_cb,
+ FLACInput::Read,
+ FLACInput::Seek,
+ FLACInput::Tell,
+ FLACInput::Length,
+ FLACInput::Eof,
flac_write_cb,
flacMetadata,
- flac_error_cb,
+ FLACInput::Error,
data);
-#else
- (void)flac_dec;
- (void)data;
-
- return FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
-#endif
}
static FLAC__StreamDecoderInitStatus
stream_init_flac(FLAC__StreamDecoder *flac_dec, struct flac_data *data)
{
return FLAC__stream_decoder_init_stream(flac_dec,
- flac_read_cb, flac_seek_cb,
- flac_tell_cb, flac_length_cb,
- flac_eof_cb, flac_write_cb,
+ FLACInput::Read,
+ FLACInput::Seek,
+ FLACInput::Tell,
+ FLACInput::Length,
+ FLACInput::Eof,
+ flac_write_cb,
flacMetadata,
- flac_error_cb,
+ FLACInput::Error,
data);
}
@@ -345,28 +254,23 @@ flac_decode_internal(struct decoder * decoder,
bool is_ogg)
{
FLAC__StreamDecoder *flac_dec;
- struct flac_data data;
flac_dec = flac_decoder_new();
- if (flac_dec == NULL)
+ if (flac_dec == nullptr)
return;
- flac_data_init(&data, decoder, input_stream);
+ struct flac_data data(decoder, input_stream);
data.tag = tag_new();
FLAC__StreamDecoderInitStatus status =
stream_init(flac_dec, &data, is_ogg);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
- flac_data_deinit(&data);
FLAC__stream_decoder_delete(flac_dec);
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
g_warning("%s", FLAC__StreamDecoderInitStatusString[status]);
-#endif
return;
}
if (!flac_decoder_initialize(&data, flac_dec, 0)) {
- flac_data_deinit(&data);
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
return;
@@ -374,8 +278,6 @@ flac_decode_internal(struct decoder * decoder,
flac_decoder_loop(&data, flac_dec, 0, 0);
- flac_data_deinit(&data);
-
FLAC__stream_decoder_finish(flac_dec);
FLAC__stream_decoder_delete(flac_dec);
}
@@ -386,101 +288,96 @@ flac_decode(struct decoder * decoder, struct input_stream *input_stream)
flac_decode_internal(decoder, input_stream, false);
}
-#ifndef HAVE_OGGFLAC
-
static bool
oggflac_init(G_GNUC_UNUSED const struct config_param *param)
{
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
return !!FLAC_API_SUPPORTS_OGG_FLAC;
-#else
- /* disable oggflac when libflac is too old */
- return false;
-#endif
}
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
-
static bool
oggflac_scan_file(const char *file,
const struct tag_handler *handler, void *handler_ctx)
{
- FLAC__Metadata_Iterator *it;
- FLAC__StreamMetadata *block;
- FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
-
- if (!(FLAC__metadata_chain_read_ogg(chain, file))) {
- FLAC__metadata_chain_delete(chain);
+ FLACMetadataChain chain;
+ if (!chain.ReadOgg(file)) {
+ g_debug("Failed to read OggFLAC tags: %s",
+ chain.GetStatusString());
return false;
}
- it = FLAC__metadata_iterator_new();
- FLAC__metadata_iterator_init(it, chain);
-
- do {
- if (!(block = FLAC__metadata_iterator_get_block(it)))
- break;
+ chain.Scan(handler, handler_ctx);
+ return true;
+}
- flac_scan_metadata(NULL, block,
- handler, handler_ctx);
- } while (FLAC__metadata_iterator_next(it));
- FLAC__metadata_iterator_delete(it);
+static bool
+oggflac_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ FLACMetadataChain chain;
+ if (!chain.ReadOgg(is)) {
+ g_debug("Failed to read OggFLAC tags: %s",
+ chain.GetStatusString());
+ return false;
+ }
- FLAC__metadata_chain_delete(chain);
+ chain.Scan(handler, handler_ctx);
return true;
}
static void
oggflac_decode(struct decoder *decoder, struct input_stream *input_stream)
{
- if (ogg_stream_type_detect(input_stream) != FLAC)
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_FLAC)
return;
- /* rewind the stream, because ogg_stream_type_detect() has
+ /* rewind the stream, because ogg_codec_detect() has
moved it */
- input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL);
+ input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr);
flac_decode_internal(decoder, input_stream, true);
}
-static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
+static const char *const oggflac_suffixes[] = { "ogg", "oga", nullptr };
static const char *const oggflac_mime_types[] = {
"application/ogg",
"application/x-ogg",
"audio/ogg",
"audio/x-flac+ogg",
"audio/x-ogg",
- NULL
+ nullptr
};
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
-
const struct decoder_plugin oggflac_decoder_plugin = {
- .name = "oggflac",
- .init = oggflac_init,
-#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
- .stream_decode = oggflac_decode,
- .scan_file = oggflac_scan_file,
- .suffixes = oggflac_suffixes,
- .mime_types = oggflac_mime_types
-#endif
+ "oggflac",
+ oggflac_init,
+ nullptr,
+ oggflac_decode,
+ nullptr,
+ oggflac_scan_file,
+ oggflac_scan_stream,
+ nullptr,
+ oggflac_suffixes,
+ oggflac_mime_types,
};
-#endif /* HAVE_OGGFLAC */
-
-static const char *const flac_suffixes[] = { "flac", NULL };
+static const char *const flac_suffixes[] = { "flac", nullptr };
static const char *const flac_mime_types[] = {
"application/flac",
"application/x-flac",
"audio/flac",
"audio/x-flac",
- NULL
+ nullptr
};
const struct decoder_plugin flac_decoder_plugin = {
- .name = "flac",
- .stream_decode = flac_decode,
- .scan_file = flac_scan_file,
- .suffixes = flac_suffixes,
- .mime_types = flac_mime_types,
+ "flac",
+ nullptr,
+ nullptr,
+ flac_decode,
+ nullptr,
+ flac_scan_file,
+ flac_scan_stream,
+ nullptr,
+ flac_suffixes,
+ flac_mime_types,
};
diff --git a/src/decoder/FLACDecoderPlugin.h b/src/decoder/FLACDecoderPlugin.h
new file mode 100644
index 00000000..c99deeef
--- /dev/null
+++ b/src/decoder/FLACDecoderPlugin.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2003-2012 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_DECODER_FLAC_H
+#define MPD_DECODER_FLAC_H
+
+extern const struct decoder_plugin flac_decoder_plugin;
+extern const struct decoder_plugin oggflac_decoder_plugin;
+
+#endif
diff --git a/src/decoder/FLACIOHandle.cxx b/src/decoder/FLACIOHandle.cxx
new file mode 100644
index 00000000..08ec36e4
--- /dev/null
+++ b/src/decoder/FLACIOHandle.cxx
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2003-2012 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 "FLACIOHandle.hxx"
+#include "io_error.h"
+#include "gcc.h"
+
+#include <errno.h>
+
+static size_t
+FLACIORead(void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ uint8_t *const p0 = (uint8_t *)ptr, *p = p0,
+ *const end = p0 + size * nmemb;
+
+ /* libFLAC is very picky about short reads, and expects the IO
+ callback to fill the whole buffer (undocumented!) */
+
+ GError *error = nullptr;
+ while (p < end) {
+ size_t nbytes = input_stream_lock_read(is, p, end - p, &error);
+ if (nbytes == 0) {
+ if (error == nullptr)
+ /* end of file */
+ break;
+
+ if (error->domain == errno_quark())
+ errno = error->code;
+ else
+ /* just some random non-zero
+ errno value */
+ errno = EINVAL;
+ g_error_free(error);
+ return 0;
+ }
+
+ p += nbytes;
+ }
+
+ /* libFLAC expects a clean errno after returning from the IO
+ callbacks (undocumented!) */
+ errno = 0;
+ return (p - p0) / size;
+}
+
+static int
+FLACIOSeek(FLAC__IOHandle handle, FLAC__int64 offset, int whence)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return input_stream_lock_seek(is, offset, whence, nullptr) ? 0 : -1;
+}
+
+static FLAC__int64
+FLACIOTell(FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return is->offset;
+}
+
+static int
+FLACIOEof(FLAC__IOHandle handle)
+{
+ input_stream *is = (input_stream *)handle;
+
+ return input_stream_lock_eof(is);
+}
+
+static int
+FLACIOClose(gcc_unused FLAC__IOHandle handle)
+{
+ /* no-op because the libFLAC caller is repsonsible for closing
+ the #input_stream */
+
+ return 0;
+}
+
+const FLAC__IOCallbacks flac_io_callbacks = {
+ FLACIORead,
+ nullptr,
+ nullptr,
+ nullptr,
+ FLACIOEof,
+ FLACIOClose,
+};
+
+const FLAC__IOCallbacks flac_io_callbacks_seekable = {
+ FLACIORead,
+ nullptr,
+ FLACIOSeek,
+ FLACIOTell,
+ FLACIOEof,
+ FLACIOClose,
+};
diff --git a/src/decoder/FLACIOHandle.hxx b/src/decoder/FLACIOHandle.hxx
new file mode 100644
index 00000000..505d2db1
--- /dev/null
+++ b/src/decoder/FLACIOHandle.hxx
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2003-2012 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_FLAC_IO_HANDLE_HXX
+#define MPD_FLAC_IO_HANDLE_HXX
+
+#include "gcc.h"
+#include "InputStream.hxx"
+
+#include <FLAC/callback.h>
+
+extern const FLAC__IOCallbacks flac_io_callbacks;
+extern const FLAC__IOCallbacks flac_io_callbacks_seekable;
+
+static inline FLAC__IOHandle
+ToFLACIOHandle(input_stream *is)
+{
+ return (FLAC__IOHandle)is;
+}
+
+static inline const FLAC__IOCallbacks &
+GetFLACIOCallbacks(const input_stream *is)
+{
+ return is->seekable
+ ? flac_io_callbacks_seekable
+ : flac_io_callbacks;
+}
+
+#endif
diff --git a/src/decoder/FLACInput.cxx b/src/decoder/FLACInput.cxx
new file mode 100644
index 00000000..ba0a86ce
--- /dev/null
+++ b/src/decoder/FLACInput.cxx
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2003-2012 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 "FLACInput.hxx"
+#include "decoder_api.h"
+#include "gcc.h"
+#include "InputStream.hxx"
+
+FLAC__StreamDecoderReadStatus
+FLACInput::Read(FLAC__byte buffer[], size_t *bytes)
+{
+ size_t r = decoder_read(decoder, input_stream, (void *)buffer, *bytes);
+ *bytes = r;
+
+ if (r == 0) {
+ if (input_stream_lock_eof(input_stream) ||
+ (decoder != nullptr &&
+ decoder_get_command(decoder) != DECODE_COMMAND_NONE))
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ else
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+ }
+
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+}
+
+FLAC__StreamDecoderSeekStatus
+FLACInput::Seek(FLAC__uint64 absolute_byte_offset)
+{
+ if (!input_stream->seekable)
+ return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
+
+ if (!input_stream_lock_seek(input_stream,
+ absolute_byte_offset, SEEK_SET,
+ nullptr))
+ return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+
+ return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+FLAC__StreamDecoderTellStatus
+FLACInput::Tell(FLAC__uint64 *absolute_byte_offset)
+{
+ if (!input_stream->seekable)
+ return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
+
+ *absolute_byte_offset = (FLAC__uint64)input_stream->offset;
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+FLAC__StreamDecoderLengthStatus
+FLACInput::Length(FLAC__uint64 *stream_length)
+{
+ if (input_stream->size < 0)
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
+
+ *stream_length = (FLAC__uint64)input_stream->size;
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+FLAC__bool
+FLACInput::Eof()
+{
+ return (decoder != nullptr &&
+ decoder_get_command(decoder) != DECODE_COMMAND_NONE &&
+ decoder_get_command(decoder) != DECODE_COMMAND_SEEK) ||
+ input_stream_lock_eof(input_stream);
+}
+
+void
+FLACInput::Error(FLAC__StreamDecoderErrorStatus status)
+{
+ if (decoder == nullptr ||
+ decoder_get_command(decoder) != DECODE_COMMAND_STOP)
+ g_warning("%s", FLAC__StreamDecoderErrorStatusString[status]);
+}
+
+FLAC__StreamDecoderReadStatus
+FLACInput::Read(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__byte buffer[], size_t *bytes,
+ void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Read(buffer, bytes);
+}
+
+FLAC__StreamDecoderSeekStatus
+FLACInput::Seek(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Seek(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderTellStatus
+FLACInput::Tell(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Tell(absolute_byte_offset);
+}
+
+FLAC__StreamDecoderLengthStatus
+FLACInput::Length(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *stream_length, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Length(stream_length);
+}
+
+FLAC__bool
+FLACInput::Eof(gcc_unused const FLAC__StreamDecoder *flac_decoder,
+ void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ return i->Eof();
+}
+
+void
+FLACInput::Error(gcc_unused const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data)
+{
+ FLACInput *i = (FLACInput *)client_data;
+
+ i->Error(status);
+}
+
diff --git a/src/decoder/FLACInput.hxx b/src/decoder/FLACInput.hxx
new file mode 100644
index 00000000..7661567d
--- /dev/null
+++ b/src/decoder/FLACInput.hxx
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2003-2012 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_FLAC_INPUT_HXX
+#define MPD_FLAC_INPUT_HXX
+
+#include <FLAC/stream_decoder.h>
+
+/**
+ * This class wraps an #input_stream in libFLAC stream decoder
+ * callbacks.
+ */
+class FLACInput {
+ struct decoder *decoder;
+
+ struct input_stream *input_stream;
+
+public:
+ FLACInput(struct input_stream *_input_stream,
+ struct decoder *_decoder=nullptr)
+ :decoder(_decoder), input_stream(_input_stream) {}
+
+protected:
+ FLAC__StreamDecoderReadStatus Read(FLAC__byte buffer[], size_t *bytes);
+ FLAC__StreamDecoderSeekStatus Seek(FLAC__uint64 absolute_byte_offset);
+ FLAC__StreamDecoderTellStatus Tell(FLAC__uint64 *absolute_byte_offset);
+ FLAC__StreamDecoderLengthStatus Length(FLAC__uint64 *stream_length);
+ FLAC__bool Eof();
+ void Error(FLAC__StreamDecoderErrorStatus status);
+
+public:
+ static FLAC__StreamDecoderReadStatus
+ Read(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__byte buffer[], size_t *bytes, void *client_data);
+
+ static FLAC__StreamDecoderSeekStatus
+ Seek(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 absolute_byte_offset, void *client_data);
+
+ static FLAC__StreamDecoderTellStatus
+ Tell(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *absolute_byte_offset, void *client_data);
+
+ static FLAC__StreamDecoderLengthStatus
+ Length(const FLAC__StreamDecoder *flac_decoder,
+ FLAC__uint64 *stream_length, void *client_data);
+
+ static FLAC__bool
+ Eof(const FLAC__StreamDecoder *flac_decoder, void *client_data);
+
+ static void
+ Error(const FLAC__StreamDecoder *decoder,
+ FLAC__StreamDecoderErrorStatus status, void *client_data);
+};
+
+#endif
diff --git a/src/decoder/flac_metadata.c b/src/decoder/FLACMetaData.cxx
index bd1eaf32..8273a230 100644
--- a/src/decoder/flac_metadata.c
+++ b/src/decoder/FLACMetaData.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,16 +18,20 @@
*/
#include "config.h"
-#include "flac_metadata.h"
-#include "replay_gain_info.h"
+#include "FLACMetaData.hxx"
+
+extern "C" {
+#include "XiphTags.h"
+}
+
#include "tag.h"
#include "tag_handler.h"
#include "tag_table.h"
+#include "replay_gain_info.h"
#include <glib.h>
#include <assert.h>
-#include <stdbool.h>
#include <stdlib.h>
static bool
@@ -91,7 +95,7 @@ flac_find_string_comment(const FLAC__StreamMetadata *block,
int len;
const unsigned char *p;
- *str = NULL;
+ *str = nullptr;
offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, 0,
cmnt);
if (offset < 0)
@@ -128,36 +132,21 @@ flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
*/
static const char *
flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
- const char *name, const char *char_tnum, size_t *length_r)
+ const char *name, size_t *length_r)
{
size_t name_length = strlen(name);
- size_t char_tnum_length = 0;
const char *comment = (const char*)entry->entry;
if (entry->length <= name_length ||
g_ascii_strncasecmp(comment, name, name_length) != 0)
- return NULL;
-
- if (char_tnum != NULL) {
- char_tnum_length = strlen(char_tnum);
- if (entry->length > name_length + char_tnum_length + 2 &&
- comment[name_length] == '[' &&
- g_ascii_strncasecmp(comment + name_length + 1,
- char_tnum, char_tnum_length) == 0 &&
- comment[name_length + char_tnum_length + 1] == ']')
- name_length = name_length + char_tnum_length + 2;
- else if (entry->length > name_length + char_tnum_length &&
- g_ascii_strncasecmp(comment + name_length,
- char_tnum, char_tnum_length) == 0)
- name_length = name_length + char_tnum_length;
- }
+ return nullptr;
if (comment[name_length] == '=') {
*length_r = entry->length - name_length - 1;
return comment + name_length + 1;
}
- return NULL;
+ return nullptr;
}
/**
@@ -167,14 +156,13 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
static bool
flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const char *name, enum tag_type tag_type,
- const char *char_tnum,
const struct tag_handler *handler, void *handler_ctx)
{
const char *value;
size_t value_length;
- value = flac_comment_value(entry, name, char_tnum, &value_length);
- if (value != NULL) {
+ value = flac_comment_value(entry, name, &value_length);
+ if (value != nullptr) {
char *p = g_strndup(value, value_length);
tag_handler_invoke_tag(handler, handler_ctx, tag_type, p);
g_free(p);
@@ -184,23 +172,15 @@ flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
return false;
}
-static const struct tag_table flac_tags[] = {
- { "tracknumber", TAG_TRACK },
- { "discnumber", TAG_DISC },
- { "album artist", TAG_ALBUM_ARTIST },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
static void
-flac_scan_comment(const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment_Entry *entry,
+flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
const struct tag_handler *handler, void *handler_ctx)
{
- if (handler->pair != NULL) {
+ if (handler->pair != nullptr) {
char *name = g_strdup((const char*)entry->entry);
char *value = strchr(name, '=');
- if (value != NULL && value > name) {
+ if (value != nullptr && value > name) {
*value++ = 0;
tag_handler_invoke_pair(handler, handler_ctx,
name, value);
@@ -209,36 +189,34 @@ flac_scan_comment(const char *char_tnum,
g_free(name);
}
- for (const struct tag_table *i = flac_tags; i->name != NULL; ++i)
- if (flac_copy_comment(entry, i->name, i->type, char_tnum,
+ for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)
+ if (flac_copy_comment(entry, i->name, i->type,
handler, handler_ctx))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (flac_copy_comment(entry,
- tag_item_names[i], i, char_tnum,
+ tag_item_names[i], (enum tag_type)i,
handler, handler_ctx))
return;
}
static void
-flac_scan_comments(const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment *comment,
+flac_scan_comments(const FLAC__StreamMetadata_VorbisComment *comment,
const struct tag_handler *handler, void *handler_ctx)
{
for (unsigned i = 0; i < comment->num_comments; ++i)
- flac_scan_comment(char_tnum, &comment->comments[i],
+ flac_scan_comment(&comment->comments[i],
handler, handler_ctx);
}
void
-flac_scan_metadata(const char *track,
- const FLAC__StreamMetadata *block,
+flac_scan_metadata(const FLAC__StreamMetadata *block,
const struct tag_handler *handler, void *handler_ctx)
{
switch (block->type) {
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
- flac_scan_comments(track, &block->data.vorbis_comment,
+ flac_scan_comments(&block->data.vorbis_comment,
handler, handler_ctx);
break;
@@ -254,70 +232,22 @@ flac_scan_metadata(const char *track,
}
void
-flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
+flac_vorbis_comments_to_tag(struct tag *tag,
const FLAC__StreamMetadata_VorbisComment *comment)
{
- flac_scan_comments(char_tnum, comment,
- &add_tag_handler, tag);
+ flac_scan_comments(comment, &add_tag_handler, tag);
}
-bool
-flac_scan_file2(const char *file, const char *char_tnum,
- const struct tag_handler *handler, void *handler_ctx)
+void
+FLACMetadataChain::Scan(const struct tag_handler *handler, void *handler_ctx)
{
- FLAC__Metadata_SimpleIterator *it;
- FLAC__StreamMetadata *block = NULL;
-
- it = FLAC__metadata_simple_iterator_new();
- if (!FLAC__metadata_simple_iterator_init(it, file, 1, 0)) {
- const char *err;
- FLAC_API FLAC__Metadata_SimpleIteratorStatus s;
-
- s = FLAC__metadata_simple_iterator_status(it);
-
- switch (s) { /* slightly more human-friendly messages: */
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT:
- err = "illegal input";
- break;
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE:
- err = "error opening file";
- break;
- case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE:
- err = "not a FLAC file";
- break;
- default:
- err = FLAC__Metadata_SimpleIteratorStatusString[s];
- }
- g_debug("Reading '%s' metadata gave the following error: %s\n",
- file, err);
- FLAC__metadata_simple_iterator_delete(it);
- return false;
- }
+ FLACMetadataIterator iterator(*this);
do {
- block = FLAC__metadata_simple_iterator_get_block(it);
- if (!block)
+ FLAC__StreamMetadata *block = iterator.GetBlock();
+ if (block == nullptr)
break;
- flac_scan_metadata(char_tnum, block, handler, handler_ctx);
- FLAC__metadata_object_delete(block);
- } while (FLAC__metadata_simple_iterator_next(it));
-
- FLAC__metadata_simple_iterator_delete(it);
-
- return true;
-}
-
-struct tag *
-flac_tag_load(const char *file, const char *char_tnum)
-{
- struct tag *tag = tag_new();
-
- if (!flac_scan_file2(file, char_tnum, &add_tag_handler, tag) ||
- tag_is_empty(tag)) {
- tag_free(tag);
- tag = NULL;
- }
-
- return tag;
+ flac_scan_metadata(block, handler, handler_ctx);
+ } while (iterator.Next());
}
diff --git a/src/decoder/FLACMetaData.hxx b/src/decoder/FLACMetaData.hxx
new file mode 100644
index 00000000..0eceec23
--- /dev/null
+++ b/src/decoder/FLACMetaData.hxx
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2003-2012 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_FLAC_METADATA_H
+#define MPD_FLAC_METADATA_H
+
+#include "gcc.h"
+#include "FLACIOHandle.hxx"
+
+#include <FLAC/metadata.h>
+
+#include <assert.h>
+
+class FLACMetadataChain {
+ FLAC__Metadata_Chain *chain;
+
+public:
+ FLACMetadataChain():chain(::FLAC__metadata_chain_new()) {}
+
+ ~FLACMetadataChain() {
+ ::FLAC__metadata_chain_delete(chain);
+ }
+
+ explicit operator FLAC__Metadata_Chain *() {
+ return chain;
+ }
+
+ bool Read(const char *path) {
+ return ::FLAC__metadata_chain_read(chain, path);
+ }
+
+ bool Read(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) {
+ return ::FLAC__metadata_chain_read_with_callbacks(chain,
+ handle,
+ callbacks);
+ }
+
+ bool Read(input_stream *is) {
+ return Read(::ToFLACIOHandle(is), ::GetFLACIOCallbacks(is));
+ }
+
+ bool ReadOgg(const char *path) {
+ return ::FLAC__metadata_chain_read_ogg(chain, path);
+ }
+
+ bool ReadOgg(FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) {
+ return ::FLAC__metadata_chain_read_ogg_with_callbacks(chain,
+ handle,
+ callbacks);
+ }
+
+ bool ReadOgg(input_stream *is) {
+ return ReadOgg(::ToFLACIOHandle(is), ::GetFLACIOCallbacks(is));
+ }
+
+ gcc_pure
+ FLAC__Metadata_ChainStatus GetStatus() const {
+ return ::FLAC__metadata_chain_status(chain);
+ }
+
+ gcc_pure
+ const char *GetStatusString() const {
+ return FLAC__Metadata_ChainStatusString[GetStatus()];
+ }
+
+ void Scan(const struct tag_handler *handler, void *handler_ctx);
+};
+
+class FLACMetadataIterator {
+ FLAC__Metadata_Iterator *iterator;
+
+public:
+ FLACMetadataIterator():iterator(::FLAC__metadata_iterator_new()) {}
+
+ FLACMetadataIterator(FLACMetadataChain &chain)
+ :iterator(::FLAC__metadata_iterator_new()) {
+ ::FLAC__metadata_iterator_init(iterator,
+ (FLAC__Metadata_Chain *)chain);
+ }
+
+ ~FLACMetadataIterator() {
+ ::FLAC__metadata_iterator_delete(iterator);
+ }
+
+ bool Next() {
+ return ::FLAC__metadata_iterator_next(iterator);
+ }
+
+ gcc_pure
+ FLAC__StreamMetadata *GetBlock() {
+ return ::FLAC__metadata_iterator_get_block(iterator);
+ }
+};
+
+struct tag_handler;
+struct tag;
+struct replay_gain_info;
+
+static inline unsigned
+flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
+{
+ assert(stream_info->sample_rate > 0);
+
+ return (stream_info->total_samples + stream_info->sample_rate - 1) /
+ stream_info->sample_rate;
+}
+
+bool
+flac_parse_replay_gain(struct replay_gain_info *rgi,
+ const FLAC__StreamMetadata *block);
+
+bool
+flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
+ const FLAC__StreamMetadata *block);
+
+void
+flac_vorbis_comments_to_tag(struct tag *tag,
+ const FLAC__StreamMetadata_VorbisComment *comment);
+
+void
+flac_scan_metadata(const FLAC__StreamMetadata *block,
+ const struct tag_handler *handler, void *handler_ctx);
+
+#endif
diff --git a/src/decoder/flac_pcm.c b/src/decoder/FLAC_PCM.cxx
index 6964d8ac..303530aa 100644
--- a/src/decoder/flac_pcm.c
+++ b/src/decoder/FLAC_PCM.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
*/
#include "config.h"
-#include "flac_pcm.h"
+#include "FLAC_PCM.hxx"
#include <assert.h>
diff --git a/src/decoder/flac_pcm.h b/src/decoder/FLAC_PCM.hxx
index a931998c..97d214c1 100644
--- a/src/decoder/flac_pcm.h
+++ b/src/decoder/FLAC_PCM.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2012 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_FLAC_PCM_H
-#define MPD_FLAC_PCM_H
+#ifndef MPD_FLAC_PCM_HXX
+#define MPD_FLAC_PCM_HXX
#include "audio_format.h"
diff --git a/src/decoder/ffmpeg_decoder_plugin.c b/src/decoder/FfmpegDecoderPlugin.cxx
index 4c4cb2b8..dd98b968 100644
--- a/src/decoder/ffmpeg_decoder_plugin.c
+++ b/src/decoder/FfmpegDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,11 +17,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+/* necessary because libavutil/common.h uses UINT64_C */
+#define __STDC_CONSTANT_MACROS
+
#include "config.h"
+#include "FfmpegDecoderPlugin.hxx"
#include "decoder_api.h"
-#include "audio_check.h"
-#include "ffmpeg_metadata.h"
+#include "FfmpegMetaData.hxx"
#include "tag_handler.h"
+#include "InputStream.hxx"
+
+extern "C" {
+#include "audio_check.h"
+}
#include <glib.h>
@@ -34,15 +42,15 @@
#include <sys/stat.h>
#include <unistd.h>
+extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavutil/mathematics.h>
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
#include <libavutil/dict.h>
-#endif
+}
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "ffmpeg"
@@ -82,18 +90,15 @@ struct mpd_ffmpeg_stream {
struct decoder *decoder;
struct input_stream *input;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
AVIOContext *io;
-#else
- ByteIOContext *io;
-#endif
+
unsigned char buffer[8192];
};
static int
mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
{
- struct mpd_ffmpeg_stream *stream = opaque;
+ struct mpd_ffmpeg_stream *stream = (struct mpd_ffmpeg_stream *)opaque;
return decoder_read(stream->decoder, stream->input,
(void *)buf, size);
@@ -102,7 +107,7 @@ mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
static int64_t
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
{
- struct mpd_ffmpeg_stream *stream = opaque;
+ struct mpd_ffmpeg_stream *stream = (struct mpd_ffmpeg_stream *)opaque;
if (whence == AVSEEK_SIZE)
return stream->input->size;
@@ -119,19 +124,11 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
stream->decoder = decoder;
stream->input = input;
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
stream->io = avio_alloc_context(stream->buffer, sizeof(stream->buffer),
false, stream,
mpd_ffmpeg_stream_read, NULL,
input->seekable
? mpd_ffmpeg_stream_seek : NULL);
-#else
- stream->io = av_alloc_put_byte(stream->buffer, sizeof(stream->buffer),
- false, stream,
- mpd_ffmpeg_stream_read, NULL,
- input->seekable
- ? mpd_ffmpeg_stream_seek : NULL);
-#endif
if (stream->io == NULL) {
g_free(stream);
return NULL;
@@ -146,15 +143,10 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
*/
static int
mpd_ffmpeg_open_input(AVFormatContext **ic_ptr,
-#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
AVIOContext *pb,
-#else
- ByteIOContext *pb,
-#endif
const char *filename,
AVInputFormat *fmt)
{
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,1,3)
AVFormatContext *context = avformat_alloc_context();
if (context == NULL)
return AVERROR(ENOMEM);
@@ -162,9 +154,6 @@ mpd_ffmpeg_open_input(AVFormatContext **ic_ptr,
context->pb = pb;
*ic_ptr = context;
return avformat_open_input(ic_ptr, filename, fmt, NULL);
-#else
- return av_open_input_stream(ic_ptr, pb, filename, fmt, NULL);
-#endif
}
static void
@@ -188,11 +177,7 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
{
for (unsigned i = 0; i < format_context->nb_streams; ++i)
if (format_context->streams[i]->codec->codec_type ==
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 64, 0)
AVMEDIA_TYPE_AUDIO)
-#else
- CODEC_TYPE_AUDIO)
-#endif
return i;
return -1;
@@ -291,12 +276,7 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
decoder_timestamp(decoder,
time_from_ffmpeg(packet->pts, *time_base));
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
AVPacket packet2 = *packet;
-#else
- const uint8_t *packet_data = packet->data;
- int packet_size = packet->size;
-#endif
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
uint8_t aligned_buffer[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
@@ -305,16 +285,11 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
/* libavcodec < 0.8 needs an aligned buffer */
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
size_t buffer_size = sizeof(audio_buf);
- int16_t *aligned_buffer = align16(audio_buf, &buffer_size);
+ int16_t *aligned_buffer = (int16_t *)align16(audio_buf, &buffer_size);
#endif
enum decoder_command cmd = DECODE_COMMAND_NONE;
- while (
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
- packet2.size > 0 &&
-#else
- packet_size > 0 &&
-#endif
+ while (packet2.size > 0 &&
cmd == DECODE_COMMAND_NONE) {
int audio_size = buffer_size;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
@@ -332,14 +307,10 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
len = audio_size;
} else if (len >= 0)
len = -1;
-#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
+#else
int len = avcodec_decode_audio3(codec_context,
aligned_buffer, &audio_size,
&packet2);
-#else
- int len = avcodec_decode_audio2(codec_context,
- aligned_buffer, &audio_size,
- packet_data, packet_size);
#endif
if (len < 0) {
@@ -348,13 +319,8 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
break;
}
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
packet2.data += len;
packet2.size -= len;
-#else
- packet_data += len;
- packet_size -= len;
-#endif
if (audio_size <= 0)
continue;
@@ -366,33 +332,21 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
return cmd;
}
-#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 94, 1)
-#define AVSampleFormat SampleFormat
-#endif
-
G_GNUC_CONST
static enum sample_format
ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
{
switch (sample_fmt) {
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S16:
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
case AV_SAMPLE_FMT_S16P:
#endif
-#else
- case SAMPLE_FMT_S16:
-#endif
return SAMPLE_FORMAT_S16;
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S32:
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
case AV_SAMPLE_FMT_S32P:
#endif
-#else
- case SAMPLE_FMT_S32:
-#endif
return SAMPLE_FORMAT_S32;
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
@@ -404,7 +358,6 @@ ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
break;
}
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
char buffer[64];
const char *name = av_get_sample_fmt_string(buffer, sizeof(buffer),
sample_fmt);
@@ -412,7 +365,6 @@ ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
g_warning("Unsupported libavcodec SampleFormat value: %s (%d)",
name, sample_fmt);
else
-#endif
g_warning("Unsupported libavcodec SampleFormat value: %d",
sample_fmt);
return SAMPLE_FORMAT_UNDEFINED;
@@ -426,7 +378,7 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
PADDING = 16,
};
- unsigned char *buffer = g_malloc(BUFFER_SIZE);
+ unsigned char *buffer = (unsigned char *)g_malloc(BUFFER_SIZE);
size_t nbytes = decoder_read(decoder, is, buffer, BUFFER_SIZE);
if (nbytes <= PADDING ||
!input_stream_lock_seek(is, 0, SEEK_SET, NULL)) {
@@ -440,11 +392,10 @@ ffmpeg_probe(struct decoder *decoder, struct input_stream *is)
size */
nbytes -= PADDING;
- AVProbeData avpd = {
- .buf = buffer,
- .buf_size = nbytes,
- .filename = is->uri,
- };
+ AVProbeData avpd;
+ avpd.buf = buffer;
+ avpd.buf_size = nbytes;
+ avpd.filename = is->uri.c_str();
AVInputFormat *format = av_probe_input_format(&avpd, true);
g_free(buffer);
@@ -471,7 +422,8 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
//ffmpeg works with ours "fileops" helper
AVFormatContext *format_context = NULL;
- if (mpd_ffmpeg_open_input(&format_context, stream->io, input->uri,
+ if (mpd_ffmpeg_open_input(&format_context, stream->io,
+ input->uri.c_str(),
input_format) != 0) {
g_warning("Open failed\n");
mpd_ffmpeg_stream_close(stream);
@@ -630,7 +582,7 @@ ffmpeg_scan_stream(struct input_stream *is,
return false;
AVFormatContext *f = NULL;
- if (mpd_ffmpeg_open_input(&f, stream->io, is->uri,
+ if (mpd_ffmpeg_open_input(&f, stream->io, is->uri.c_str(),
input_format) != 0) {
mpd_ffmpeg_stream_close(stream);
return false;
@@ -656,10 +608,6 @@ ffmpeg_scan_stream(struct input_stream *is,
tag_handler_invoke_duration(handler, handler_ctx,
f->duration / AV_TIME_BASE);
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,101,0)
- av_metadata_conv(f, NULL, f->iformat->metadata_conv);
-#endif
-
ffmpeg_scan_dictionary(f->metadata, handler, handler_ctx);
int idx = ffmpeg_find_audio_stream(f);
if (idx >= 0)
@@ -791,10 +739,14 @@ static const char *const ffmpeg_mime_types[] = {
};
const struct decoder_plugin ffmpeg_decoder_plugin = {
- .name = "ffmpeg",
- .init = ffmpeg_init,
- .stream_decode = ffmpeg_decode,
- .scan_stream = ffmpeg_scan_stream,
- .suffixes = ffmpeg_suffixes,
- .mime_types = ffmpeg_mime_types
+ "ffmpeg",
+ ffmpeg_init,
+ nullptr,
+ ffmpeg_decode,
+ nullptr,
+ nullptr,
+ ffmpeg_scan_stream,
+ nullptr,
+ ffmpeg_suffixes,
+ ffmpeg_mime_types
};
diff --git a/src/decoder/FfmpegDecoderPlugin.hxx b/src/decoder/FfmpegDecoderPlugin.hxx
new file mode 100644
index 00000000..9a637fff
--- /dev/null
+++ b/src/decoder/FfmpegDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_DECODER_FFMPEG_HXX
+#define MPD_DECODER_FFMPEG_HXX
+
+extern const struct decoder_plugin ffmpeg_decoder_plugin;
+
+#endif
diff --git a/src/decoder/ffmpeg_metadata.c b/src/decoder/FfmpegMetaData.cxx
index 3ef774f6..2d7ebbca 100644
--- a/src/decoder/ffmpeg_metadata.c
+++ b/src/decoder/FfmpegMetaData.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+/* necessary because libavutil/common.h uses UINT64_C */
+#define __STDC_CONSTANT_MACROS
+
#include "config.h"
-#include "ffmpeg_metadata.h"
+#include "FfmpegMetaData.hxx"
#include "tag_table.h"
#include "tag_handler.h"
@@ -26,9 +29,6 @@
#define G_LOG_DOMAIN "ffmpeg"
static const struct tag_table ffmpeg_tags[] = {
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,50,0)
- { "author", TAG_ARTIST },
-#endif
{ "year", TAG_DATE },
{ "author-sort", TAG_ARTIST_SORT },
{ "album_artist", TAG_ALBUM_ARTIST },
@@ -50,8 +50,6 @@ ffmpeg_copy_metadata(enum tag_type type,
type, mt->value);
}
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
-
static void
ffmpeg_scan_pairs(AVDictionary *dict,
const struct tag_handler *handler, void *handler_ctx)
@@ -63,14 +61,12 @@ ffmpeg_scan_pairs(AVDictionary *dict,
i->key, i->value);
}
-#endif
-
void
ffmpeg_scan_dictionary(AVDictionary *dict,
const struct tag_handler *handler, void *handler_ctx)
{
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
- ffmpeg_copy_metadata(i, dict, tag_item_names[i],
+ ffmpeg_copy_metadata(tag_type(i), dict, tag_item_names[i],
handler, handler_ctx);
for (const struct tag_table *i = ffmpeg_tags;
@@ -78,8 +74,6 @@ ffmpeg_scan_dictionary(AVDictionary *dict,
ffmpeg_copy_metadata(i->type, dict, i->name,
handler, handler_ctx);
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
if (handler->pair != NULL)
ffmpeg_scan_pairs(dict, handler, handler_ctx);
-#endif
}
diff --git a/src/decoder/ffmpeg_metadata.h b/src/decoder/FfmpegMetaData.hxx
index 60658f47..466d2cb1 100644
--- a/src/decoder/ffmpeg_metadata.h
+++ b/src/decoder/FfmpegMetaData.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,20 +17,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_FFMPEG_METADATA_H
-#define MPD_FFMPEG_METADATA_H
+#ifndef MPD_FFMPEG_METADATA_HXX
+#define MPD_FFMPEG_METADATA_HXX
+extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
-#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
#include <libavutil/dict.h>
-#endif
-
-#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,1,0)
-#define AVDictionary AVMetadata
-#define AVDictionaryEntry AVMetadataTag
-#define av_dict_get av_metadata_get
-#endif
+}
struct tag_handler;
diff --git a/src/decoder/_ogg_common.c b/src/decoder/OggCodec.cxx
index 09d2712d..5ad9c69d 100644
--- a/src/decoder/_ogg_common.c
+++ b/src/decoder/OggCodec.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,25 +22,27 @@
*/
#include "config.h"
-#include "_ogg_common.h"
+#include "OggCodec.hxx"
-ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream)
+enum ogg_codec
+ogg_codec_detect(struct decoder *decoder, struct input_stream *is)
{
/* oggflac detection based on code in ogg123 and this post
* http://lists.xiph.org/pipermail/flac/2004-December/000393.html
* ogg123 trunk still doesn't have this patch as of June 2005 */
unsigned char buf[41];
- size_t r;
-
- r = decoder_read(NULL, inStream, buf, sizeof(buf));
+ size_t r = decoder_read(decoder, is, buf, sizeof(buf));
if (r < sizeof(buf) || memcmp(buf, "OggS", 4) != 0)
- return VORBIS;
+ return OGG_CODEC_UNKNOWN;
if ((memcmp(buf + 29, "FLAC", 4) == 0 &&
memcmp(buf + 37, "fLaC", 4) == 0) ||
memcmp(buf + 28, "FLAC", 4) == 0 ||
memcmp(buf + 28, "fLaC", 4) == 0)
- return FLAC;
+ return OGG_CODEC_FLAC;
+
+ if (memcmp(buf + 28, "Opus", 4) == 0)
+ return OGG_CODEC_OPUS;
- return VORBIS;
+ return OGG_CODEC_VORBIS;
}
diff --git a/src/decoder/_ogg_common.h b/src/decoder/OggCodec.hxx
index 85e4ebba..e241560f 100644
--- a/src/decoder/_ogg_common.h
+++ b/src/decoder/OggCodec.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,13 +21,19 @@
* Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC)
*/
-#ifndef MPD_OGG_COMMON_H
-#define MPD_OGG_COMMON_H
+#ifndef MPD_OGG_CODEC_HXX
+#define MPD_OGG_CODEC_HXX
#include "decoder_api.h"
-typedef enum _ogg_stream_type { VORBIS, FLAC } ogg_stream_type;
+enum ogg_codec {
+ OGG_CODEC_UNKNOWN,
+ OGG_CODEC_VORBIS,
+ OGG_CODEC_FLAC,
+ OGG_CODEC_OPUS,
+};
-ogg_stream_type ogg_stream_type_detect(struct input_stream *inStream);
+enum ogg_codec
+ogg_codec_detect(struct decoder *decoder, struct input_stream *is);
#endif /* _OGG_COMMON_H */
diff --git a/src/decoder/OggFind.cxx b/src/decoder/OggFind.cxx
new file mode 100644
index 00000000..9df4c645
--- /dev/null
+++ b/src/decoder/OggFind.cxx
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2003-2013 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 "OggFind.hxx"
+#include "OggSyncState.hxx"
+
+bool
+OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet)
+{
+ while (true) {
+ int r = ogg_stream_packetout(&os, &packet);
+ if (r == 0) {
+ if (!oy.ExpectPageIn(os))
+ return false;
+
+ continue;
+ } else if (r > 0 && packet.e_o_s)
+ return true;
+ }
+}
diff --git a/src/decoder/OggFind.hxx b/src/decoder/OggFind.hxx
new file mode 100644
index 00000000..7d18d206
--- /dev/null
+++ b/src/decoder/OggFind.hxx
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2003-2013 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_OGG_FIND_HXX
+#define MPD_OGG_FIND_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+class OggSyncState;
+
+/**
+ * Skip all pages/packets until an end-of-stream (EOS) packet for the
+ * specified stream is found.
+ *
+ * @return true if the EOS packet was found
+ */
+bool
+OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet);
+
+#endif
diff --git a/src/decoder/OggSyncState.hxx b/src/decoder/OggSyncState.hxx
new file mode 100644
index 00000000..eaeb9bd8
--- /dev/null
+++ b/src/decoder/OggSyncState.hxx
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2003-2013 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_OGG_SYNC_STATE_HXX
+#define MPD_OGG_SYNC_STATE_HXX
+
+#include "check.h"
+#include "OggUtil.hxx"
+
+#include <ogg/ogg.h>
+
+#include <stddef.h>
+
+/**
+ * Wrapper for an ogg_sync_state.
+ */
+class OggSyncState {
+ ogg_sync_state oy;
+
+ input_stream &is;
+ struct decoder *const decoder;
+
+public:
+ OggSyncState(input_stream &_is, struct decoder *const _decoder=nullptr)
+ :is(_is), decoder(_decoder) {
+ ogg_sync_init(&oy);
+ }
+
+ ~OggSyncState() {
+ ogg_sync_clear(&oy);
+ }
+
+ void Reset() {
+ ogg_sync_reset(&oy);
+ }
+
+ bool Feed(size_t size) {
+ return OggFeed(oy, decoder, &is, size);
+ }
+
+ bool ExpectPage(ogg_page &page) {
+ return OggExpectPage(oy, page, decoder, &is);
+ }
+
+ bool ExpectFirstPage(ogg_stream_state &os) {
+ return OggExpectFirstPage(oy, os, decoder, &is);
+ }
+
+ bool ExpectPageIn(ogg_stream_state &os) {
+ return OggExpectPageIn(oy, os, decoder, &is);
+ }
+
+ bool ExpectPageSeek(ogg_page &page) {
+ return OggExpectPageSeek(oy, page, decoder, &is);
+ }
+
+ bool ExpectPageSeekIn(ogg_stream_state &os) {
+ return OggExpectPageSeekIn(oy, os, decoder, &is);
+ }
+};
+
+#endif
diff --git a/src/decoder/OggUtil.cxx b/src/decoder/OggUtil.cxx
new file mode 100644
index 00000000..a1125a2c
--- /dev/null
+++ b/src/decoder/OggUtil.cxx
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2003-2012 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 "OggUtil.hxx"
+#include "decoder_api.h"
+
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder,
+ input_stream *input_stream, size_t size)
+{
+ char *buffer = ogg_sync_buffer(&oy, size);
+ if (buffer == nullptr)
+ return false;
+
+ size_t nbytes = decoder_read(decoder, input_stream,
+ buffer, size);
+ if (nbytes == 0)
+ return false;
+
+ ogg_sync_wrote(&oy, nbytes);
+ return true;
+}
+
+bool
+OggExpectPage(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream)
+{
+ while (true) {
+ int r = ogg_sync_pageout(&oy, &page);
+ if (r != 0)
+ return r > 0;
+
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ return false;
+ }
+}
+
+bool
+OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPage(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_init(&os, ogg_page_serialno(&page));
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+bool
+OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPage(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+bool
+OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream)
+{
+ size_t remaining_skipped = 16384;
+
+ while (true) {
+ int r = ogg_sync_pageseek(&oy, &page);
+ if (r > 0)
+ return true;
+
+ if (r < 0) {
+ /* skipped -r bytes */
+ size_t nbytes = -r;
+ if (nbytes > remaining_skipped)
+ /* still no ogg page - we lost our
+ patience, abort */
+ return false;
+
+ remaining_skipped -= nbytes;
+ continue;
+ }
+
+ if (!OggFeed(oy, decoder, input_stream, 1024))
+ return false;
+ }
+}
+
+bool
+OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is)
+{
+ ogg_page page;
+ if (!OggExpectPageSeek(oy, page, decoder, is))
+ return false;
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
diff --git a/src/decoder/OggUtil.hxx b/src/decoder/OggUtil.hxx
new file mode 100644
index 00000000..32479781
--- /dev/null
+++ b/src/decoder/OggUtil.hxx
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2003-2012 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_OGG_UTIL_HXX
+#define MPD_OGG_UTIL_HXX
+
+#include "check.h"
+
+#include <ogg/ogg.h>
+
+#include <stddef.h>
+
+struct input_stream;
+struct decoder;
+
+/**
+ * Feed data from the #input_stream into the #ogg_sync_state.
+ *
+ * @return false on error or end-of-file
+ */
+bool
+OggFeed(ogg_sync_state &oy, struct decoder *decoder, input_stream *is,
+ size_t size);
+
+/**
+ * Feed into the #ogg_sync_state until a page gets available. Garbage
+ * data at the beginning is considered a fatal error.
+ *
+ * @return true if a page is available
+ */
+bool
+OggExpectPage(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream);
+
+/**
+ * Combines OggExpectPage(), ogg_stream_init() and
+ * ogg_stream_pagein().
+ *
+ * @return true if the stream was initialized and the first page was
+ * delivered to it
+ */
+bool
+OggExpectFirstPage(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+/**
+ * Combines OggExpectPage() and ogg_stream_pagein().
+ *
+ * @return true if a page was delivered to the stream
+ */
+bool
+OggExpectPageIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+/**
+ * Like OggExpectPage(), but allow skipping garbage (after seeking).
+ */
+bool
+OggExpectPageSeek(ogg_sync_state &oy, ogg_page &page,
+ decoder *decoder, input_stream *input_stream);
+
+/**
+ * Combines OggExpectPageSeek() and ogg_stream_pagein().
+ *
+ * @return true if a page was delivered to the stream
+ */
+bool
+OggExpectPageSeekIn(ogg_sync_state &oy, ogg_stream_state &os,
+ decoder *decoder, input_stream *is);
+
+#endif
diff --git a/src/decoder/OpusDecoderPlugin.cxx b/src/decoder/OpusDecoderPlugin.cxx
new file mode 100644
index 00000000..3e3a1e4e
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.cxx
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2003-2012 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" /* must be first for large file support */
+#include "OpusDecoderPlugin.h"
+#include "OpusHead.hxx"
+#include "OpusTags.hxx"
+#include "OggUtil.hxx"
+#include "OggFind.hxx"
+#include "OggSyncState.hxx"
+#include "decoder_api.h"
+#include "OggCodec.hxx"
+#include "audio_check.h"
+#include "tag_handler.h"
+#include "InputStream.hxx"
+
+#include <opus.h>
+#include <ogg/ogg.h>
+
+#include <glib.h>
+
+#include <stdio.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "opus"
+
+static const opus_int32 opus_sample_rate = 48000;
+
+gcc_pure
+static bool
+IsOpusHead(const ogg_packet &packet)
+{
+ return packet.bytes >= 8 && memcmp(packet.packet, "OpusHead", 8) == 0;
+}
+
+gcc_pure
+static bool
+IsOpusTags(const ogg_packet &packet)
+{
+ return packet.bytes >= 8 && memcmp(packet.packet, "OpusTags", 8) == 0;
+}
+
+static bool
+mpd_opus_init(G_GNUC_UNUSED const struct config_param *param)
+{
+ g_debug("%s", opus_get_version_string());
+
+ return true;
+}
+
+class MPDOpusDecoder {
+ struct decoder *decoder;
+ struct input_stream *input_stream;
+
+ ogg_stream_state os;
+
+ OpusDecoder *opus_decoder;
+ opus_int16 *output_buffer;
+ unsigned output_size;
+
+ bool os_initialized;
+ bool found_opus;
+
+ int opus_serialno;
+
+ size_t frame_size;
+
+public:
+ MPDOpusDecoder(struct decoder *_decoder,
+ struct input_stream *_input_stream)
+ :decoder(_decoder), input_stream(_input_stream),
+ opus_decoder(nullptr),
+ output_buffer(nullptr), output_size(0),
+ os_initialized(false), found_opus(false) {}
+ ~MPDOpusDecoder();
+
+ bool ReadFirstPage(OggSyncState &oy);
+ bool ReadNextPage(OggSyncState &oy);
+
+ enum decoder_command HandlePackets();
+ enum decoder_command HandlePacket(const ogg_packet &packet);
+ enum decoder_command HandleBOS(const ogg_packet &packet);
+ enum decoder_command HandleTags(const ogg_packet &packet);
+ enum decoder_command HandleAudio(const ogg_packet &packet);
+};
+
+MPDOpusDecoder::~MPDOpusDecoder()
+{
+ g_free(output_buffer);
+
+ if (opus_decoder != nullptr)
+ opus_decoder_destroy(opus_decoder);
+
+ if (os_initialized)
+ ogg_stream_clear(&os);
+}
+
+inline bool
+MPDOpusDecoder::ReadFirstPage(OggSyncState &oy)
+{
+ assert(!os_initialized);
+
+ if (!oy.ExpectFirstPage(os))
+ return false;
+
+ os_initialized = true;
+ return true;
+}
+
+inline bool
+MPDOpusDecoder::ReadNextPage(OggSyncState &oy)
+{
+ assert(os_initialized);
+
+ ogg_page page;
+ if (!oy.ExpectPage(page))
+ return false;
+
+ const auto page_serialno = ogg_page_serialno(&page);
+ if (page_serialno != os.serialno)
+ ogg_stream_reset_serialno(&os, page_serialno);
+
+ ogg_stream_pagein(&os, &page);
+ return true;
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandlePackets()
+{
+ ogg_packet packet;
+ while (ogg_stream_packetout(&os, &packet) == 1) {
+ enum decoder_command cmd = HandlePacket(packet);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+ }
+
+ return DECODE_COMMAND_NONE;
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandlePacket(const ogg_packet &packet)
+{
+ if (packet.e_o_s)
+ return DECODE_COMMAND_STOP;
+
+ if (packet.b_o_s)
+ return HandleBOS(packet);
+ else if (!found_opus)
+ return DECODE_COMMAND_STOP;
+
+ if (IsOpusTags(packet))
+ return HandleTags(packet);
+
+ return HandleAudio(packet);
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
+{
+ assert(packet.b_o_s);
+
+ if (found_opus || !IsOpusHead(packet))
+ return DECODE_COMMAND_STOP;
+
+ unsigned channels;
+ if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
+ !audio_valid_channel_count(channels))
+ return DECODE_COMMAND_STOP;
+
+ assert(opus_decoder == nullptr);
+ assert(output_buffer == nullptr);
+
+ opus_serialno = os.serialno;
+ found_opus = true;
+
+ /* TODO: parse attributes from the OpusHead (sample rate,
+ channels, ...) */
+
+ int opus_error;
+ opus_decoder = opus_decoder_create(opus_sample_rate, channels,
+ &opus_error);
+ if (opus_decoder == nullptr) {
+ g_warning("libopus error: %s",
+ opus_strerror(opus_error));
+ return DECODE_COMMAND_STOP;
+ }
+
+ struct audio_format audio_format;
+ audio_format_init(&audio_format, opus_sample_rate,
+ SAMPLE_FORMAT_S16, channels);
+ decoder_initialized(decoder, &audio_format, false, -1);
+ frame_size = audio_format_frame_size(&audio_format);
+
+ /* allocate an output buffer for 16 bit PCM samples big enough
+ to hold a quarter second, larger than 120ms required by
+ libopus */
+ output_size = audio_format.sample_rate / 4;
+ output_buffer = (opus_int16 *)
+ g_malloc(sizeof(*output_buffer) * output_size *
+ audio_format.channels);
+
+ return decoder_get_command(decoder);
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandleTags(const ogg_packet &packet)
+{
+ struct tag *tag = tag_new();
+
+ enum decoder_command cmd;
+ if (ScanOpusTags(packet.packet, packet.bytes, &add_tag_handler, tag) &&
+ !tag_is_empty(tag))
+ cmd = decoder_tag(decoder, input_stream, tag);
+ else
+ cmd = decoder_get_command(decoder);
+
+ tag_free(tag);
+ return cmd;
+}
+
+inline enum decoder_command
+MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
+{
+ assert(opus_decoder != nullptr);
+
+ int nframes = opus_decode(opus_decoder,
+ (const unsigned char*)packet.packet,
+ packet.bytes,
+ output_buffer, output_size,
+ 0);
+ if (nframes < 0) {
+ g_warning("%s", opus_strerror(nframes));
+ return DECODE_COMMAND_STOP;
+ }
+
+ if (nframes > 0) {
+ const size_t nbytes = nframes * frame_size;
+ enum decoder_command cmd =
+ decoder_data(decoder, input_stream,
+ output_buffer, nbytes,
+ 0);
+ if (cmd != DECODE_COMMAND_NONE)
+ return cmd;
+ }
+
+ return DECODE_COMMAND_NONE;
+}
+
+static void
+mpd_opus_stream_decode(struct decoder *decoder,
+ struct input_stream *input_stream)
+{
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_OPUS)
+ return;
+
+ /* rewind the stream, because ogg_codec_detect() has
+ moved it */
+ input_stream_lock_seek(input_stream, 0, SEEK_SET, nullptr);
+
+ MPDOpusDecoder d(decoder, input_stream);
+ OggSyncState oy(*input_stream, decoder);
+
+ if (!d.ReadFirstPage(oy))
+ return;
+
+ while (true) {
+ enum decoder_command cmd = d.HandlePackets();
+ if (cmd != DECODE_COMMAND_NONE)
+ break;
+
+ if (!d.ReadNextPage(oy))
+ break;
+
+ }
+}
+
+static bool
+SeekFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet,
+ input_stream *is)
+{
+ if (is->size > 0 && is->size - is->offset < 65536)
+ return OggFindEOS(oy, os, packet);
+
+ if (!input_stream_cheap_seeking(is))
+ return false;
+
+ oy.Reset();
+
+ return input_stream_lock_seek(is, -65536, SEEK_END, nullptr) &&
+ oy.ExpectPageSeekIn(os) &&
+ OggFindEOS(oy, os, packet);
+}
+
+static bool
+mpd_opus_scan_stream(struct input_stream *is,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ OggSyncState oy(*is);
+
+ ogg_stream_state os;
+ if (!oy.ExpectFirstPage(os))
+ return false;
+
+ /* read at most two more pages */
+ unsigned remaining_pages = 2;
+
+ bool result = false;
+
+ ogg_packet packet;
+ while (true) {
+ int r = ogg_stream_packetout(&os, &packet);
+ if (r < 0) {
+ result = false;
+ break;
+ }
+
+ if (r == 0) {
+ if (remaining_pages-- == 0)
+ break;
+
+ if (!oy.ExpectPageIn(os)) {
+ result = false;
+ break;
+ }
+
+ continue;
+ }
+
+ if (packet.b_o_s) {
+ if (!IsOpusHead(packet))
+ break;
+
+ unsigned channels;
+ if (!ScanOpusHeader(packet.packet, packet.bytes, channels) ||
+ !audio_valid_channel_count(channels)) {
+ result = false;
+ break;
+ }
+
+ result = true;
+ } else if (!result)
+ break;
+ else if (IsOpusTags(packet)) {
+ if (!ScanOpusTags(packet.packet, packet.bytes,
+ handler, handler_ctx))
+ result = false;
+
+ break;
+ }
+ }
+
+ if (packet.e_o_s || SeekFindEOS(oy, os, packet, is))
+ tag_handler_invoke_duration(handler, handler_ctx,
+ packet.granulepos / opus_sample_rate);
+
+ ogg_stream_clear(&os);
+
+ return result;
+}
+
+static const char *const opus_suffixes[] = {
+ "opus",
+ "ogg",
+ "oga",
+ nullptr
+};
+
+static const char *const opus_mime_types[] = {
+ "audio/opus",
+ nullptr
+};
+
+const struct decoder_plugin opus_decoder_plugin = {
+ "opus",
+ mpd_opus_init,
+ nullptr,
+ mpd_opus_stream_decode,
+ nullptr,
+ nullptr,
+ mpd_opus_scan_stream,
+ nullptr,
+ opus_suffixes,
+ opus_mime_types,
+};
diff --git a/src/decoder/OpusDecoderPlugin.h b/src/decoder/OpusDecoderPlugin.h
new file mode 100644
index 00000000..c95d6ded
--- /dev/null
+++ b/src/decoder/OpusDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 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_DECODER_OPUS_H
+#define MPD_DECODER_OPUS_H
+
+extern const struct decoder_plugin opus_decoder_plugin;
+
+#endif
diff --git a/src/decoder/OpusHead.cxx b/src/decoder/OpusHead.cxx
new file mode 100644
index 00000000..c57e08e1
--- /dev/null
+++ b/src/decoder/OpusHead.cxx
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2003-2012 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 "OpusHead.hxx"
+
+#include <stdint.h>
+#include <string.h>
+
+struct OpusHead {
+ char signature[8];
+ uint8_t version, channels;
+ uint16_t pre_skip;
+ uint32_t sample_rate;
+ uint16_t output_gain;
+ uint8_t channel_mapping;
+};
+
+bool
+ScanOpusHeader(const void *data, size_t size, unsigned &channels_r)
+{
+ const OpusHead *h = (const OpusHead *)data;
+ if (size < 19 || (h->version & 0xf0) != 0)
+ return false;
+
+ channels_r = h->channels;
+ return true;
+}
diff --git a/src/decoder/OpusHead.hxx b/src/decoder/OpusHead.hxx
new file mode 100644
index 00000000..9f75c4f7
--- /dev/null
+++ b/src/decoder/OpusHead.hxx
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2003-2012 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_OPUS_HEAD_HXX
+#define MPD_OPUS_HEAD_HXX
+
+#include "check.h"
+
+#include <stddef.h>
+
+bool
+ScanOpusHeader(const void *data, size_t size, unsigned &channels_r);
+
+#endif
diff --git a/src/decoder/OpusReader.hxx b/src/decoder/OpusReader.hxx
new file mode 100644
index 00000000..1fd07b55
--- /dev/null
+++ b/src/decoder/OpusReader.hxx
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2003-2012 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_OPUS_READER_HXX
+#define MPD_OPUS_READER_HXX
+
+#include "check.h"
+#include "string_util.h"
+
+#include <stdint.h>
+#include <string.h>
+
+class OpusReader {
+ const uint8_t *p, *const end;
+
+public:
+ OpusReader(const void *_p, size_t size)
+ :p((const uint8_t *)_p), end(p + size) {}
+
+ bool Skip(size_t length) {
+ p += length;
+ return p <= end;
+ }
+
+ const void *Read(size_t length) {
+ const uint8_t *result = p;
+ return Skip(length)
+ ? result
+ : nullptr;
+ }
+
+ bool Expect(const void *value, size_t length) {
+ const void *data = Read(length);
+ return data != nullptr && memcmp(value, data, length) == 0;
+ }
+
+ bool ReadByte(uint8_t &value_r) {
+ if (p >= end)
+ return false;
+
+ value_r = *p++;
+ return true;
+ }
+
+ bool ReadShort(uint16_t &value_r) {
+ const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
+ if (value == nullptr)
+ return false;
+
+ value_r = value[0] | (value[1] << 8);
+ return true;
+ }
+
+ bool ReadWord(uint32_t &value_r) {
+ const uint8_t *value = (const uint8_t *)Read(sizeof(value_r));
+ if (value == nullptr)
+ return false;
+
+ value_r = value[0] | (value[1] << 8)
+ | (value[2] << 16) | (value[3] << 24);
+ return true;
+ }
+
+ bool SkipString() {
+ uint32_t length;
+ return ReadWord(length) && Skip(length);
+ }
+
+ char *ReadString() {
+ uint32_t length;
+ if (!ReadWord(length))
+ return nullptr;
+
+ const char *src = (const char *)Read(length);
+ if (src == nullptr)
+ return nullptr;
+
+ return strndup(src, length);
+ }
+};
+
+#endif
diff --git a/src/decoder/OpusTags.cxx b/src/decoder/OpusTags.cxx
new file mode 100644
index 00000000..cb35a624
--- /dev/null
+++ b/src/decoder/OpusTags.cxx
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2003-2012 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 "OpusTags.hxx"
+#include "OpusReader.hxx"
+#include "XiphTags.h"
+#include "tag_handler.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+static void
+ScanOneOpusTag(const char *name, const char *value,
+ const struct tag_handler *handler, void *ctx)
+{
+ tag_handler_invoke_pair(handler, ctx, name, value);
+
+ if (handler->tag != nullptr) {
+ enum tag_type t = tag_table_lookup_i(xiph_tags, name);
+ if (t != TAG_NUM_OF_ITEM_TYPES)
+ tag_handler_invoke_tag(handler, ctx, t, value);
+ }
+}
+
+bool
+ScanOpusTags(const void *data, size_t size,
+ const struct tag_handler *handler, void *ctx)
+{
+ OpusReader r(data, size);
+ if (!r.Expect("OpusTags", 8))
+ return false;
+
+ if (handler->pair == nullptr && handler->tag == nullptr)
+ return true;
+
+ if (!r.SkipString())
+ return false;
+
+ uint32_t n;
+ if (!r.ReadWord(n))
+ return false;
+
+ while (n-- > 0) {
+ char *p = r.ReadString();
+ if (p == nullptr)
+ return false;
+
+ char *eq = strchr(p, '=');
+ if (eq != nullptr && eq > p) {
+ *eq = 0;
+
+ ScanOneOpusTag(p, eq + 1, handler, ctx);
+ }
+
+ free(p);
+ }
+
+ return true;
+}
diff --git a/src/decoder/OpusTags.hxx b/src/decoder/OpusTags.hxx
new file mode 100644
index 00000000..2f3eec84
--- /dev/null
+++ b/src/decoder/OpusTags.hxx
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2003-2012 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_OPUS_TAGS_HXX
+#define MPD_OPUS_TAGS_HXX
+
+#include "check.h"
+
+#include <stddef.h>
+
+bool
+ScanOpusTags(const void *data, size_t size,
+ const struct tag_handler *handler, void *ctx);
+
+#endif
diff --git a/src/decoder/vorbis_comments.c b/src/decoder/VorbisComments.cxx
index 6c2d57b7..10fe2236 100644
--- a/src/decoder/vorbis_comments.c
+++ b/src/decoder/VorbisComments.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,8 @@
*/
#include "config.h"
-#include "vorbis_comments.h"
+#include "VorbisComments.hxx"
+#include "XiphTags.h"
#include "tag.h"
#include "tag_table.h"
#include "tag_handler.h"
@@ -95,13 +96,6 @@ vorbis_copy_comment(const char *comment,
return false;
}
-static const struct tag_table vorbis_tags[] = {
- { "tracknumber", TAG_TRACK },
- { "discnumber", TAG_DISC },
- { "album artist", TAG_ALBUM_ARTIST },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
static void
vorbis_scan_comment(const char *comment,
const struct tag_handler *handler, void *handler_ctx)
@@ -119,14 +113,14 @@ vorbis_scan_comment(const char *comment,
g_free(name);
}
- for (const struct tag_table *i = vorbis_tags; i->name != NULL; ++i)
+ for (const struct tag_table *i = xiph_tags; i->name != NULL; ++i)
if (vorbis_copy_comment(comment, i->name, i->type,
handler, handler_ctx))
return;
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
if (vorbis_copy_comment(comment,
- tag_item_names[i], i,
+ tag_item_names[i], tag_type(i),
handler, handler_ctx))
return;
}
diff --git a/src/decoder/vorbis_comments.h b/src/decoder/VorbisComments.hxx
index c1509693..8212cac4 100644
--- a/src/decoder/vorbis_comments.h
+++ b/src/decoder/VorbisComments.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2012 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -17,8 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_VORBIS_COMMENTS_H
-#define MPD_VORBIS_COMMENTS_H
+#ifndef MPD_VORBIS_COMMENTS_HXX
+#define MPD_VORBIS_COMMENTS_HXX
#include "check.h"
diff --git a/src/decoder/vorbis_decoder_plugin.c b/src/decoder/VorbisDecoderPlugin.cxx
index 15cdc0ca..488786ed 100644
--- a/src/decoder/vorbis_decoder_plugin.c
+++ b/src/decoder/VorbisDecoderPlugin.cxx
@@ -18,10 +18,17 @@
*/
#include "config.h"
-#include "vorbis_comments.h"
-#include "_ogg_common.h"
+#include "VorbisDecoderPlugin.h"
+#include "VorbisComments.hxx"
+#include "decoder_api.h"
+#include "InputStream.hxx"
+#include "OggCodec.hxx"
+
+extern "C" {
#include "audio_check.h"
#include "uri.h"
+}
+
#include "tag_handler.h"
#ifndef HAVE_TREMOR
@@ -48,12 +55,11 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "vorbis"
-#define OGG_CHUNK_SIZE 4096
#if G_BYTE_ORDER == G_BIG_ENDIAN
-#define OGG_DECODE_USE_BIGENDIAN 1
+#define VORBIS_BIG_ENDIAN true
#else
-#define OGG_DECODE_USE_BIGENDIAN 0
+#define VORBIS_BIG_ENDIAN false
#endif
struct vorbis_input_stream {
@@ -65,10 +71,9 @@ struct vorbis_input_stream {
static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
{
- struct vorbis_input_stream *vis = data;
- size_t ret;
-
- ret = decoder_read(vis->decoder, vis->input_stream, ptr, size * nmemb);
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
+ size_t ret = decoder_read(vis->decoder, vis->input_stream,
+ ptr, size * nmemb);
errno = 0;
@@ -77,7 +82,7 @@ static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data)
static int ogg_seek_cb(void *data, ogg_int64_t offset, int whence)
{
- struct vorbis_input_stream *vis = data;
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
return vis->seekable &&
(!vis->decoder || decoder_get_command(vis->decoder) != DECODE_COMMAND_STOP) &&
@@ -93,16 +98,16 @@ static int ogg_close_cb(G_GNUC_UNUSED void *data)
static long ogg_tell_cb(void *data)
{
- const struct vorbis_input_stream *vis = data;
+ struct vorbis_input_stream *vis = (struct vorbis_input_stream *)data;
return (long)vis->input_stream->offset;
}
static const ov_callbacks vorbis_is_callbacks = {
- .read_func = ogg_read_cb,
- .seek_func = ogg_seek_cb,
- .close_func = ogg_close_cb,
- .tell_func = ogg_tell_cb,
+ ogg_read_cb,
+ ogg_seek_cb,
+ ogg_close_cb,
+ ogg_tell_cb,
};
static const char *
@@ -135,9 +140,7 @@ vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf,
{
vis->decoder = decoder;
vis->input_stream = input_stream;
- vis->seekable = input_stream->seekable &&
- (input_stream->uri == NULL ||
- !uri_has_scheme(input_stream->uri));
+ vis->seekable = input_stream_cheap_seeking(input_stream);
int ret = ov_open_callbacks(vis, vf, NULL, 0, vorbis_is_callbacks);
if (ret < 0) {
@@ -155,9 +158,7 @@ static void
vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
char **comments)
{
- struct tag *tag;
-
- tag = vorbis_comments_to_tag(comments);
+ struct tag *tag = vorbis_comments_to_tag(comments);
if (!tag)
return;
@@ -165,55 +166,79 @@ vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
tag_free(tag);
}
+#ifndef HAVE_TREMOR
+static void
+vorbis_interleave(float *dest, const float *const*src,
+ unsigned nframes, unsigned channels)
+{
+ for (const float *const*src_end = src + channels;
+ src != src_end; ++src, ++dest) {
+ float *d = dest;
+ for (const float *s = *src, *s_end = s + nframes;
+ s != s_end; ++s, d += channels)
+ *d = *s;
+ }
+}
+#endif
+
/* public */
static void
vorbis_stream_decode(struct decoder *decoder,
struct input_stream *input_stream)
{
GError *error = NULL;
- OggVorbis_File vf;
- struct vorbis_input_stream vis;
- struct audio_format audio_format;
- float total_time;
- int current_section;
- int prev_section = -1;
- long ret;
- char chunk[OGG_CHUNK_SIZE];
- long bitRate = 0;
- long test;
- const vorbis_info *vi;
- enum decoder_command cmd = DECODE_COMMAND_NONE;
-
- if (ogg_stream_type_detect(input_stream) != VORBIS)
+
+ if (ogg_codec_detect(decoder, input_stream) != OGG_CODEC_VORBIS)
return;
- /* rewind the stream, because ogg_stream_type_detect() has
+ /* rewind the stream, because ogg_codec_detect() has
moved it */
input_stream_lock_seek(input_stream, 0, SEEK_SET, NULL);
+ struct vorbis_input_stream vis;
+ OggVorbis_File vf;
if (!vorbis_is_open(&vis, &vf, decoder, input_stream))
return;
- vi = ov_info(&vf, -1);
+ const vorbis_info *vi = ov_info(&vf, -1);
if (vi == NULL) {
g_warning("ov_info() has failed");
return;
}
+ struct audio_format audio_format;
if (!audio_format_init_checked(&audio_format, vi->rate,
+#ifdef HAVE_TREMOR
SAMPLE_FORMAT_S16,
+#else
+ SAMPLE_FORMAT_FLOAT,
+#endif
vi->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);
return;
}
- total_time = ov_time_total(&vf, -1);
+ float total_time = ov_time_total(&vf, -1);
if (total_time < 0)
total_time = 0;
decoder_initialized(decoder, &audio_format, vis.seekable, total_time);
+ enum decoder_command cmd = decoder_get_command(decoder);
+
+#ifdef HAVE_TREMOR
+ char buffer[4096];
+#else
+ float buffer[2048];
+ const int frames_per_buffer =
+ G_N_ELEMENTS(buffer) / audio_format.channels;
+ const unsigned frame_size = sizeof(buffer[0]) * audio_format.channels;
+#endif
+
+ int prev_section = -1;
+ unsigned kbit_rate = 0;
+
do {
if (cmd == DECODE_COMMAND_SEEK) {
double seek_where = decoder_seek_where(decoder);
@@ -223,17 +248,33 @@ vorbis_stream_decode(struct decoder *decoder,
decoder_seek_error(decoder);
}
- ret = ov_read(&vf, chunk, sizeof(chunk),
- OGG_DECODE_USE_BIGENDIAN, 2, 1, &current_section);
- if (ret == OV_HOLE) /* bad packet */
- ret = 0;
- else if (ret <= 0)
+ int current_section;
+
+#ifdef HAVE_TREMOR
+ long nbytes = ov_read(&vf, buffer, sizeof(buffer),
+ VORBIS_BIG_ENDIAN, 2, 1,
+ &current_section);
+#else
+ float **per_channel;
+ long nframes = ov_read_float(&vf, &per_channel,
+ frames_per_buffer,
+ &current_section);
+ long nbytes = nframes;
+ if (nframes > 0) {
+ vorbis_interleave(buffer,
+ (const float*const*)per_channel,
+ nframes, audio_format.channels);
+ nbytes *= frame_size;
+ }
+#endif
+
+ if (nbytes == OV_HOLE) /* bad packet */
+ nbytes = 0;
+ else if (nbytes <= 0)
/* break on EOF or other error */
break;
if (current_section != prev_section) {
- char **comments;
-
vi = ov_info(&vf, -1);
if (vi == NULL) {
g_warning("ov_info() has failed");
@@ -248,7 +289,7 @@ vorbis_stream_decode(struct decoder *decoder,
break;
}
- comments = ov_comment(&vf, -1)->user_comments;
+ char **comments = ov_comment(&vf, -1)->user_comments;
vorbis_send_comments(decoder, input_stream, comments);
struct replay_gain_info rgi;
@@ -258,12 +299,13 @@ vorbis_stream_decode(struct decoder *decoder,
prev_section = current_section;
}
- if ((test = ov_bitrate_instant(&vf)) > 0)
- bitRate = test / 1000;
+ long test = ov_bitrate_instant(&vf);
+ if (test > 0)
+ kbit_rate = test / 1000;
cmd = decoder_data(decoder, input_stream,
- chunk, ret,
- bitRate);
+ buffer, nbytes,
+ kbit_rate);
} while (cmd != DECODE_COMMAND_STOP);
ov_clear(&vf);
@@ -306,9 +348,14 @@ static const char *const vorbis_mime_types[] = {
};
const struct decoder_plugin vorbis_decoder_plugin = {
- .name = "vorbis",
- .stream_decode = vorbis_stream_decode,
- .scan_stream = vorbis_scan_stream,
- .suffixes = vorbis_suffixes,
- .mime_types = vorbis_mime_types
+ "vorbis",
+ nullptr,
+ nullptr,
+ vorbis_stream_decode,
+ nullptr,
+ nullptr,
+ vorbis_scan_stream,
+ nullptr,
+ vorbis_suffixes,
+ vorbis_mime_types
};
diff --git a/src/decoder/VorbisDecoderPlugin.h b/src/decoder/VorbisDecoderPlugin.h
new file mode 100644
index 00000000..618c9ffd
--- /dev/null
+++ b/src/decoder/VorbisDecoderPlugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2012 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_DECODER_VORBIS_H
+#define MPD_DECODER_VORBIS_H
+
+extern const struct decoder_plugin vorbis_decoder_plugin;
+
+#endif
diff --git a/src/decoder/wavpack_decoder_plugin.c b/src/decoder/WavpackDecoderPlugin.cxx
index 9ebd0fcc..bac62d42 100644
--- a/src/decoder/wavpack_decoder_plugin.c
+++ b/src/decoder/WavpackDecoderPlugin.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2011 The Music Player Daemon Project
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,14 @@
*/
#include "config.h"
+#include "WavpackDecoderPlugin.hxx"
#include "decoder_api.h"
+#include "InputStream.hxx"
+
+extern "C" {
#include "audio_check.h"
-#include "path.h"
-#include "utils.h"
-#include "tag_table.h"
+}
+
#include "tag_handler.h"
#include "tag_ape.h"
@@ -30,7 +33,6 @@
#include <glib.h>
#include <assert.h>
-#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
@@ -53,18 +55,18 @@ typedef void (*format_samples_t)(
static void
format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
{
- int32_t *src = buffer;
+ int32_t *src = (int32_t *)buffer;
switch (bytes_per_sample) {
case 1: {
- int8_t *dst = buffer;
+ int8_t *dst = (int8_t *)buffer;
/*
* The asserts like the following one are because we do the
* formatting of samples within a single buffer. The size
* of the output samples never can be greater than the size
* of the input ones. Otherwise we would have an overflow.
*/
- assert_static(sizeof(*dst) <= sizeof(*src));
+ static_assert(sizeof(*dst) <= sizeof(*src), "Wrong size");
/* pass through and align 8-bit samples */
while (count--) {
@@ -73,8 +75,8 @@ format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
break;
}
case 2: {
- uint16_t *dst = buffer;
- assert_static(sizeof(*dst) <= sizeof(*src));
+ uint16_t *dst = (uint16_t *)buffer;
+ static_assert(sizeof(*dst) <= sizeof(*src), "Wrong size");
/* pass through and align 16-bit samples */
while (count--) {
@@ -97,7 +99,7 @@ static void
format_samples_float(G_GNUC_UNUSED int bytes_per_sample, void *buffer,
uint32_t count)
{
- float *p = buffer;
+ float *p = (float *)buffer;
while (count--) {
*p /= (1 << 23);
@@ -359,7 +361,7 @@ static struct wavpack_input *
wpin(void *id)
{
assert(id);
- return id;
+ return (struct wavpack_input *)id;
}
static int32_t
@@ -441,14 +443,14 @@ wavpack_input_can_seek(void *id)
}
static WavpackStreamReader mpd_is_reader = {
- .read_bytes = wavpack_input_read_bytes,
- .get_pos = wavpack_input_get_pos,
- .set_pos_abs = wavpack_input_set_pos_abs,
- .set_pos_rel = wavpack_input_set_pos_rel,
- .push_back_byte = wavpack_input_push_back_byte,
- .get_length = wavpack_input_get_length,
- .can_seek = wavpack_input_can_seek,
- .write_bytes = NULL /* no need to write edited tags */
+ wavpack_input_read_bytes,
+ wavpack_input_get_pos,
+ wavpack_input_set_pos_abs,
+ wavpack_input_set_pos_rel,
+ wavpack_input_push_back_byte,
+ wavpack_input_get_length,
+ wavpack_input_can_seek,
+ nullptr /* no need to write edited tags */
};
static void
@@ -462,7 +464,7 @@ wavpack_input_init(struct wavpack_input *isp, struct decoder *decoder,
static struct input_stream *
wavpack_open_wvc(struct decoder *decoder, const char *uri,
- GMutex *mutex, GCond *cond,
+ Mutex &mutex, Cond &cond,
struct wavpack_input *wpi)
{
struct input_stream *is_wvc;
@@ -475,7 +477,7 @@ wavpack_open_wvc(struct decoder *decoder, const char *uri,
* single files. utf8url is not absolute file path :/
*/
if (uri == NULL)
- return false;
+ return nullptr;
wvc_url = g_strconcat(uri, "c", NULL);
is_wvc = input_stream_open(wvc_url, mutex, cond, NULL);
@@ -515,7 +517,8 @@ wavpack_streamdecode(struct decoder * decoder, struct input_stream *is)
struct wavpack_input isp, isp_wvc;
bool can_seek = is->seekable;
- is_wvc = wavpack_open_wvc(decoder, is->uri, is->mutex, is->cond,
+ is_wvc = wavpack_open_wvc(decoder, is->uri.c_str(),
+ is->mutex, is->cond,
&isp_wvc);
if (is_wvc != NULL) {
open_flags |= OPEN_WVC;
@@ -587,10 +590,14 @@ static char const *const wavpack_mime_types[] = {
};
const struct decoder_plugin wavpack_decoder_plugin = {
- .name = "wavpack",
- .stream_decode = wavpack_streamdecode,
- .file_decode = wavpack_filedecode,
- .scan_file = wavpack_scan_file,
- .suffixes = wavpack_suffixes,
- .mime_types = wavpack_mime_types
+ "wavpack",
+ nullptr,
+ nullptr,
+ wavpack_streamdecode,
+ wavpack_filedecode,
+ wavpack_scan_file,
+ nullptr,
+ nullptr,
+ wavpack_suffixes,
+ wavpack_mime_types
};
diff --git a/src/decoder/WavpackDecoderPlugin.hxx b/src/decoder/WavpackDecoderPlugin.hxx
new file mode 100644
index 00000000..9ebe6354
--- /dev/null
+++ b/src/decoder/WavpackDecoderPlugin.hxx
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2003-2013 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_DECODER_WAVPACK_HXX
+#define MPD_DECODER_WAVPACK_HXX
+
+extern const struct decoder_plugin wavpack_decoder_plugin;
+
+#endif
diff --git a/src/decoder/XiphTags.c b/src/decoder/XiphTags.c
new file mode 100644
index 00000000..d55787b9
--- /dev/null
+++ b/src/decoder/XiphTags.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2012 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 "XiphTags.h"
+
+const struct tag_table xiph_tags[] = {
+ { "tracknumber", TAG_TRACK },
+ { "discnumber", TAG_DISC },
+ { "album artist", TAG_ALBUM_ARTIST },
+ { NULL, TAG_NUM_OF_ITEM_TYPES }
+};
diff --git a/src/decoder/XiphTags.h b/src/decoder/XiphTags.h
new file mode 100644
index 00000000..22a4e220
--- /dev/null
+++ b/src/decoder/XiphTags.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2003-2012 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_XIPH_TAGS_H
+#define MPD_XIPH_TAGS_H
+
+#include "check.h"
+#include "tag_table.h"
+
+extern const struct tag_table xiph_tags[];
+
+#endif
diff --git a/src/decoder/audiofile_decoder_plugin.c b/src/decoder/audiofile_decoder_plugin.c
index b344795e..d2ceee8a 100644
--- a/src/decoder/audiofile_decoder_plugin.c
+++ b/src/decoder/audiofile_decoder_plugin.c
@@ -69,14 +69,14 @@ static AFfileoffset
audiofile_file_length(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
- return is->size;
+ return input_stream_get_size(is);
}
static AFfileoffset
audiofile_file_tell(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
- return is->offset;
+ return input_stream_get_offset(is);
}
static void
@@ -93,7 +93,7 @@ audiofile_file_seek(AFvirtualfile *vfile, AFfileoffset offset, int is_relative)
struct input_stream *is = (struct input_stream *) vfile->closure;
int whence = (is_relative ? SEEK_CUR : SEEK_SET);
if (input_stream_lock_seek(is, offset, whence, NULL)) {
- return is->offset;
+ return input_stream_get_offset(is);
} else {
return -1;
}
@@ -166,7 +166,7 @@ audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
char chunk[CHUNK_SIZE];
enum decoder_command cmd;
- if (!is->seekable) {
+ if (!input_stream_is_seekable(is)) {
g_warning("not seekable");
return;
}
@@ -194,7 +194,7 @@ audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
total_time = ((float)frame_count / (float)audio_format.sample_rate);
- bit_rate = (uint16_t)(is->size * 8.0 / total_time / 1000.0 + 0.5);
+ bit_rate = (uint16_t)(input_stream_get_size(is) * 8.0 / total_time / 1000.0 + 0.5);
fs = (int)afGetVirtualFrameSize(af_fp, AF_DEFAULT_TRACK, 1);
diff --git a/src/decoder/dsdiff_decoder_plugin.c b/src/decoder/dsdiff_decoder_plugin.c
index 84471fb3..44d12d89 100644
--- a/src/decoder/dsdiff_decoder_plugin.c
+++ b/src/decoder/dsdiff_decoder_plugin.c
@@ -52,10 +52,23 @@ struct dsdiff_chunk_header {
uint32_t size_high, size_low;
};
+/** struct for DSDIFF native Artist and Title tags */
+struct dsdiff_native_tag {
+ uint32_t size;
+};
+
struct dsdiff_metadata {
unsigned sample_rate, channels;
bool bitreverse;
uint64_t chunk_size;
+#ifdef HAVE_ID3TAG
+ goffset id3_offset;
+ uint64_t id3_size;
+#endif
+ /** offset for artist tag */
+ goffset diar_offset;
+ /** offset for title tag */
+ goffset diti_offset;
};
static bool lsbitfirst;
@@ -115,12 +128,12 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
goffset end_offset)
{
struct dsdiff_chunk_header header;
- while ((goffset)(is->offset + sizeof(header)) <= end_offset) {
+ while ((goffset)(input_stream_get_offset(is) + sizeof(header)) <= end_offset) {
if (!dsdiff_read_chunk_header(decoder, is, &header))
return false;
- goffset chunk_end_offset =
- is->offset + dsdiff_chunk_size(&header);
+ goffset chunk_end_offset = input_stream_get_offset(is)
+ + dsdiff_chunk_size(&header);
if (chunk_end_offset > end_offset)
return false;
@@ -161,7 +174,7 @@ dsdiff_read_prop_snd(struct decoder *decoder, struct input_stream *is,
}
}
- return is->offset == end_offset;
+ return input_stream_get_offset(is) == end_offset;
}
/**
@@ -173,7 +186,7 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
const struct dsdiff_chunk_header *prop_header)
{
uint64_t prop_size = dsdiff_chunk_size(prop_header);
- goffset end_offset = is->offset + prop_size;
+ goffset end_offset = input_stream_get_offset(is) + prop_size;
struct dsdlib_id prop_id;
if (prop_size < sizeof(prop_id) ||
@@ -187,6 +200,127 @@ dsdiff_read_prop(struct decoder *decoder, struct input_stream *is,
return dsdlib_skip_to(decoder, is, end_offset);
}
+static void
+dsdiff_handle_native_tag(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset,
+ enum tag_type type)
+{
+ if (!dsdlib_skip_to(NULL, is, tagoffset))
+ return;
+
+ struct dsdiff_native_tag metatag;
+
+ if (!dsdlib_read(NULL, is, &metatag, sizeof(metatag)))
+ return;
+
+ uint32_t length = GUINT32_FROM_BE(metatag.size);
+
+ /* Check and limit size of the tag to prevent a stack overflow */
+ if (length == 0 || length > 60)
+ return;
+
+ char string[length];
+ char *label;
+ label = string;
+
+ if (!dsdlib_read(NULL, is, label, (size_t)length))
+ return;
+
+ string[length] = '\0';
+ tag_handler_invoke_tag(handler, handler_ctx, type, label);
+ return;
+}
+
+/**
+ * Read and parse additional metadata chunks for tagging purposes. By default
+ * dsdiff files only support equivalents for artist and title but some of the
+ * extract tools add an id3 tag to provide more tags. If such id3 is found
+ * this will be used for tagging otherwise the native tags (if any) will be
+ * used
+ */
+
+static bool
+dsdiff_read_metadata_extra(struct decoder *decoder, struct input_stream *is,
+ struct dsdiff_metadata *metadata,
+ struct dsdiff_chunk_header *chunk_header,
+ const struct tag_handler *handler,
+ void *handler_ctx)
+{
+
+ /* skip from DSD data to next chunk header */
+ if (!dsdlib_skip(decoder, is, metadata->chunk_size))
+ return false;
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+
+#ifdef HAVE_ID3TAG
+ metadata->id3_size = 0;
+#endif
+
+ /* Now process all the remaining chunk headers in the stream
+ and record their position and size */
+
+ const goffset size = input_stream_get_size(is);
+ while (input_stream_get_offset(is) < size) {
+ uint64_t chunk_size = dsdiff_chunk_size(chunk_header);
+
+ /* DIIN chunk, is directly followed by other chunks */
+ if (dsdlib_id_equals(&chunk_header->id, "DIIN"))
+ chunk_size = 0;
+
+ /* DIAR chunk - DSDIFF native tag for Artist */
+ if (dsdlib_id_equals(&chunk_header->id, "DIAR")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->diar_offset = input_stream_get_offset(is);
+ }
+
+ /* DITI chunk - DSDIFF native tag for Title */
+ if (dsdlib_id_equals(&chunk_header->id, "DITI")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->diti_offset = input_stream_get_offset(is);
+ }
+#ifdef HAVE_ID3TAG
+ /* 'ID3 ' chunk, offspec. Used by sacdextract */
+ if (dsdlib_id_equals(&chunk_header->id, "ID3 ")) {
+ chunk_size = dsdiff_chunk_size(chunk_header);
+ metadata->id3_offset = input_stream_get_offset(is);
+ metadata->id3_size = chunk_size;
+ }
+#endif
+ if (chunk_size != 0) {
+ if (!dsdlib_skip(decoder, is, chunk_size))
+ break;
+ }
+
+ if (input_stream_get_offset(is) < size) {
+ if (!dsdiff_read_chunk_header(decoder, is, chunk_header))
+ return false;
+ }
+ chunk_size = 0;
+ }
+ /* done processing chunk headers, process tags if any */
+
+#ifdef HAVE_ID3TAG
+ if (metadata->id3_offset != 0)
+ {
+ /* a ID3 tag has preference over the other tags, do not process
+ other tags if we have one */
+ dsdlib_tag_id3(is, handler, handler_ctx, metadata->id3_offset);
+ return true;
+ }
+#endif
+
+ if (metadata->diar_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diar_offset, TAG_ARTIST);
+
+ if (metadata->diti_offset != 0)
+ dsdiff_handle_native_tag(is, handler, handler_ctx,
+ metadata->diti_offset, TAG_TITLE);
+ return true;
+}
+
/**
* Read and parse all metadata chunks at the beginning. Stop when the
* first "DSD" chunk is seen, and return its header in the
@@ -221,7 +355,8 @@ dsdiff_read_metadata(struct decoder *decoder, struct input_stream *is,
/* ignore unknown chunk */
uint64_t chunk_size;
chunk_size = dsdiff_chunk_size(chunk_header);
- goffset chunk_end_offset = is->offset + chunk_size;
+ goffset chunk_end_offset = input_stream_get_offset(is)
+ + chunk_size;
if (!dsdlib_skip_to(decoder, is, chunk_end_offset))
return false;
@@ -374,6 +509,10 @@ dsdiff_scan_stream(struct input_stream *is,
metadata.sample_rate;
tag_handler_invoke_duration(handler, handler_ctx, songtime);
+ /* Read additional metadata and created tags if available */
+ dsdiff_read_metadata_extra(NULL, is, &metadata, &chunk_header,
+ handler, handler_ctx);
+
return true;
}
diff --git a/src/decoder/dsdlib.c b/src/decoder/dsdlib.c
index 3df9497c..d3043fb0 100644
--- a/src/decoder/dsdlib.c
+++ b/src/decoder/dsdlib.c
@@ -27,12 +27,18 @@
#include "dsf_decoder_plugin.h"
#include "decoder_api.h"
#include "util/bit_reverse.h"
+#include "tag_handler.h"
+#include "tag_id3.h"
#include "dsdlib.h"
#include "dsdiff_decoder_plugin.h"
#include <unistd.h>
#include <stdio.h> /* for SEEK_SET, SEEK_CUR */
+#ifdef HAVE_ID3TAG
+#include <id3tag.h>
+#endif
+
bool
dsdlib_id_equals(const struct dsdlib_id *id, const char *s)
{
@@ -58,24 +64,24 @@ bool
dsdlib_skip_to(struct decoder *decoder, struct input_stream *is,
goffset offset)
{
- if (is->seekable)
+ if (input_stream_is_seekable(is))
return input_stream_seek(is, offset, SEEK_SET, NULL);
- if (is->offset > offset)
+ if (input_stream_get_offset(is) > offset)
return false;
char buffer[8192];
- while (is->offset < offset) {
+ while (input_stream_get_offset(is) < offset) {
size_t length = sizeof(buffer);
- if (offset - is->offset < (goffset)length)
- length = offset - is->offset;
+ if (offset - input_stream_get_offset(is) < (goffset)length)
+ length = offset - input_stream_get_offset(is);
size_t nbytes = decoder_read(decoder, is, buffer, length);
if (nbytes == 0)
return false;
}
- assert(is->offset == offset);
+ assert(input_stream_get_offset(is) == offset);
return true;
}
@@ -91,7 +97,7 @@ dsdlib_skip(struct decoder *decoder, struct input_stream *is,
if (delta == 0)
return true;
- if (is->seekable)
+ if (input_stream_is_seekable(is))
return input_stream_seek(is, delta, SEEK_CUR, NULL);
char buffer[8192];
@@ -110,3 +116,55 @@ dsdlib_skip(struct decoder *decoder, struct input_stream *is,
return true;
}
+/**
+ * Add tags from ID3 tag. All tags commonly found in the ID3 tags of
+ * DSF and DSDIFF files are imported
+ */
+
+#ifdef HAVE_ID3TAG
+void
+dsdlib_tag_id3(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset)
+{
+ assert(tagoffset >= 0);
+
+ if (tagoffset == 0)
+ return;
+
+ if (!dsdlib_skip_to(NULL, is, tagoffset))
+ return;
+
+ struct id3_tag *id3_tag = NULL;
+ id3_length_t count;
+
+ /* Prevent broken files causing problems */
+ const goffset size = input_stream_get_size(is);
+ const goffset offset = input_stream_get_offset(is);
+ if (offset >= size)
+ return;
+
+ count = size - offset;
+
+ /* Check and limit id3 tag size to prevent a stack overflow */
+ if (count == 0 || count > 4096)
+ return;
+
+ id3_byte_t dsdid3[count];
+ id3_byte_t *dsdid3data;
+ dsdid3data = dsdid3;
+
+ if (!dsdlib_read(NULL, is, dsdid3data, count))
+ return;
+
+ id3_tag = id3_tag_parse(dsdid3data, count);
+ if (id3_tag == NULL)
+ return;
+
+ scan_id3_tag(id3_tag, handler, handler_ctx);
+
+ id3_tag_delete(id3_tag);
+
+ return;
+}
+#endif
diff --git a/src/decoder/dsdlib.h b/src/decoder/dsdlib.h
index d9675f5f..0912740c 100644
--- a/src/decoder/dsdlib.h
+++ b/src/decoder/dsdlib.h
@@ -39,4 +39,9 @@ bool
dsdlib_skip(struct decoder *decoder, struct input_stream *is,
goffset delta);
+void
+dsdlib_tag_id3(struct input_stream *is,
+ const struct tag_handler *handler,
+ void *handler_ctx, goffset tagoffset);
+
#endif
diff --git a/src/decoder/dsf_decoder_plugin.c b/src/decoder/dsf_decoder_plugin.c
index c0107eb3..23576a62 100644
--- a/src/decoder/dsf_decoder_plugin.c
+++ b/src/decoder/dsf_decoder_plugin.c
@@ -45,6 +45,10 @@ struct dsf_metadata {
unsigned sample_rate, channels;
bool bitreverse;
uint64_t chunk_size;
+#ifdef HAVE_ID3TAG
+ goffset id3_offset;
+ uint64_t id3_size;
+#endif
};
struct dsf_header {
@@ -57,6 +61,7 @@ struct dsf_header {
/** pointer to id3v2 metadata, should be at the end of the file */
uint32_t pmeta_low, pmeta_high;
};
+
/** DSF file fmt chunk */
struct dsf_fmt_chunk {
@@ -109,6 +114,12 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
if (sizeof(dsf_header) != chunk_size)
return false;
+#ifdef HAVE_ID3TAG
+ uint64_t metadata_offset;
+ metadata_offset = (((uint64_t)GUINT32_FROM_LE(dsf_header.pmeta_high)) << 32) |
+ ((uint64_t)GUINT32_FROM_LE(dsf_header.pmeta_low));
+#endif
+
/* read the 'fmt ' chunk of the DSF file */
struct dsf_fmt_chunk dsf_fmt_chunk;
if (!dsdlib_read(decoder, is, &dsf_fmt_chunk, sizeof(dsf_fmt_chunk)) ||
@@ -153,9 +164,20 @@ dsf_read_metadata(struct decoder *decoder, struct input_stream *is,
data_size -= sizeof(data_chunk);
metadata->chunk_size = data_size;
+ /* data_size cannot be bigger or equal to total file size */
+ const uint64_t size = (uint64_t)input_stream_get_size(is);
+ if (data_size >= size)
+ return false;
+
metadata->channels = (unsigned) dsf_fmt_chunk.channelnum;
metadata->sample_rate = samplefreq;
-
+#ifdef HAVE_ID3TAG
+ /* metada_offset cannot be bigger then or equal to total file size */
+ if (metadata_offset >= size)
+ metadata->id3_offset = 0;
+ else
+ metadata->id3_offset = (goffset) metadata_offset;
+#endif
/* check bits per sample format, determine if bitreverse is needed */
metadata->bitreverse = dsf_fmt_chunk.bitssample == 1;
return true;
@@ -285,7 +307,7 @@ dsf_stream_decode(struct decoder *decoder, struct input_stream *is)
decoder_initialized(decoder, &audio_format, false, songtime);
if (!dsf_decode_chunk(decoder, is, metadata.channels,
- metadata.chunk_size,
+ chunk_size,
metadata.bitreverse))
return;
}
@@ -316,6 +338,10 @@ dsf_scan_stream(struct input_stream *is,
metadata.sample_rate;
tag_handler_invoke_duration(handler, handler_ctx, songtime);
+#ifdef HAVE_ID3TAG
+ /* Add available tags from the ID3 tag */
+ dsdlib_tag_id3(is, handler, handler_ctx, metadata.id3_offset);
+#endif
return true;
}
diff --git a/src/decoder/faad_decoder_plugin.c b/src/decoder/faad_decoder_plugin.c
index 911f033b..a7ece93f 100644
--- a/src/decoder/faad_decoder_plugin.c
+++ b/src/decoder/faad_decoder_plugin.c
@@ -23,16 +23,18 @@
#include "audio_check.h"
#include "tag_handler.h"
-#define AAC_MAX_CHANNELS 6
+#include <neaacdec.h>
+
+#include <glib.h>
#include <assert.h>
#include <unistd.h>
-#include <faad.h>
-#include <glib.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "faad"
+#define AAC_MAX_CHANNELS 6
+
static const unsigned adts_sample_rates[] =
{ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000, 7350, 0, 0, 0
@@ -175,7 +177,8 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
size_t length;
bool success;
- fileread = is->size >= 0 ? is->size : 0;
+ const goffset size = input_stream_get_size(is);
+ fileread = size >= 0 ? size : 0;
decoder_buffer_fill(buffer);
data = decoder_buffer_read(buffer, &length);
@@ -201,7 +204,7 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
return -1;
}
- if (is->seekable && length >= 2 &&
+ if (input_stream_is_seekable(is) && length >= 2 &&
data[0] == 0xFF && ((data[1] & 0xF6) == 0xF0)) {
/* obtain the duration from the ADTS header */
float song_length = adts_song_duration(buffer);
@@ -238,11 +241,11 @@ faad_song_duration(struct decoder_buffer *buffer, struct input_stream *is)
}
/**
- * Wrapper for faacDecInit() which works around some API
+ * Wrapper for NeAACDecInit() which works around some API
* inconsistencies in libfaad.
*/
static bool
-faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
+faad_decoder_init(NeAACDecHandle decoder, struct decoder_buffer *buffer,
struct audio_format *audio_format, GError **error_r)
{
union {
@@ -270,10 +273,8 @@ faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
return false;
}
- nbytes = faacDecInit(decoder, u.out,
-#ifdef HAVE_FAAD_BUFLEN_FUNCS
+ nbytes = NeAACDecInit(decoder, u.out,
length,
-#endif
sample_rate_p, &channels);
if (nbytes < 0) {
g_set_error(error_r, faad_decoder_quark(), 0,
@@ -288,12 +289,12 @@ faad_decoder_init(faacDecHandle decoder, struct decoder_buffer *buffer,
}
/**
- * Wrapper for faacDecDecode() which works around some API
+ * Wrapper for NeAACDecDecode() which works around some API
* inconsistencies in libfaad.
*/
static const void *
-faad_decoder_decode(faacDecHandle decoder, struct decoder_buffer *buffer,
- faacDecFrameInfo *frame_info)
+faad_decoder_decode(NeAACDecHandle decoder, struct decoder_buffer *buffer,
+ NeAACDecFrameInfo *frame_info)
{
union {
/* deconst hack for libfaad */
@@ -301,20 +302,13 @@ faad_decoder_decode(faacDecHandle decoder, struct decoder_buffer *buffer,
void *out;
} u;
size_t length;
- void *result;
u.in = decoder_buffer_read(buffer, &length);
if (u.in == NULL)
return NULL;
- result = faacDecDecode(decoder, frame_info,
- u.out
-#ifdef HAVE_FAAD_BUFLEN_FUNCS
- , length
-#endif
- );
-
- return result;
+ return NeAACDecDecode(decoder, frame_info,
+ u.out, length);
}
/**
@@ -327,8 +321,6 @@ faad_get_file_time_float(struct input_stream *is)
{
struct decoder_buffer *buffer;
float length;
- faacDecHandle decoder;
- faacDecConfigurationPtr config;
buffer = decoder_buffer_new(NULL, is,
FAAD_MIN_STREAMSIZE * AAC_MAX_CHANNELS);
@@ -338,11 +330,12 @@ faad_get_file_time_float(struct input_stream *is)
bool ret;
struct audio_format audio_format;
- decoder = faacDecOpen();
+ NeAACDecHandle decoder = NeAACDecOpen();
- config = faacDecGetCurrentConfiguration(decoder);
+ NeAACDecConfigurationPtr config =
+ NeAACDecGetCurrentConfiguration(decoder);
config->outputFormat = FAAD_FMT_16BIT;
- faacDecSetConfiguration(decoder, config);
+ NeAACDecSetConfiguration(decoder, config);
decoder_buffer_fill(buffer);
@@ -350,7 +343,7 @@ faad_get_file_time_float(struct input_stream *is)
if (ret)
length = 0;
- faacDecClose(decoder);
+ NeAACDecClose(decoder);
}
decoder_buffer_free(buffer);
@@ -380,9 +373,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
{
GError *error = NULL;
float total_time = 0;
- faacDecHandle decoder;
struct audio_format audio_format;
- faacDecConfigurationPtr config;
bool ret;
uint16_t bit_rate = 0;
struct decoder_buffer *buffer;
@@ -394,17 +385,14 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
/* create the libfaad decoder */
- decoder = faacDecOpen();
+ NeAACDecHandle decoder = NeAACDecOpen();
- config = faacDecGetCurrentConfiguration(decoder);
+ NeAACDecConfigurationPtr config =
+ NeAACDecGetCurrentConfiguration(decoder);
config->outputFormat = FAAD_FMT_16BIT;
-#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
config->downMatrix = 1;
-#endif
-#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
config->dontUpSampleImplicitSBR = 0;
-#endif
- faacDecSetConfiguration(decoder, config);
+ NeAACDecSetConfiguration(decoder, config);
while (!decoder_buffer_is_full(buffer) &&
!input_stream_lock_eof(is) &&
@@ -419,7 +407,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
if (!ret) {
g_warning("%s", error->message);
g_error_free(error);
- faacDecClose(decoder);
+ NeAACDecClose(decoder);
return;
}
@@ -432,7 +420,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
do {
size_t frame_size;
const void *decoded;
- faacDecFrameInfo frame_info;
+ NeAACDecFrameInfo frame_info;
/* find the next frame */
@@ -447,7 +435,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
if (frame_info.error > 0) {
g_warning("error decoding AAC stream: %s\n",
- faacDecGetErrorMessage(frame_info.error));
+ NeAACDecGetErrorMessage(frame_info.error));
break;
}
@@ -457,14 +445,12 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
break;
}
-#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
if (frame_info.samplerate != audio_format.sample_rate) {
g_warning("sample rate changed from %u to %lu",
audio_format.sample_rate,
(unsigned long)frame_info.samplerate);
break;
}
-#endif
decoder_buffer_consume(buffer, frame_info.bytesconsumed);
@@ -485,7 +471,7 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
/* cleanup */
- faacDecClose(decoder);
+ NeAACDecClose(decoder);
}
static bool
diff --git a/src/decoder/flac_compat.h b/src/decoder/flac_compat.h
deleted file mode 100644
index 9a30acc2..00000000
--- a/src/decoder/flac_compat.h
+++ /dev/null
@@ -1,114 +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.
- */
-
-/*
- * Common data structures and functions used by FLAC and OggFLAC
- */
-
-#ifndef MPD_FLAC_COMPAT_H
-#define MPD_FLAC_COMPAT_H
-
-#include <FLAC/export.h>
-#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
-# include <FLAC/seekable_stream_decoder.h>
-
-/* starting with libFLAC 1.1.3, the SeekableStreamDecoder has been
- merged into the StreamDecoder. The following macros try to emulate
- the new API for libFLAC 1.1.2 by mapping MPD's StreamDecoder calls
- to the old SeekableStreamDecoder API. */
-
-#define FLAC__StreamDecoder FLAC__SeekableStreamDecoder
-#define FLAC__stream_decoder_new FLAC__seekable_stream_decoder_new
-#define FLAC__stream_decoder_get_decode_position FLAC__seekable_stream_decoder_get_decode_position
-#define FLAC__stream_decoder_get_state FLAC__seekable_stream_decoder_get_state
-#define FLAC__stream_decoder_process_single FLAC__seekable_stream_decoder_process_single
-#define FLAC__stream_decoder_process_until_end_of_metadata FLAC__seekable_stream_decoder_process_until_end_of_metadata
-#define FLAC__stream_decoder_seek_absolute FLAC__seekable_stream_decoder_seek_absolute
-#define FLAC__stream_decoder_finish FLAC__seekable_stream_decoder_finish
-#define FLAC__stream_decoder_delete FLAC__seekable_stream_decoder_delete
-
-#define FLAC__STREAM_DECODER_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM
-
-typedef unsigned flac_read_status_size_t;
-
-#define FLAC__StreamDecoderReadStatus FLAC__SeekableStreamDecoderReadStatus
-#define FLAC__STREAM_DECODER_READ_STATUS_CONTINUE FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
-#define FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK
-#define FLAC__STREAM_DECODER_READ_STATUS_ABORT FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR
-
-#define FLAC__StreamDecoderSeekStatus FLAC__SeekableStreamDecoderSeekStatus
-#define FLAC__STREAM_DECODER_SEEK_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK
-#define FLAC__STREAM_DECODER_SEEK_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
-#define FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR
-
-#define FLAC__StreamDecoderTellStatus FLAC__SeekableStreamDecoderTellStatus
-#define FLAC__STREAM_DECODER_TELL_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK
-#define FLAC__STREAM_DECODER_TELL_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
-#define FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR
-
-#define FLAC__StreamDecoderLengthStatus FLAC__SeekableStreamDecoderLengthStatus
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_OK FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
-#define FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR
-
-typedef enum {
- FLAC__STREAM_DECODER_INIT_STATUS_OK,
- FLAC__STREAM_DECODER_INIT_STATUS_ERROR,
-} FLAC__StreamDecoderInitStatus;
-
-static inline FLAC__StreamDecoderInitStatus
-FLAC__stream_decoder_init_stream(FLAC__SeekableStreamDecoder *decoder,
- FLAC__SeekableStreamDecoderReadCallback read_cb,
- FLAC__SeekableStreamDecoderSeekCallback seek_cb,
- FLAC__SeekableStreamDecoderTellCallback tell_cb,
- FLAC__SeekableStreamDecoderLengthCallback length_cb,
- FLAC__SeekableStreamDecoderEofCallback eof_cb,
- FLAC__SeekableStreamDecoderWriteCallback write_cb,
- FLAC__SeekableStreamDecoderMetadataCallback metadata_cb,
- FLAC__SeekableStreamDecoderErrorCallback error_cb,
- void *data)
-{
- return FLAC__seekable_stream_decoder_set_read_callback(decoder, read_cb) &&
- FLAC__seekable_stream_decoder_set_seek_callback(decoder, seek_cb) &&
- FLAC__seekable_stream_decoder_set_tell_callback(decoder, tell_cb) &&
- FLAC__seekable_stream_decoder_set_length_callback(decoder, length_cb) &&
- FLAC__seekable_stream_decoder_set_eof_callback(decoder, eof_cb) &&
- FLAC__seekable_stream_decoder_set_write_callback(decoder, write_cb) &&
- FLAC__seekable_stream_decoder_set_metadata_callback(decoder, metadata_cb) &&
- FLAC__seekable_stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT) &&
- FLAC__seekable_stream_decoder_set_error_callback(decoder, error_cb) &&
- FLAC__seekable_stream_decoder_set_client_data(decoder, data) &&
- FLAC__seekable_stream_decoder_init(decoder) == FLAC__SEEKABLE_STREAM_DECODER_OK
- ? FLAC__STREAM_DECODER_INIT_STATUS_OK
- : FLAC__STREAM_DECODER_INIT_STATUS_ERROR;
-}
-
-#else /* FLAC_API_VERSION_CURRENT > 7 */
-
-# include <FLAC/stream_decoder.h>
-
-# define flac_init(a,b,c,d,e,f,g,h,i,j) \
- (FLAC__stream_decoder_init_stream(a,b,c,d,e,f,g,h,i,j) \
- == FLAC__STREAM_DECODER_INIT_STATUS_OK)
-
-typedef size_t flac_read_status_size_t;
-
-#endif /* FLAC_API_VERSION_CURRENT >= 7 */
-
-#endif /* _FLAC_COMMON_H */
diff --git a/src/decoder/flac_metadata.h b/src/decoder/flac_metadata.h
deleted file mode 100644
index 3c463d5d..00000000
--- a/src/decoder/flac_metadata.h
+++ /dev/null
@@ -1,64 +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_FLAC_METADATA_H
-#define MPD_FLAC_METADATA_H
-
-#include <assert.h>
-#include <stdbool.h>
-#include <FLAC/metadata.h>
-
-struct tag_handler;
-struct tag;
-struct replay_gain_info;
-
-static inline unsigned
-flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
-{
- assert(stream_info->sample_rate > 0);
-
- return (stream_info->total_samples + stream_info->sample_rate - 1) /
- stream_info->sample_rate;
-}
-
-bool
-flac_parse_replay_gain(struct replay_gain_info *rgi,
- const FLAC__StreamMetadata *block);
-
-bool
-flac_parse_mixramp(char **mixramp_start, char **mixramp_end,
- const FLAC__StreamMetadata *block);
-
-void
-flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
- const FLAC__StreamMetadata_VorbisComment *comment);
-
-void
-flac_scan_metadata(const char *track,
- const FLAC__StreamMetadata *block,
- const struct tag_handler *handler, void *handler_ctx);
-
-bool
-flac_scan_file2(const char *file, const char *char_tnum,
- const struct tag_handler *handler, void *handler_ctx);
-
-struct tag *
-flac_tag_load(const char *file, const char *char_tnum);
-
-#endif
diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/mad_decoder_plugin.c
index 62c37164..4e2e0331 100644
--- a/src/decoder/mad_decoder_plugin.c
+++ b/src/decoder/mad_decoder_plugin.c
@@ -76,9 +76,9 @@ mad_fixed_to_24_sample(mad_fixed_t sample)
sample = sample + (1L << (MAD_F_FRACBITS - bits));
/* clip */
- if (sample > MAX)
+ if (gcc_unlikely(sample > MAX))
sample = MAX;
- else if (sample < MIN)
+ else if (gcc_unlikely(sample < MIN))
sample = MIN;
/* quantize */
@@ -359,15 +359,14 @@ static void mp3_parse_id3(struct mp3_data *data, size_t tagsize,
struct replay_gain_info rgi;
char *mixramp_start;
char *mixramp_end;
- float replay_gain_db = 0;
if (parse_id3_replay_gain_info(&rgi, id3_tag)) {
- replay_gain_db = decoder_replay_gain(data->decoder, &rgi);
+ decoder_replay_gain(data->decoder, &rgi);
data->found_replay_gain = true;
}
if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag))
- decoder_mixramp(data->decoder, replay_gain_db,
+ decoder_mixramp(data->decoder,
mixramp_start, mixramp_end);
}
@@ -756,7 +755,7 @@ mp3_frame_duration(const struct mad_frame *frame)
static goffset
mp3_this_frame_offset(const struct mp3_data *data)
{
- goffset offset = data->input_stream->offset;
+ goffset offset = input_stream_get_offset(data->input_stream);
if (data->stream.this_frame != NULL)
offset -= data->stream.bufend - data->stream.this_frame;
@@ -769,7 +768,8 @@ mp3_this_frame_offset(const struct mp3_data *data)
static goffset
mp3_rest_including_this_frame(const struct mp3_data *data)
{
- return data->input_stream->size - mp3_this_frame_offset(data);
+ return input_stream_get_size(data->input_stream)
+ - mp3_this_frame_offset(data);
}
/**
@@ -842,7 +842,7 @@ mp3_decode_first_frame(struct mp3_data *data, struct tag **tag)
if (parse_lame(&lame, &ptr, &bitlen)) {
if (gapless_playback &&
- data->input_stream->seekable) {
+ input_stream_is_seekable(data->input_stream)) {
data->drop_start_samples = lame.encoder_delay +
DECODERDELAY;
data->drop_end_samples = lame.encoder_padding;
@@ -1082,7 +1082,7 @@ mp3_read(struct mp3_data *data)
if (cmd == DECODE_COMMAND_SEEK) {
unsigned long j;
- assert(data->input_stream->seekable);
+ assert(input_stream_is_seekable(data->input_stream));
j = mp3_time_to_frame(data,
decoder_seek_where(decoder));
@@ -1164,7 +1164,8 @@ mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
}
decoder_initialized(decoder, &audio_format,
- data.input_stream->seekable, data.total_time);
+ input_stream_is_seekable(input_stream),
+ data.total_time);
if (tag != NULL) {
decoder_tag(decoder, input_stream, tag);
diff --git a/src/decoder/modplug_decoder_plugin.c b/src/decoder/modplug_decoder_plugin.c
index 21ee79e7..bc3d8176 100644
--- a/src/decoder/modplug_decoder_plugin.c
+++ b/src/decoder/modplug_decoder_plugin.c
@@ -41,19 +41,21 @@ static GByteArray *mod_loadfile(struct decoder *decoder, struct input_stream *is
GByteArray *bdatas;
size_t ret;
- if (is->size == 0) {
+ const goffset size = input_stream_get_size(is);
+
+ if (size == 0) {
g_warning("file is empty");
return NULL;
}
- if (is->size > MODPLUG_FILE_LIMIT) {
+ if (size > MODPLUG_FILE_LIMIT) {
g_warning("file too large");
return NULL;
}
//known/unknown size, preallocate array, lets read in chunks
- if (is->size > 0) {
- bdatas = g_byte_array_sized_new(is->size);
+ if (size > 0) {
+ bdatas = g_byte_array_sized_new(size);
} else {
bdatas = g_byte_array_sized_new(MODPLUG_PREALLOC_BLOCK);
}
@@ -126,7 +128,8 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
assert(audio_format_valid(&audio_format));
decoder_initialized(decoder, &audio_format,
- is->seekable, ModPlug_GetLength(f) / 1000.0);
+ input_stream_is_seekable(is),
+ ModPlug_GetLength(f) / 1000.0);
do {
ret = ModPlug_Read(f, audio_buffer, MODPLUG_FRAME_SIZE);
diff --git a/src/decoder/mp4ff_decoder_plugin.c b/src/decoder/mp4ff_decoder_plugin.c
deleted file mode 100644
index ca78a22d..00000000
--- a/src/decoder/mp4ff_decoder_plugin.c
+++ /dev/null
@@ -1,448 +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 "decoder_api.h"
-#include "audio_check.h"
-#include "tag_table.h"
-#include "tag_handler.h"
-
-#include <glib.h>
-
-#include <mp4ff.h>
-#include <faad.h>
-
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "mp4ff"
-
-/* all code here is either based on or copied from FAAD2's frontend code */
-
-struct mp4ff_input_stream {
- mp4ff_callback_t callback;
-
- struct decoder *decoder;
- struct input_stream *input_stream;
-};
-
-static int
-mp4_get_aac_track(mp4ff_t * infile, faacDecHandle decoder,
- uint32_t *sample_rate, unsigned char *channels_r)
-{
-#ifdef HAVE_FAAD_LONG
- /* neaacdec.h declares all arguments as "unsigned long", but
- internally expects uint32_t pointers. To avoid gcc
- warnings, use this workaround. */
- unsigned long *sample_rate_r = (unsigned long*)sample_rate;
-#else
- uint32_t *sample_rate_r = sample_rate;
-#endif
- int i, rc;
- int num_tracks = mp4ff_total_tracks(infile);
-
- for (i = 0; i < num_tracks; i++) {
- unsigned char *buff = NULL;
- unsigned int buff_size = 0;
-
- if (mp4ff_get_track_type(infile, i) != 1)
- /* not an audio track */
- continue;
-
- if (decoder == NULL)
- /* have don't have a decoder to initialize -
- we're done now, because we found an audio
- track */
- return i;
-
- mp4ff_get_decoder_config(infile, i, &buff, &buff_size);
- if (buff == NULL)
- continue;
-
- rc = faacDecInit2(decoder, buff, buff_size,
- sample_rate_r, channels_r);
- free(buff);
-
- if (rc >= 0)
- /* found a valid AAC track */
- return i;
- }
-
- /* can't decode this */
- return -1;
-}
-
-static uint32_t
-mp4_read(void *user_data, void *buffer, uint32_t length)
-{
- struct mp4ff_input_stream *mis = user_data;
-
- if (length == 0)
- /* libmp4ff is known to attempt to read 0 bytes - make
- this a special case, because the input_stream API
- would not allow this */
- return 0;
-
- return decoder_read(mis->decoder, mis->input_stream, buffer, length);
-}
-
-static uint32_t
-mp4_seek(void *user_data, uint64_t position)
-{
- struct mp4ff_input_stream *mis = user_data;
-
- return input_stream_lock_seek(mis->input_stream, position, SEEK_SET,
- NULL)
- ? 0 : -1;
-}
-
-static const mp4ff_callback_t mpd_mp4ff_callback = {
- .read = mp4_read,
- .seek = mp4_seek,
-};
-
-static mp4ff_t *
-mp4ff_input_stream_open(struct mp4ff_input_stream *mis,
- struct decoder *decoder,
- struct input_stream *input_stream)
-{
- mis->callback = mpd_mp4ff_callback;
- mis->callback.user_data = mis;
- mis->decoder = decoder;
- mis->input_stream = input_stream;
-
- return mp4ff_open_read(&mis->callback);
-}
-
-static faacDecHandle
-mp4_faad_new(mp4ff_t *mp4fh, int *track_r, struct audio_format *audio_format)
-{
- faacDecHandle decoder;
- faacDecConfigurationPtr config;
- int track;
- uint32_t sample_rate;
- unsigned char channels;
- GError *error = NULL;
-
- decoder = faacDecOpen();
-
- config = faacDecGetCurrentConfiguration(decoder);
- config->outputFormat = FAAD_FMT_16BIT;
-#ifdef HAVE_FAACDECCONFIGURATION_DOWNMATRIX
- config->downMatrix = 1;
-#endif
-#ifdef HAVE_FAACDECCONFIGURATION_DONTUPSAMPLEIMPLICITSBR
- config->dontUpSampleImplicitSBR = 0;
-#endif
- faacDecSetConfiguration(decoder, config);
-
- track = mp4_get_aac_track(mp4fh, decoder, &sample_rate, &channels);
- if (track < 0) {
- g_warning("No AAC track found");
- faacDecClose(decoder);
- return NULL;
- }
-
- if (!audio_format_init_checked(audio_format, sample_rate,
- SAMPLE_FORMAT_S16, channels,
- &error)) {
- g_warning("%s", error->message);
- g_error_free(error);
- faacDecClose(decoder);
- return NULL;
- }
-
- *track_r = track;
-
- return decoder;
-}
-
-static void
-mp4_decode(struct decoder *mpd_decoder, struct input_stream *input_stream)
-{
- struct mp4ff_input_stream mis;
- mp4ff_t *mp4fh;
- int32_t track;
- float file_time, total_time;
- int32_t scale;
- faacDecHandle decoder;
- struct audio_format audio_format;
- faacDecFrameInfo frame_info;
- unsigned char *mp4_buffer;
- unsigned int mp4_buffer_size;
- long sample_id;
- long num_samples;
- long dur;
- unsigned int sample_count;
- char *sample_buffer;
- size_t sample_buffer_length;
- unsigned int initial = 1;
- float *seek_table;
- long seek_table_end = -1;
- bool seek_position_found = false;
- long offset;
- uint16_t bit_rate = 0;
- bool seeking = false;
- double seek_where = 0;
- enum decoder_command cmd = DECODE_COMMAND_NONE;
-
- mp4fh = mp4ff_input_stream_open(&mis, mpd_decoder, input_stream);
- if (!mp4fh) {
- g_warning("Input does not appear to be a mp4 stream.\n");
- return;
- }
-
- decoder = mp4_faad_new(mp4fh, &track, &audio_format);
- if (decoder == NULL) {
- mp4ff_close(mp4fh);
- return;
- }
-
- file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
- scale = mp4ff_time_scale(mp4fh, track);
-
- if (scale < 0) {
- g_warning("Error getting audio format of mp4 AAC track.\n");
- faacDecClose(decoder);
- mp4ff_close(mp4fh);
- return;
- }
- total_time = ((float)file_time) / scale;
-
- num_samples = mp4ff_num_samples(mp4fh, track);
- if (num_samples > (long)(G_MAXINT / sizeof(float))) {
- g_warning("Integer overflow.\n");
- faacDecClose(decoder);
- mp4ff_close(mp4fh);
- return;
- }
-
- file_time = 0.0;
-
- seek_table = input_stream->seekable
- ? g_malloc(sizeof(float) * num_samples)
- : NULL;
-
- decoder_initialized(mpd_decoder, &audio_format,
- input_stream->seekable,
- total_time);
-
- for (sample_id = 0;
- sample_id < num_samples && cmd != DECODE_COMMAND_STOP;
- sample_id++) {
- if (cmd == DECODE_COMMAND_SEEK) {
- assert(seek_table != NULL);
-
- seeking = true;
- seek_where = decoder_seek_where(mpd_decoder);
- }
-
- if (seeking && seek_table_end > 1 &&
- seek_table[seek_table_end] >= seek_where) {
- int i = 2;
-
- assert(seek_table != NULL);
-
- while (seek_table[i] < seek_where)
- i++;
- sample_id = i - 1;
- file_time = seek_table[sample_id];
- }
-
- dur = mp4ff_get_sample_duration(mp4fh, track, sample_id);
- offset = mp4ff_get_sample_offset(mp4fh, track, sample_id);
-
- if (seek_table != NULL && sample_id > seek_table_end) {
- seek_table[sample_id] = file_time;
- seek_table_end = sample_id;
- }
-
- if (sample_id == 0)
- dur = 0;
- if (offset > dur)
- dur = 0;
- else
- dur -= offset;
- file_time += ((float)dur) / scale;
-
- if (seeking && file_time >= seek_where)
- seek_position_found = true;
-
- if (seeking && seek_position_found) {
- seek_position_found = false;
- seeking = 0;
- decoder_command_finished(mpd_decoder);
- }
-
- if (seeking)
- continue;
-
- if (mp4ff_read_sample(mp4fh, track, sample_id, &mp4_buffer,
- &mp4_buffer_size) == 0)
- break;
-
-#ifdef HAVE_FAAD_BUFLEN_FUNCS
- sample_buffer = faacDecDecode(decoder, &frame_info, mp4_buffer,
- mp4_buffer_size);
-#else
- sample_buffer = faacDecDecode(decoder, &frame_info, mp4_buffer);
-#endif
-
- free(mp4_buffer);
-
- if (frame_info.error > 0) {
- g_warning("faad2 error: %s\n",
- faacDecGetErrorMessage(frame_info.error));
- break;
- }
-
- if (frame_info.channels != audio_format.channels) {
- g_warning("channel count changed from %u to %u",
- audio_format.channels, frame_info.channels);
- break;
- }
-
-#ifdef HAVE_FAACDECFRAMEINFO_SAMPLERATE
- if (frame_info.samplerate != audio_format.sample_rate) {
- g_warning("sample rate changed from %u to %lu",
- audio_format.sample_rate,
- (unsigned long)frame_info.samplerate);
- break;
- }
-#endif
-
- if (audio_format.channels * (unsigned long)(dur + offset) > frame_info.samples) {
- dur = frame_info.samples / audio_format.channels;
- offset = 0;
- }
-
- sample_count = (unsigned long)(dur * audio_format.channels);
-
- if (sample_count > 0) {
- initial = 0;
- bit_rate = frame_info.bytesconsumed * 8.0 *
- frame_info.channels * scale /
- frame_info.samples / 1000 + 0.5;
- }
-
- sample_buffer_length = sample_count * 2;
-
- sample_buffer += offset * audio_format.channels * 2;
-
- cmd = decoder_data(mpd_decoder, input_stream,
- sample_buffer, sample_buffer_length,
- bit_rate);
- }
-
- g_free(seek_table);
- faacDecClose(decoder);
- mp4ff_close(mp4fh);
-}
-
-static const struct tag_table mp4ff_tags[] = {
- { "album artist", TAG_ALBUM_ARTIST },
- { "writer", TAG_COMPOSER },
- { "band", TAG_PERFORMER },
- { NULL, TAG_NUM_OF_ITEM_TYPES }
-};
-
-static enum tag_type
-mp4ff_tag_name_parse(const char *name)
-{
- enum tag_type type = tag_table_lookup_i(mp4ff_tags, name);
- if (type == TAG_NUM_OF_ITEM_TYPES)
- type = tag_name_parse_i(name);
-
- if (g_ascii_strcasecmp(name, "albumartist") == 0 ||
- g_ascii_strcasecmp(name, "album_artist") == 0)
- return TAG_ALBUM_ARTIST;
-
- return type;
-}
-
-static bool
-mp4ff_scan_stream(struct input_stream *is,
- const struct tag_handler *handler, void *handler_ctx)
-{
- struct mp4ff_input_stream mis;
- int32_t track;
- int32_t file_time;
- int32_t scale;
- int i;
-
- mp4ff_t *mp4fh = mp4ff_input_stream_open(&mis, NULL, is);
- if (mp4fh == NULL)
- return false;
-
- track = mp4_get_aac_track(mp4fh, NULL, NULL, NULL);
- if (track < 0) {
- mp4ff_close(mp4fh);
- return false;
- }
-
- file_time = mp4ff_get_track_duration_use_offsets(mp4fh, track);
- scale = mp4ff_time_scale(mp4fh, track);
- if (scale < 0) {
- mp4ff_close(mp4fh);
- return false;
- }
-
- tag_handler_invoke_duration(handler, handler_ctx,
- ((float)file_time) / scale + 0.5);
-
- for (i = 0; i < mp4ff_meta_get_num_items(mp4fh); i++) {
- char *item;
- char *value;
-
- mp4ff_meta_get_by_index(mp4fh, i, &item, &value);
-
- tag_handler_invoke_pair(handler, handler_ctx, item, value);
-
- enum tag_type type = mp4ff_tag_name_parse(item);
- if (type != TAG_NUM_OF_ITEM_TYPES)
- tag_handler_invoke_tag(handler, handler_ctx,
- type, value);
-
- free(item);
- free(value);
- }
-
- mp4ff_close(mp4fh);
-
- return true;
-}
-
-static const char *const mp4_suffixes[] = {
- "m4a",
- "m4b",
- "mp4",
- NULL
-};
-
-static const char *const mp4_mime_types[] = { "audio/mp4", "audio/m4a", NULL };
-
-const struct decoder_plugin mp4ff_decoder_plugin = {
- .name = "mp4ff",
- .stream_decode = mp4_decode,
- .scan_stream = mp4ff_scan_stream,
- .suffixes = mp4_suffixes,
- .mime_types = mp4_mime_types,
-};
diff --git a/src/decoder/mpcdec_decoder_plugin.c b/src/decoder/mpcdec_decoder_plugin.c
index d4768b35..77db2416 100644
--- a/src/decoder/mpcdec_decoder_plugin.c
+++ b/src/decoder/mpcdec_decoder_plugin.c
@@ -70,7 +70,7 @@ mpc_tell_cb(cb_first_arg)
{
struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
- return (long)(data->is->offset);
+ return (long)input_stream_get_offset(data->is);
}
static mpc_bool_t
@@ -78,7 +78,7 @@ mpc_canseek_cb(cb_first_arg)
{
struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
- return data->is->seekable;
+ return input_stream_is_seekable(data->is);
}
static mpc_int32_t
@@ -86,7 +86,7 @@ mpc_getsize_cb(cb_first_arg)
{
struct mpc_decoder_data *data = (struct mpc_decoder_data *) cb_data;
- return data->is->size;
+ return input_stream_get_size(data->is);
}
/* this _looks_ performance-critical, don't de-inline -- eric */
@@ -222,7 +222,7 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
decoder_replay_gain(mpd_decoder, &replay_gain_info);
decoder_initialized(mpd_decoder, &audio_format,
- is->seekable,
+ input_stream_is_seekable(is),
mpc_streaminfo_get_length(&info));
do {
diff --git a/src/decoder/pcm_decoder_plugin.c b/src/decoder/pcm_decoder_plugin.c
index fc7dffc0..d529cef5 100644
--- a/src/decoder/pcm_decoder_plugin.c
+++ b/src/decoder/pcm_decoder_plugin.c
@@ -38,8 +38,9 @@ pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
.channels = 2,
};
- const bool reverse_endian = is->mime != NULL &&
- strcmp(is->mime, "audio/x-mpd-cdda-pcm-reverse") == 0;
+ const char *const mime = input_stream_get_mime_type(is);
+ const bool reverse_endian = mime != NULL &&
+ strcmp(mime, "audio/x-mpd-cdda-pcm-reverse") == 0;
GError *error = NULL;
enum decoder_command cmd;
@@ -47,10 +48,12 @@ pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
double time_to_size = audio_format_time_to_size(&audio_format);
float total_time = -1;
- if (is->size >= 0)
- total_time = is->size / time_to_size;
+ const goffset size = input_stream_get_size(is);
+ if (size >= 0)
+ total_time = size / time_to_size;
- decoder_initialized(decoder, &audio_format, is->seekable, total_time);
+ decoder_initialized(decoder, &audio_format,
+ input_stream_is_seekable(is), total_time);
do {
char buffer[4096];
diff --git a/src/decoder/sidplay_decoder_plugin.cxx b/src/decoder/sidplay_decoder_plugin.cxx
index 5d162f17..175d2ee7 100644
--- a/src/decoder/sidplay_decoder_plugin.cxx
+++ b/src/decoder/sidplay_decoder_plugin.cxx
@@ -18,9 +18,9 @@
*/
#include "config.h"
+#include "../decoder_api.h"
extern "C" {
-#include "../decoder_api.h"
#include "tag_handler.h"
}
@@ -104,7 +104,7 @@ sidplay_init(const struct config_param *param)
return true;
}
-void
+static void
sidplay_finish()
{
g_pattern_spec_free(path_with_subtune);
@@ -136,7 +136,7 @@ get_container_name(const char *path_fs)
* returns tune number from file.sid/tune_xxx.sid style path or 1 if
* no subtune is appended
*/
-static int
+static unsigned
get_song_num(const char *path_fs)
{
if(g_pattern_match(path_with_subtune,
@@ -172,7 +172,7 @@ get_song_length(const char *path_fs)
char md5sum[SIDTUNE_MD5_LENGTH+1];
tune.createMD5(md5sum);
- int song_num=get_song_num(path_fs);
+ const unsigned song_num = get_song_num(path_fs);
gsize num_items;
gchar **values=g_key_file_get_string_list(songlength_database,
@@ -330,7 +330,7 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
decoder_command_finished(decoder);
}
- if (song_len > 0 && player.time() >= song_len)
+ if (song_len > 0 && player.time() >= (unsigned)song_len)
break;
} while (cmd != DECODE_COMMAND_STOP);
diff --git a/src/decoder/sndfile_decoder_plugin.c b/src/decoder/sndfile_decoder_plugin.c
index 8dd98236..e70a2dc2 100644
--- a/src/decoder/sndfile_decoder_plugin.c
+++ b/src/decoder/sndfile_decoder_plugin.c
@@ -32,7 +32,7 @@ sndfile_vio_get_filelen(void *user_data)
{
const struct input_stream *is = user_data;
- return is->size;
+ return input_stream_get_size(is);
}
static sf_count_t
@@ -45,7 +45,7 @@ sndfile_vio_seek(sf_count_t offset, int whence, void *user_data)
if (!success)
return -1;
- return is->offset;
+ return input_stream_get_offset(is);
}
static sf_count_t
@@ -79,7 +79,7 @@ sndfile_vio_tell(void *user_data)
{
const struct input_stream *is = user_data;
- return is->offset;
+ return input_stream_get_offset(is);
}
/**