Compare commits

...

28 Commits

Author SHA1 Message Date
Avuton Olrich
56bf4ede18 mpd version 0.15.11 2010-07-14 17:33:28 -07:00
Max Kellermann
49bc317fb8 decoder/ffmpeg: fix libavformat 0.6 by using av_open_input_stream()
libavformat 0.6 does not pass the original URI pointer to the "open"
method, which leads to a crash because MPD was using a dirty hack to
pass a pointer to that method.

This patch switches to av_open_input_stream() with a custom
ByteIOContext class, instead of doing the URI string hack with
av_open_input_file().

Loosely based on a patch from Jasper St. Pierre.
2010-06-30 23:41:32 +02:00
Max Kellermann
375a09d6f6 decoder/ffmpeg: manual format probing
Use the libavformat function av_probe_input_format() to probe the
AVInputFormat, instead of letting av_open_input_file() do it
implicitly.  We will switch to av_open_input_stream() very soon, which
does not have the probing code.

Loosely based on a patch from Jasper St. Pierre.
2010-06-30 23:40:31 +02:00
Max Kellermann
0265c34bed decoder/ffmpeg: free URI, fix memory leak
Free the string allocated by decoder_get_uri().
2010-06-30 23:40:04 +02:00
Max Kellermann
a1882f48be decoder/vorbis: handle uri==NULL
This fixes a theoretical crash, which has never occurred in practice.
2010-06-30 23:38:49 +02:00
Anton Khirnov
c3569814bd ffmpeg: read more metadata. 2010-06-30 23:18:45 +02:00
Max Kellermann
814daac5ba decoder/ffmpeg: free AVFormatContext on error
Fix a memory leak in some code paths.
2010-06-30 23:14:43 +02:00
Max Kellermann
0d03bdce6d configure.ac: check ffmpeg version number with pkg-config
Replace the check for avcodec_decode_audio2(), assume it's present in
libavcodec version 51.
2010-06-30 21:56:04 +02:00
Max Kellermann
768be22f7c pcm_buffer: make the buffer pointer "void" 2010-06-30 21:55:46 +02:00
Max Kellermann
ec89ce5a8a decoder/mp4ff: support tag "album artist"
We already supported "albumartist", but it seems some folks also use
"album artist" (with a space).
2010-06-30 21:55:03 +02:00
Andreas Vögele
34415bf0b6 Make get_remote_uid() work on BSD
I've attached a patch that will make file URIs work on operating systems
that provide the getpeereid() function call to check the user ID of the
peer connected to a UNIX domain socket.
2010-06-30 21:42:01 +02:00
Max Kellermann
0a0c78674f playlist: emit IDLE_OPTIONS when resetting single mode 2010-06-30 21:40:33 +02:00
Max Kellermann
1bffdabe41 directory_print: return void
There is no useful return value here.
2010-06-30 21:39:34 +02:00
Max Kellermann
77e6810c14 decoder/mikmod: fix memory leak
The return value of Player_LoadTitle() is allocated with malloc(), and
must be freed by the caller.
2010-06-30 19:37:36 +00:00
Max Kellermann
5ebe33653c decoder/mp4ff: remove duplicate entries in the tag name table
Reuse the function tag_name_parse_i().
2010-06-30 21:36:15 +02:00
Max Kellermann
8e3eace289 decoder/mp4ff: moved code to mp4ff_tag_name_parse() 2010-06-30 21:36:00 +02:00
Max Kellermann
284659034d tag_ape: remove duplicate entries in the tag name table
Reuse the function tag_name_parse_i().
2010-06-30 21:33:19 +02:00
Max Kellermann
9550c87327 tag: added function tag_name_parse()
Convert a string into a tag_type enum.
2010-06-30 21:31:45 +02:00
Max Kellermann
e223e8a5b5 tag_ape: move code to tag_ape_name_parse() 2010-06-30 21:30:21 +02:00
Max Kellermann
4d6d372a5b decoder/vorbis: use single global ov_callbacks constant
Initialize the ov_callbacks struct at compile time.
2010-06-30 19:24:41 +00:00
Max Kellermann
0aeec90590 decoder/mp4ff: support tags "albumartist", "band"
I'm not sure if mapping "band" to TAG_PERFORMER is correct, but it
might be better than nothing.
2010-06-30 21:22:13 +02:00
Max Kellermann
cfcd84655c decoder/mp4ff: use tag_table.h to parse tag names
Convert if/else/else/... to a loop.
2010-06-30 21:19:30 +02:00
Max Kellermann
5092eaf1cc tag_ape: move table lookup to tag_table.h
Allow code sharing.
2010-06-30 21:18:27 +02:00
Max Kellermann
9328558fc7 tag_ape: support album artist
I took this tag name from a MusePack sample file I got from a user.
It is not documented in the APE specification:

 http://wiki.hydrogenaudio.org/index.php?title=APE_key

People seem to be using undocumented extensions to the specification
anyway, and the best we can do is attempt to support them.
2010-06-30 21:16:28 +02:00
Max Kellermann
026bd15872 tag_ape: simplified the apeItems array
Make "enum tag_type" the array index, and convert apeItems to a sparse
array.
2010-06-30 21:15:52 +02:00
Max Kellermann
7cca55549b tag_ape: moved code to tag_ape_import_item()
Improve code readability.
2010-06-30 21:15:39 +02:00
Max Kellermann
c7e89ea3a3 tag_ape: converted apeItems and tagItems to global vars
Don't initialize those arrays each time tag_ape_load() is called.
2010-06-30 21:15:14 +02:00
Avuton Olrich
65ad298460 Modify version string to post-release version 0.15.11~git 2010-05-30 08:59:00 -07:00
17 changed files with 312 additions and 165 deletions

@@ -142,6 +142,7 @@ mpd_headers = \
src/tag.h \
src/tag_internal.h \
src/tag_pool.h \
src/tag_table.h \
src/tag_ape.h \
src/tag_id3.h \
src/tag_print.h \

15
NEWS

@@ -1,3 +1,18 @@
ver 0.15.11 (2010/06/14)
* tags:
- ape: support album artist
* decoders:
- mp4ff: support tags "album artist", "albumartist", "band"
- mikmod: fix memory leak
- vorbis: handle uri==NULL
- ffmpeg: fix memory leak
- ffmpeg: free AVFormatContext on error
- ffmpeg: read more metadata
- ffmpeg: fix libavformat 0.6 by using av_open_input_stream()
* playlist: emit IDLE_OPTIONS when resetting single mode
* listen: make get_remote_uid() work on BSD
ver 0.15.10 (2010/05/30)
* input:
- mms: fix memory leak in error handler

@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.15.10, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.15.11, musicpd-dev-team@lists.sourceforge.net)
AC_CONFIG_SRCDIR([src/main.c])
AM_INIT_AUTOMAKE([foreign 1.9 dist-bzip2])
AM_CONFIG_HEADER(config.h)
@@ -177,6 +177,7 @@ AC_ARG_ENABLE(un,
if test x$enable_un = xyes; then
AC_DEFINE(HAVE_UN, 1, [Define if unix domain socket support is enabled])
STRUCT_UCRED
AC_CHECK_FUNCS(getpeereid)
fi
@@ -913,17 +914,9 @@ fi
AM_CONDITIONAL(HAVE_AUDIOFILE, test x$enable_audiofile = xyes)
MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat libavcodec libavutil],
MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 52 libavcodec >= 51 libavutil >= 49],
[ffmpeg decoder library], [libavformat+libavcodec+libavutil not found])
if test x$enable_ffmpeg = xyes; then
old_LIBS=$LIBS
LIBS="$LIBS $FFMPEG_LIBS"
AC_CHECK_LIB(avcodec, avcodec_decode_audio2,,
enable_ffmpeg=no)
LIBS=$old_LIBS
fi
if test x$enable_ffmpeg = xyes; then
# prior to ffmpeg svn12865, you had to specify include files
# without path prefix

@@ -53,48 +53,27 @@ struct ffmpeg_context {
struct tag *tag;
};
struct ffmpeg_stream {
/** hack - see url_to_struct() */
char url[64];
struct mpd_ffmpeg_stream {
struct decoder *decoder;
struct input_stream *input;
ByteIOContext *io;
unsigned char buffer[8192];
};
/**
* Convert a faked mpd:// URL to a ffmpeg_stream structure. This is a
* hack because ffmpeg does not provide a nice API for passing a
* user-defined pointer to mpdurl_open().
*/
static struct ffmpeg_stream *url_to_struct(const char *url)
static int
mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size)
{
union {
const char *in;
struct ffmpeg_stream *out;
} u = { .in = url };
return u.out;
}
static int mpd_ffmpeg_open(URLContext *h, const char *filename,
G_GNUC_UNUSED int flags)
{
struct ffmpeg_stream *stream = url_to_struct(filename);
h->priv_data = stream;
h->is_streamed = stream->input->seekable ? 0 : 1;
return 0;
}
static int mpd_ffmpeg_read(URLContext *h, unsigned char *buf, int size)
{
struct ffmpeg_stream *stream = (struct ffmpeg_stream *) h->priv_data;
struct mpd_ffmpeg_stream *stream = opaque;
return decoder_read(stream->decoder, stream->input,
(void *)buf, size);
}
static int64_t mpd_ffmpeg_seek(URLContext *h, int64_t pos, int whence)
static int64_t
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
{
struct ffmpeg_stream *stream = (struct ffmpeg_stream *) h->priv_data;
struct mpd_ffmpeg_stream *stream = opaque;
bool ret;
if (whence == AVSEEK_SIZE)
@@ -107,25 +86,36 @@ static int64_t mpd_ffmpeg_seek(URLContext *h, int64_t pos, int whence)
return stream->input->offset;
}
static int mpd_ffmpeg_close(URLContext *h)
static struct mpd_ffmpeg_stream *
mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
{
h->priv_data = NULL;
return 0;
struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
stream->decoder = decoder;
stream->input = input;
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);
if (stream->io == NULL) {
g_free(stream);
return NULL;
}
return stream;
}
static URLProtocol mpd_ffmpeg_fileops = {
.name = "mpd",
.url_open = mpd_ffmpeg_open,
.url_read = mpd_ffmpeg_read,
.url_seek = mpd_ffmpeg_seek,
.url_close = mpd_ffmpeg_close,
};
static void
mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream)
{
av_free(stream->io);
g_free(stream);
}
static bool
ffmpeg_init(G_GNUC_UNUSED const struct config_param *param)
{
av_register_all();
register_protocol(&mpd_ffmpeg_fileops);
return true;
}
@@ -140,64 +130,86 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
return -1;
}
/**
* Append the suffix of the original URI to the virtual stream URI.
* Without this, libavformat cannot detect some of the codecs
* (e.g. "shorten").
*/
static void
append_uri_suffix(struct ffmpeg_stream *stream, const char *uri)
static AVInputFormat *
ffmpeg_probe(struct decoder *decoder, struct input_stream *is,
const char *uri)
{
assert(stream != NULL);
assert(uri != NULL);
enum {
BUFFER_SIZE = 16384,
PADDING = 16,
};
char *base = g_path_get_basename(uri);
unsigned char *buffer = g_malloc(BUFFER_SIZE);
size_t nbytes = decoder_read(decoder, is, buffer, BUFFER_SIZE);
if (nbytes <= PADDING || !input_stream_seek(is, 0, SEEK_SET)) {
g_free(buffer);
return NULL;
}
const char *suffix = strrchr(base, '.');
if (suffix != NULL && suffix[1] != 0)
g_strlcat(stream->url, suffix, sizeof(stream->url));
/* some ffmpeg parsers (e.g. ac3_parser.c) read a few bytes
beyond the declared buffer limit, which makes valgrind
angry; this workaround removes some padding from the buffer
size */
nbytes -= PADDING;
g_free(base);
AVProbeData avpd = {
.buf = buffer,
.buf_size = nbytes,
.filename = uri,
};
AVInputFormat *format = av_probe_input_format(&avpd, true);
g_free(buffer);
return format;
}
static bool
ffmpeg_helper(const char *uri, struct input_stream *input,
ffmpeg_helper(const char *uri,
struct decoder *decoder, struct input_stream *input,
bool (*callback)(struct ffmpeg_context *ctx),
struct ffmpeg_context *ctx)
{
AVInputFormat *input_format = ffmpeg_probe(decoder, input, uri);
if (input_format == NULL)
return false;
g_debug("detected input format '%s' (%s)",
input_format->name, input_format->long_name);
struct mpd_ffmpeg_stream *stream =
mpd_ffmpeg_stream_open(decoder, input);
if (stream == NULL) {
g_warning("Failed to open stream");
return false;
}
AVFormatContext *format_context;
AVCodecContext *codec_context;
AVCodec *codec;
int audio_stream;
struct ffmpeg_stream stream = {
.url = "mpd://X", /* only the mpd:// prefix matters */
};
bool ret;
if (uri != NULL)
append_uri_suffix(&stream, uri);
stream.input = input;
if (ctx && ctx->decoder) {
stream.decoder = ctx->decoder; //are we in decoding loop ?
} else {
stream.decoder = NULL;
}
//ffmpeg works with ours "fileops" helper
if (av_open_input_file(&format_context, stream.url, NULL, 0, NULL) != 0) {
if (av_open_input_stream(&format_context, stream->io, uri,
input_format, NULL) != 0) {
g_warning("Open failed\n");
mpd_ffmpeg_stream_close(stream);
return false;
}
if (av_find_stream_info(format_context)<0) {
g_warning("Couldn't find stream info\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return false;
}
audio_stream = ffmpeg_find_audio_stream(format_context);
if (audio_stream == -1) {
g_warning("No audio stream inside\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return false;
}
@@ -209,11 +221,15 @@ ffmpeg_helper(const char *uri, struct input_stream *input,
if (!codec) {
g_warning("Unsupported audio codec\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return false;
}
if (avcodec_open(codec_context, codec)<0) {
g_warning("Could not open codec\n");
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return false;
}
@@ -227,7 +243,8 @@ ffmpeg_helper(const char *uri, struct input_stream *input,
ret = true;
avcodec_close(codec_context);
av_close_input_file(format_context);
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return ret;
}
@@ -372,8 +389,10 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
ctx.input = input;
ctx.decoder = decoder;
ffmpeg_helper(decoder_get_uri(decoder), input,
char *uri = decoder_get_uri(decoder);
ffmpeg_helper(uri, decoder, input,
ffmpeg_decode_internal, &ctx);
g_free(uri);
}
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
@@ -401,12 +420,21 @@ static bool ffmpeg_tag_internal(struct ffmpeg_context *ctx)
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_TITLE, "title");
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(50<<8))
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ARTIST, "artist");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_DATE, "date");
#else
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ARTIST, "author");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_DATE, "year");
#endif
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ALBUM, "album");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_COMMENT, "comment");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_GENRE, "genre");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_TRACK, "track");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_DATE, "year");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ALBUM_ARTIST, "album_artist");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_COMPOSER, "composer");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_PERFORMER, "performer");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_DISC, "disc");
#else
if (f->author[0])
tag_add_item(tag, TAG_ITEM_ARTIST, f->author);
@@ -450,7 +478,7 @@ static struct tag *ffmpeg_tag(const char *file)
ctx.decoder = NULL;
ctx.tag = tag_new();
ret = ffmpeg_helper(file, &input, ffmpeg_tag_internal, &ctx);
ret = ffmpeg_helper(file, NULL, &input, ffmpeg_tag_internal, &ctx);
if (!ret) {
tag_free(ctx.tag);
ctx.tag = NULL;

@@ -219,10 +219,12 @@ static struct tag *modTagDup(const char *file)
ret->time = 0;
path2 = g_strdup(file);
title = g_strdup(Player_LoadTitle(path2));
title = Player_LoadTitle(path2);
g_free(path2);
if (title)
if (title) {
tag_add_item(ret, TAG_ITEM_TITLE, title);
free(title);
}
return ret;
}

@@ -19,6 +19,7 @@
#include "../decoder_api.h"
#include "config.h"
#include "tag_table.h"
#include <glib.h>
@@ -339,6 +340,22 @@ mp4_decode(struct decoder *mpd_decoder, struct input_stream *input_stream)
mp4ff_close(mp4fh);
}
static const char *const mp4ff_tag_names[TAG_NUM_OF_ITEM_TYPES] = {
[TAG_ITEM_ALBUM_ARTIST] = "album artist",
[TAG_ITEM_COMPOSER] = "writer",
[TAG_ITEM_PERFORMER] = "band",
};
static enum tag_type
mp4ff_tag_name_parse(const char *name)
{
enum tag_type type = tag_table_lookup(mp4ff_tag_names, name);
if (type == TAG_NUM_OF_ITEM_TYPES)
type = tag_name_parse_i(name);
return type;
}
static struct tag *
mp4_tag_dup(const char *file)
{
@@ -394,24 +411,9 @@ mp4_tag_dup(const char *file)
mp4ff_meta_get_by_index(mp4fh, i, &item, &value);
if (0 == g_ascii_strcasecmp("artist", item)) {
tag_add_item(ret, TAG_ITEM_ARTIST, value);
} else if (0 == g_ascii_strcasecmp("title", item)) {
tag_add_item(ret, TAG_ITEM_TITLE, value);
} else if (0 == g_ascii_strcasecmp("album", item)) {
tag_add_item(ret, TAG_ITEM_ALBUM, value);
} else if (0 == g_ascii_strcasecmp("track", item)) {
tag_add_item(ret, TAG_ITEM_TRACK, value);
} else if (0 == g_ascii_strcasecmp("disc", item)) {
/* Is that the correct id? */
tag_add_item(ret, TAG_ITEM_DISC, value);
} else if (0 == g_ascii_strcasecmp("genre", item)) {
tag_add_item(ret, TAG_ITEM_GENRE, value);
} else if (0 == g_ascii_strcasecmp("date", item)) {
tag_add_item(ret, TAG_ITEM_DATE, value);
} else if (0 == g_ascii_strcasecmp("writer", item)) {
tag_add_item(ret, TAG_ITEM_COMPOSER, value);
}
enum tag_type type = mp4ff_tag_name_parse(item);
if (type != TAG_NUM_OF_ITEM_TYPES)
tag_add_item(ret, type, value);
free(item);
free(value);

@@ -97,6 +97,13 @@ static long ogg_tell_cb(void *vdata)
return (long)data->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,
};
static const char *
vorbis_comment_value(const char *comment, const char *needle)
{
@@ -226,6 +233,9 @@ oggvorbis_seekable(struct decoder *decoder)
bool seekable;
uri = decoder_get_uri(decoder);
if (uri == NULL)
return false;
/* disable seeking on remote streams, because libvorbis seeks
around like crazy, and due to being very expensive, this
delays song playback my 10 or 20 seconds */
@@ -241,7 +251,6 @@ vorbis_stream_decode(struct decoder *decoder,
struct input_stream *input_stream)
{
OggVorbis_File vf;
ov_callbacks callbacks;
OggCallbackData data;
struct audio_format audio_format;
int current_section;
@@ -266,13 +275,9 @@ vorbis_stream_decode(struct decoder *decoder,
data.input_stream = input_stream;
data.seekable = input_stream->seekable && oggvorbis_seekable(decoder);
callbacks.read_func = ogg_read_cb;
callbacks.seek_func = ogg_seek_cb;
callbacks.close_func = ogg_close_cb;
callbacks.tell_func = ogg_tell_cb;
if ((ret = ov_open_callbacks(&data, &vf, NULL, 0, callbacks)) < 0) {
if ((ret = ov_open_callbacks(&data, &vf, NULL, 0,
vorbis_is_callbacks)) < 0) {
const char *error;
if (decoder_get_command(decoder) != DECODE_COMMAND_NONE)
return;

@@ -22,7 +22,7 @@
#include "client.h"
#include "song_print.h"
static int
static void
dirvec_print(struct client *client, const struct dirvec *dv)
{
size_t i;
@@ -30,15 +30,11 @@ dirvec_print(struct client *client, const struct dirvec *dv)
for (i = 0; i < dv->nr; ++i)
client_printf(client, DIRECTORY_DIR "%s\n",
directory_get_path(dv->base[i]));
return 0;
}
int
void
directory_print(struct client *client, const struct directory *directory)
{
dirvec_print(client, &directory->children);
songvec_print(client, &directory->songs);
return 0;
}

@@ -23,7 +23,7 @@
struct client;
struct directory;
int
void
directory_print(struct client *client, const struct directory *directory);
#endif

@@ -407,7 +407,13 @@ static int get_remote_uid(int fd)
return cred.uid;
#else
(void)fd;
#ifdef HAVE_GETPEEREID
uid_t euid;
gid_t egid;
if (getpeereid(fd, &euid, &egid) == 0)
return euid;
#endif
return -1;
#endif
}

@@ -42,9 +42,9 @@ locate_parse_type(const char *str)
if (0 == g_ascii_strcasecmp(str, LOCATE_TAG_ANY_KEY))
return LOCATE_TAG_ANY_TYPE;
for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++)
if (0 == g_ascii_strcasecmp(str, tag_item_names[i]))
return i;
i = tag_name_parse_i(str);
if (i != TAG_NUM_OF_ITEM_TYPES)
return i;
return -1;
}

@@ -28,7 +28,7 @@
* would put too much stress on the allocator.
*/
struct pcm_buffer {
char *buffer;
void *buffer;
size_t size;
};

@@ -24,6 +24,7 @@
#include "playlist_internal.h"
#include "player_control.h"
#include "idle.h"
#include <glib.h>
@@ -156,6 +157,8 @@ nextSongInPlaylist(struct playlist *playlist)
if (next_order < 0) {
/* cancel single */
playlist->queue.single = false;
idle_add(IDLE_OPTIONS);
/* no song after this one: stop playback */
stopPlaylist(playlist);

@@ -64,6 +64,36 @@ const char *tag_item_names[TAG_NUM_OF_ITEM_TYPES] = {
bool ignore_tag_items[TAG_NUM_OF_ITEM_TYPES];
enum tag_type
tag_name_parse(const char *name)
{
assert(name != NULL);
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
assert(tag_item_names[i] != NULL);
if (strcmp(name, tag_item_names[i]) == 0)
return (enum tag_type)i;
}
return TAG_NUM_OF_ITEM_TYPES;
}
enum tag_type
tag_name_parse_i(const char *name)
{
assert(name != NULL);
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
assert(tag_item_names[i] != NULL);
if (g_ascii_strcasecmp(name, tag_item_names[i]) == 0)
return (enum tag_type)i;
}
return TAG_NUM_OF_ITEM_TYPES;
}
static size_t items_size(const struct tag *tag)
{
return tag->num_items * sizeof(struct tag_item *);
@@ -76,7 +106,7 @@ void tag_lib_init(void)
char *temp;
char *s;
char *c;
int i;
enum tag_type type;
/* parse the "metadata_to_use" config parameter below */
@@ -98,16 +128,18 @@ void tag_lib_init(void)
if (*s == '\0')
quit = 1;
*s = '\0';
for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) {
if (g_ascii_strcasecmp(c, tag_item_names[i]) == 0) {
ignore_tag_items[i] = false;
break;
}
}
if (strlen(c) && i == TAG_NUM_OF_ITEM_TYPES) {
c = g_strstrip(c);
if (*c == 0)
continue;
type = tag_name_parse_i(c);
if (type == TAG_NUM_OF_ITEM_TYPES)
g_error("error parsing metadata item \"%s\"",
c);
}
ignore_tag_items[type] = false;
s++;
c = s;
}

@@ -93,6 +93,22 @@ struct tag {
unsigned num_items;
};
/**
* Parse the string, and convert it into a #tag_type. Returns
* #TAG_NUM_OF_ITEM_TYPES if the string could not be recognized.
*/
enum tag_type
tag_name_parse(const char *name);
/**
* Parse the string, and convert it into a #tag_type. Returns
* #TAG_NUM_OF_ITEM_TYPES if the string could not be recognized.
*
* Case does not matter.
*/
enum tag_type
tag_name_parse_i(const char *name);
/**
* Creates an empty #tag.
*/

@@ -19,12 +19,47 @@
#include "tag_ape.h"
#include "tag.h"
#include "tag_table.h"
#include <glib.h>
#include <assert.h>
#include <stdio.h>
static const char *const ape_tag_names[TAG_NUM_OF_ITEM_TYPES] = {
[TAG_ITEM_ALBUM_ARTIST] = "album artist",
[TAG_ITEM_DATE] = "year"
};
static enum tag_type
tag_ape_name_parse(const char *name)
{
enum tag_type type = tag_table_lookup(ape_tag_names, name);
if (type == TAG_NUM_OF_ITEM_TYPES)
type = tag_name_parse_i(name);
return type;
}
static struct tag *
tag_ape_import_item(struct tag *tag, unsigned long flags,
const char *key, const char *value, size_t value_length)
{
/* we only care about utf-8 text tags */
if ((flags & (0x3 << 1)) != 0)
return tag;
enum tag_type type = tag_ape_name_parse(key);
if (type == TAG_NUM_OF_ITEM_TYPES)
return tag;
if (tag == NULL)
tag = tag_new();
tag_add_item_n(tag, type, value, value_length);
return tag;
}
struct tag *
tag_ape_load(const char *file)
{
@@ -36,7 +71,6 @@ tag_ape_load(const char *file)
size_t tagLen;
size_t size;
unsigned long flags;
int i;
char *key;
struct {
@@ -48,26 +82,6 @@ tag_ape_load(const char *file)
unsigned char reserved[8];
} footer;
const char *apeItems[7] = {
"title",
"artist",
"album",
"comment",
"genre",
"track",
"year"
};
int tagItems[7] = {
TAG_ITEM_TITLE,
TAG_ITEM_ARTIST,
TAG_ITEM_ALBUM,
TAG_ITEM_COMMENT,
TAG_ITEM_GENRE,
TAG_ITEM_TRACK,
TAG_ITEM_DATE,
};
fp = fopen(file, "r");
if (!fp)
return NULL;
@@ -127,17 +141,8 @@ tag_ape_load(const char *file)
if (tagLen < size)
goto fail;
/* we only care about utf-8 text tags */
if (!(flags & (0x3 << 1))) {
for (i = 0; i < 7; i++) {
if (g_ascii_strcasecmp(key, apeItems[i]) == 0) {
if (!ret)
ret = tag_new();
tag_add_item_n(ret, tagItems[i],
p, size);
}
}
}
ret = tag_ape_import_item(ret, flags, key, p, size);
p += size;
tagLen -= size;
}

43
src/tag_table.h Normal file

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2003-2010 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_TAG_TABLE_H
#define MPD_TAG_TABLE_H
#include "tag.h"
#include <glib.h>
/**
* Looks up a string in a tag translation table (case insensitive).
* Returns TAG_NUM_OF_ITEM_TYPES if the specified name was not found
* in the table.
*/
static inline enum tag_type
tag_table_lookup(const char *const* table, const char *name)
{
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++)
if (table[i] != NULL &&
g_ascii_strcasecmp(name, table[i]) == 0)
return (enum tag_type)i;
return TAG_NUM_OF_ITEM_TYPES;
}
#endif