diff options
Diffstat (limited to '')
32 files changed, 642 insertions, 329 deletions
diff --git a/Makefile.am b/Makefile.am index 2b98e6ba5..636bf0648 100644 --- a/Makefile.am +++ b/Makefile.am @@ -335,6 +335,7 @@ src_mpd_SOURCES = \ src/tag_pool.c \ src/tag_print.c \ src/tag_save.c \ + src/tag_handler.c src/tag_handler.h \ src/tokenizer.c \ src/text_file.c \ src/text_input_stream.c \ @@ -1006,6 +1007,7 @@ test_dump_playlist_SOURCES = test/dump_playlist.c \ src/conf.c src/tokenizer.c src/utils.c src/string_util.c\ src/uri.c \ src/song.c src/tag.c src/tag_pool.c src/tag_save.c \ + src/tag_handler.c \ src/text_input_stream.c src/fifo_buffer.c \ src/cue/cue_parser.c src/cue/cue_parser.h \ src/fd_util.c @@ -1027,7 +1029,7 @@ test_run_decoder_SOURCES = test/run_decoder.c \ test/stdbin.h \ src/io_thread.c src/io_thread.h \ src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \ - src/tag.c src/tag_pool.c \ + src/tag.c src/tag_pool.c src/tag_handler.c \ src/replay_gain_info.c \ src/uri.c \ src/fd_util.c \ @@ -1049,7 +1051,7 @@ test_read_tags_LDADD = \ test_read_tags_SOURCES = test/read_tags.c \ src/io_thread.c src/io_thread.h \ src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \ - src/tag.c src/tag_pool.c \ + src/tag.c src/tag_pool.c src/tag_handler.c \ src/replay_gain_info.c \ src/uri.c \ src/fd_util.c \ diff --git a/src/decoder/audiofile_decoder_plugin.c b/src/decoder/audiofile_decoder_plugin.c index 8d6236a57..7db7c7517 100644 --- a/src/decoder/audiofile_decoder_plugin.c +++ b/src/decoder/audiofile_decoder_plugin.c @@ -20,6 +20,7 @@ #include "config.h" #include "decoder_api.h" #include "audio_check.h" +#include "tag_handler.h" #include <audiofile.h> #include <af_vfs.h> @@ -222,20 +223,20 @@ audiofile_stream_decode(struct decoder *decoder, struct input_stream *is) afCloseFile(af_fp); } -static struct tag *audiofile_tag_dup(const char *file) +static bool +audiofile_scan_file(const char *file, + const struct tag_handler *handler, void *handler_ctx) { - struct tag *ret = NULL; int total_time = audiofile_get_duration(file); - if (total_time >= 0) { - ret = tag_new(); - ret->time = total_time; - } else { + if (total_time < 0) { g_debug("Failed to get total song time from: %s\n", file); + return false; } - return ret; + tag_handler_invoke_duration(handler, handler_ctx, total_time); + return true; } static const char *const audiofile_suffixes[] = { @@ -251,7 +252,7 @@ static const char *const audiofile_mime_types[] = { const struct decoder_plugin audiofile_decoder_plugin = { .name = "audiofile", .stream_decode = audiofile_stream_decode, - .tag_dup = audiofile_tag_dup, + .scan_file = audiofile_scan_file, .suffixes = audiofile_suffixes, .mime_types = audiofile_mime_types, }; diff --git a/src/decoder/dsdiff_decoder_plugin.c b/src/decoder/dsdiff_decoder_plugin.c index 47b71d8c0..391a5bf78 100644 --- a/src/decoder/dsdiff_decoder_plugin.c +++ b/src/decoder/dsdiff_decoder_plugin.c @@ -433,8 +433,10 @@ dsdiff_stream_decode(struct decoder *decoder, struct input_stream *is) dsd2pcm_destroy(dsd2pcm[i]); } -static struct tag * -dsdiff_stream_tag(struct input_stream *is) +static bool +dsdiff_scan_stream(struct input_stream *is, + G_GNUC_UNUSED const struct tag_handler *handler, + G_GNUC_UNUSED void *handler_ctx) { struct dsdiff_metadata metadata = { .sample_rate = 0, @@ -443,17 +445,17 @@ dsdiff_stream_tag(struct input_stream *is) struct dsdiff_chunk_header chunk_header; if (!dsdiff_read_metadata(NULL, is, &metadata, &chunk_header)) - return NULL; + return false; struct audio_format audio_format; if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8, SAMPLE_FORMAT_S24_P32, metadata.channels, NULL)) /* refuse to parse files which we cannot play anyway */ - return NULL; + return false; /* no total time estimate, no tags implemented yet */ - return tag_new(); + return true; } static const char *const dsdiff_suffixes[] = { @@ -470,7 +472,7 @@ const struct decoder_plugin dsdiff_decoder_plugin = { .name = "dsdiff", .init = dsdiff_init, .stream_decode = dsdiff_stream_decode, - .stream_tag = dsdiff_stream_tag, + .scan_stream = dsdiff_scan_stream, .suffixes = dsdiff_suffixes, .mime_types = dsdiff_mime_types, }; diff --git a/src/decoder/faad_decoder_plugin.c b/src/decoder/faad_decoder_plugin.c index 91aa5392a..911f033b8 100644 --- a/src/decoder/faad_decoder_plugin.c +++ b/src/decoder/faad_decoder_plugin.c @@ -21,6 +21,7 @@ #include "decoder_api.h" #include "decoder_buffer.h" #include "audio_check.h" +#include "tag_handler.h" #define AAC_MAX_CHANNELS 6 @@ -487,18 +488,17 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is) faacDecClose(decoder); } -static struct tag * -faad_stream_tag(struct input_stream *is) +static bool +faad_scan_stream(struct input_stream *is, + const struct tag_handler *handler, void *handler_ctx) { int file_time = faad_get_file_time(is); - struct tag *tag; if (file_time < 0) - return NULL; + return false; - tag = tag_new(); - tag->time = file_time; - return tag; + tag_handler_invoke_duration(handler, handler_ctx, file_time); + return true; } static const char *const faad_suffixes[] = { "aac", NULL }; @@ -509,7 +509,7 @@ static const char *const faad_mime_types[] = { const struct decoder_plugin faad_decoder_plugin = { .name = "faad", .stream_decode = faad_stream_decode, - .stream_tag = faad_stream_tag, + .scan_stream = faad_scan_stream, .suffixes = faad_suffixes, .mime_types = faad_mime_types, }; diff --git a/src/decoder/ffmpeg_decoder_plugin.c b/src/decoder/ffmpeg_decoder_plugin.c index 20d24f130..6ad10026a 100644 --- a/src/decoder/ffmpeg_decoder_plugin.c +++ b/src/decoder/ffmpeg_decoder_plugin.c @@ -21,6 +21,7 @@ #include "decoder_api.h" #include "audio_check.h" #include "ffmpeg_metadata.h" +#include "tag_handler.h" #include <glib.h> @@ -570,22 +571,23 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input) } //no tag reading in ffmpeg, check if playable -static struct tag * -ffmpeg_stream_tag(struct input_stream *is) +static bool +ffmpeg_scan_stream(struct input_stream *is, + const struct tag_handler *handler, void *handler_ctx) { AVInputFormat *input_format = ffmpeg_probe(NULL, is); if (input_format == NULL) - return NULL; + return false; struct mpd_ffmpeg_stream *stream = mpd_ffmpeg_stream_open(NULL, is); if (stream == NULL) - return NULL; + return false; AVFormatContext *f = NULL; if (mpd_ffmpeg_open_input(&f, stream->io, is->uri, input_format) != 0) { mpd_ffmpeg_stream_close(stream); - return NULL; + return false; } #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,6,0) @@ -601,23 +603,22 @@ ffmpeg_stream_tag(struct input_stream *is) av_close_input_stream(f); #endif mpd_ffmpeg_stream_close(stream); - return NULL; + return false; } - struct tag *tag = tag_new(); - - tag->time = f->duration != (int64_t)AV_NOPTS_VALUE - ? f->duration / AV_TIME_BASE - : 0; + if (f->duration != (int64_t)AV_NOPTS_VALUE) + 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_copy_dictionary(tag, f->metadata); + ffmpeg_scan_dictionary(f->metadata, handler, handler_ctx); int idx = ffmpeg_find_audio_stream(f); if (idx >= 0) - ffmpeg_copy_dictionary(tag, f->streams[idx]->metadata); + ffmpeg_scan_dictionary(f->streams[idx]->metadata, + handler, handler_ctx); #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0) avformat_close_input(&f); @@ -626,7 +627,7 @@ ffmpeg_stream_tag(struct input_stream *is) #endif mpd_ffmpeg_stream_close(stream); - return tag; + return true; } /** @@ -745,7 +746,7 @@ const struct decoder_plugin ffmpeg_decoder_plugin = { .name = "ffmpeg", .init = ffmpeg_init, .stream_decode = ffmpeg_decode, - .stream_tag = ffmpeg_stream_tag, + .scan_stream = ffmpeg_scan_stream, .suffixes = ffmpeg_suffixes, .mime_types = ffmpeg_mime_types }; diff --git a/src/decoder/ffmpeg_metadata.c b/src/decoder/ffmpeg_metadata.c index d091afbc6..5325c1cae 100644 --- a/src/decoder/ffmpeg_metadata.c +++ b/src/decoder/ffmpeg_metadata.c @@ -19,8 +19,8 @@ #include "config.h" #include "ffmpeg_metadata.h" -#include "tag.h" #include "tag_table.h" +#include "tag_handler.h" #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "ffmpeg" @@ -39,23 +39,27 @@ static const struct tag_table ffmpeg_tags[] = { }; static void -ffmpeg_copy_metadata(struct tag *tag, enum tag_type type, - AVDictionary *m, const char *name) +ffmpeg_copy_metadata(enum tag_type type, + AVDictionary *m, const char *name, + const struct tag_handler *handler, void *handler_ctx) { AVDictionaryEntry *mt = NULL; while ((mt = av_dict_get(m, name, mt, 0)) != NULL) - tag_add_item(tag, type, mt->value); + tag_handler_invoke_tag(handler, handler_ctx, + type, mt->value); } void -ffmpeg_copy_dictionary(struct tag *tag, AVDictionary *dict) +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(tag, i, - dict, tag_item_names[i]); + ffmpeg_copy_metadata(i, dict, tag_item_names[i], + handler, handler_ctx); for (const struct tag_table *i = ffmpeg_tags; i->name != NULL; ++i) - ffmpeg_copy_metadata(tag, i->type, dict, i->name); + ffmpeg_copy_metadata(i->type, dict, i->name, + handler, handler_ctx); } diff --git a/src/decoder/ffmpeg_metadata.h b/src/decoder/ffmpeg_metadata.h index fc10e1ebe..60658f479 100644 --- a/src/decoder/ffmpeg_metadata.h +++ b/src/decoder/ffmpeg_metadata.h @@ -32,9 +32,10 @@ #define av_dict_get av_metadata_get #endif -struct tag; +struct tag_handler; void -ffmpeg_copy_dictionary(struct tag *tag, AVDictionary *dict); +ffmpeg_scan_dictionary(AVDictionary *dict, + const struct tag_handler *handler, void *handler_ctx); #endif diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/flac_decoder_plugin.c index 9dc1898d5..fb0b3502d 100644 --- a/src/decoder/flac_decoder_plugin.c +++ b/src/decoder/flac_decoder_plugin.c @@ -191,10 +191,11 @@ flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame, return flac_common_write(data, frame, buf, nbytes); } -static struct tag * -flac_tag_dup(const char *file) +static bool +flac_scan_file(const char *file, + const struct tag_handler *handler, void *handler_ctx) { - return flac_tag_load(file, NULL); + return flac_scan_file2(file, NULL, handler, handler_ctx); } /** @@ -400,38 +401,33 @@ oggflac_init(G_GNUC_UNUSED const struct config_param *param) #if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 -static struct tag * -oggflac_tag_dup(const char *file) +static bool +oggflac_scan_file(const char *file, + const struct tag_handler *handler, void *handler_ctx) { - struct tag *ret = NULL; 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); - return NULL; + return false; } it = FLAC__metadata_iterator_new(); FLAC__metadata_iterator_init(it, chain); - ret = tag_new(); do { if (!(block = FLAC__metadata_iterator_get_block(it))) break; - flac_tag_apply_metadata(ret, NULL, block); + flac_scan_metadata(NULL, block, + handler, handler_ctx); } while (FLAC__metadata_iterator_next(it)); FLAC__metadata_iterator_delete(it); - if (!tag_is_defined(ret)) { - tag_free(ret); - ret = NULL; - } - FLAC__metadata_chain_delete(chain); - return ret; + return true; } static void @@ -464,7 +460,7 @@ const struct decoder_plugin oggflac_decoder_plugin = { .init = oggflac_init, #if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7 .stream_decode = oggflac_decode, - .tag_dup = oggflac_tag_dup, + .scan_file = oggflac_scan_file, .suffixes = oggflac_suffixes, .mime_types = oggflac_mime_types #endif @@ -484,7 +480,7 @@ static const char *const flac_mime_types[] = { const struct decoder_plugin flac_decoder_plugin = { .name = "flac", .stream_decode = flac_decode, - .tag_dup = flac_tag_dup, + .scan_file = flac_scan_file, .suffixes = flac_suffixes, .mime_types = flac_mime_types, }; diff --git a/src/decoder/flac_metadata.c b/src/decoder/flac_metadata.c index 30dedda37..9e138ef96 100644 --- a/src/decoder/flac_metadata.c +++ b/src/decoder/flac_metadata.c @@ -21,6 +21,7 @@ #include "flac_metadata.h" #include "replay_gain_info.h" #include "tag.h" +#include "tag_handler.h" #include "tag_table.h" #include <glib.h> @@ -164,17 +165,19 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry, * the comment value into the tag. */ static bool -flac_copy_comment(struct tag *tag, - const FLAC__StreamMetadata_VorbisComment_Entry *entry, +flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *name, enum tag_type tag_type, - const char *char_tnum) + 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) { - tag_add_item_n(tag, tag_type, value, value_length); + char *p = g_strndup(value, value_length); + tag_handler_invoke_tag(handler, handler_ctx, tag_type, p); + g_free(p); return true; } @@ -189,42 +192,47 @@ static const struct tag_table flac_tags[] = { }; static void -flac_parse_comment(struct tag *tag, const char *char_tnum, - const FLAC__StreamMetadata_VorbisComment_Entry *entry) +flac_scan_comment(const char *char_tnum, + const FLAC__StreamMetadata_VorbisComment_Entry *entry, + const struct tag_handler *handler, void *handler_ctx) { - assert(tag != NULL); - for (const struct tag_table *i = flac_tags; i->name != NULL; ++i) - if (flac_copy_comment(tag, entry, i->name, i->type, char_tnum)) + if (flac_copy_comment(entry, i->name, i->type, char_tnum, + handler, handler_ctx)) return; for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) - if (flac_copy_comment(tag, entry, - tag_item_names[i], i, char_tnum)) + if (flac_copy_comment(entry, + tag_item_names[i], i, char_tnum, + handler, handler_ctx)) return; } -void -flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum, - const FLAC__StreamMetadata_VorbisComment *comment) +static void +flac_scan_comments(const char *char_tnum, + const FLAC__StreamMetadata_VorbisComment *comment, + const struct tag_handler *handler, void *handler_ctx) { for (unsigned i = 0; i < comment->num_comments; ++i) - flac_parse_comment(tag, char_tnum, &comment->comments[i]); + flac_scan_comment(char_tnum, &comment->comments[i], + handler, handler_ctx); } void -flac_tag_apply_metadata(struct tag *tag, const char *track, - const FLAC__StreamMetadata *block) +flac_scan_metadata(const char *track, + const FLAC__StreamMetadata *block, + const struct tag_handler *handler, void *handler_ctx) { switch (block->type) { case FLAC__METADATA_TYPE_VORBIS_COMMENT: - flac_vorbis_comments_to_tag(tag, track, - &block->data.vorbis_comment); + flac_scan_comments(track, &block->data.vorbis_comment, + handler, handler_ctx); break; case FLAC__METADATA_TYPE_STREAMINFO: if (block->data.stream_info.sample_rate > 0) - tag->time = flac_duration(&block->data.stream_info); + tag_handler_invoke_duration(handler, handler_ctx, + flac_duration(&block->data.stream_info)); break; default: @@ -232,10 +240,18 @@ flac_tag_apply_metadata(struct tag *tag, const char *track, } } -struct tag * -flac_tag_load(const char *file, const char *char_tnum) +void +flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum, + const FLAC__StreamMetadata_VorbisComment *comment) +{ + flac_scan_comments(char_tnum, comment, + &add_tag_handler, tag); +} + +bool +flac_scan_file2(const char *file, const char *char_tnum, + const struct tag_handler *handler, void *handler_ctx) { - struct tag *tag; FLAC__Metadata_SimpleIterator *it; FLAC__StreamMetadata *block = NULL; @@ -262,22 +278,30 @@ flac_tag_load(const char *file, const char *char_tnum) g_debug("Reading '%s' metadata gave the following error: %s\n", file, err); FLAC__metadata_simple_iterator_delete(it); - return NULL; + return false; } - tag = tag_new(); do { block = FLAC__metadata_simple_iterator_get_block(it); if (!block) break; - flac_tag_apply_metadata(tag, char_tnum, block); + 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); - if (!tag_is_defined(tag)) { + 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; } diff --git a/src/decoder/flac_metadata.h b/src/decoder/flac_metadata.h index 01bc1924a..3c463d5d6 100644 --- a/src/decoder/flac_metadata.h +++ b/src/decoder/flac_metadata.h @@ -24,6 +24,7 @@ #include <stdbool.h> #include <FLAC/metadata.h> +struct tag_handler; struct tag; struct replay_gain_info; @@ -49,8 +50,13 @@ flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum, const FLAC__StreamMetadata_VorbisComment *comment); void -flac_tag_apply_metadata(struct tag *tag, const char *track, - const FLAC__StreamMetadata *block); +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); diff --git a/src/decoder/gme_decoder_plugin.c b/src/decoder/gme_decoder_plugin.c index 8fdc39fd1..237a1deb1 100644 --- a/src/decoder/gme_decoder_plugin.c +++ b/src/decoder/gme_decoder_plugin.c @@ -2,6 +2,7 @@ #include "../decoder_api.h" #include "audio_check.h" #include "uri.h" +#include "tag_handler.h" #include <glib.h> #include <assert.h> @@ -180,8 +181,9 @@ gme_file_decode(struct decoder *decoder, const char *path_fs) gme_delete(emu); } -static struct tag * -gme_tag_dup(const char *path_fs) +static bool +gme_scan_file(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx) { Music_Emu *emu; gme_info_t *ti; @@ -194,42 +196,49 @@ gme_tag_dup(const char *path_fs) g_free(path_container); if (gme_err != NULL) { g_warning("%s", gme_err); - return NULL; + return false; } if((gme_err = gme_track_info(emu, &ti, song_num)) != NULL){ g_warning("%s", gme_err); gme_delete(emu); - return NULL; + return false; } assert(ti != NULL); - struct tag *tag = tag_new(); - if(ti->length > 0) - tag->time = ti->length / 1000; + tag_handler_invoke_duration(handler, handler_ctx, + ti->length / 100); + if(ti->song != NULL){ if(gme_track_count(emu) > 1){ /* start numbering subtunes from 1 */ char *tag_title=g_strdup_printf("%s (%d/%d)", ti->song, song_num+1, gme_track_count(emu)); - tag_add_item(tag, TAG_TITLE, tag_title); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_TITLE, tag_title); g_free(tag_title); }else - tag_add_item(tag, TAG_TITLE, ti->song); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_TITLE, ti->song); } if(ti->author != NULL) - tag_add_item(tag, TAG_ARTIST, ti->author); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_ARTIST, ti->author); if(ti->game != NULL) - tag_add_item(tag, TAG_ALBUM, ti->game); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_ALBUM, ti->game); if(ti->comment != NULL) - tag_add_item(tag, TAG_COMMENT, ti->comment); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_COMMENT, ti->comment); if(ti->copyright != NULL) - tag_add_item(tag, TAG_DATE, ti->copyright); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_DATE, ti->copyright); gme_free_info(ti); gme_delete(emu); - return tag; + + return true; } static const char *const gme_suffixes[] = { @@ -242,7 +251,7 @@ extern const struct decoder_plugin gme_decoder_plugin; const struct decoder_plugin gme_decoder_plugin = { .name = "gme", .file_decode = gme_file_decode, - .tag_dup = gme_tag_dup, + .scan_file = gme_scan_file, .suffixes = gme_suffixes, .container_scan = gme_container_scan, }; diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/mad_decoder_plugin.c index 8bf3f6546..a69284be5 100644 --- a/src/decoder/mad_decoder_plugin.c +++ b/src/decoder/mad_decoder_plugin.c @@ -22,6 +22,7 @@ #include "conf.h" #include "tag_id3.h" #include "tag_rva2.h" +#include "tag_handler.h" #include "audio_check.h" #include <assert.h> @@ -1176,19 +1177,18 @@ mp3_decode(struct decoder *decoder, struct input_stream *input_stream) mp3_data_finish(&data); } -static struct tag * -mad_decoder_stream_tag(struct input_stream *is) +static bool +mad_decoder_scan_stream(struct input_stream *is, + const struct tag_handler *handler, void *handler_ctx) { - struct tag *tag; int total_time; total_time = mad_decoder_total_file_time(is); if (total_time < 0) - return NULL; + return false; - tag = tag_new(); - tag->time = total_time; - return tag; + tag_handler_invoke_duration(handler, handler_ctx, total_time); + return true; } static const char *const mp3_suffixes[] = { "mp3", "mp2", NULL }; @@ -1198,7 +1198,7 @@ const struct decoder_plugin mad_decoder_plugin = { .name = "mad", .init = mp3_plugin_init, .stream_decode = mp3_decode, - .stream_tag = mad_decoder_stream_tag, + .scan_stream = mad_decoder_scan_stream, .suffixes = mp3_suffixes, .mime_types = mp3_mime_types }; diff --git a/src/decoder/modplug_decoder_plugin.c b/src/decoder/modplug_decoder_plugin.c index 9345dd240..21ee79e7e 100644 --- a/src/decoder/modplug_decoder_plugin.c +++ b/src/decoder/modplug_decoder_plugin.c @@ -19,6 +19,7 @@ #include "config.h" #include "decoder_api.h" +#include "tag_handler.h" #include <glib.h> #include <modplug.h> @@ -149,34 +150,33 @@ mod_decode(struct decoder *decoder, struct input_stream *is) ModPlug_Unload(f); } -static struct tag * -modplug_stream_tag(struct input_stream *is) +static bool +modplug_scan_stream(struct input_stream *is, + const struct tag_handler *handler, void *handler_ctx) { ModPlugFile *f; - struct tag *ret = NULL; GByteArray *bdatas; - char *title; bdatas = mod_loadfile(NULL, is); if (!bdatas) - return NULL; + return false; f = ModPlug_Load(bdatas->data, bdatas->len); g_byte_array_free(bdatas, TRUE); if (f == NULL) - return NULL; + return false; - ret = tag_new(); - ret->time = ModPlug_GetLength(f) / 1000; + tag_handler_invoke_duration(handler, handler_ctx, + ModPlug_GetLength(f) / 1000); - title = g_strdup(ModPlug_GetName(f)); - if (title) - tag_add_item(ret, TAG_TITLE, title); - g_free(title); + const char *title = ModPlug_GetName(f); + if (title != NULL) + tag_handler_invoke_tag(handler, handler_ctx, + TAG_TITLE, title); ModPlug_Unload(f); - return ret; + return true; } static const char *const mod_suffixes[] = { @@ -189,6 +189,6 @@ static const char *const mod_suffixes[] = { const struct decoder_plugin modplug_decoder_plugin = { .name = "modplug", .stream_decode = mod_decode, - .stream_tag = modplug_stream_tag, + .scan_stream = modplug_scan_stream, .suffixes = mod_suffixes, }; diff --git a/src/decoder/mpcdec_decoder_plugin.c b/src/decoder/mpcdec_decoder_plugin.c index 7864c0790..d4768b35b 100644 --- a/src/decoder/mpcdec_decoder_plugin.c +++ b/src/decoder/mpcdec_decoder_plugin.c @@ -20,6 +20,7 @@ #include "config.h" #include "decoder_api.h" #include "audio_check.h" +#include "tag_handler.h" #ifdef MPC_IS_OLD_API #include <mpcdec/mpcdec.h> @@ -323,18 +324,17 @@ mpcdec_get_file_duration(struct input_stream *is) return total_time; } -static struct tag * -mpcdec_stream_tag(struct input_stream *is) +static bool +mpcdec_scan_stream(struct input_stream *is, + const struct tag_handler *handler, void *handler_ctx) { float total_time = mpcdec_get_file_duration(is); - struct tag *tag; if (total_time < 0) - return NULL; + return false; - tag = tag_new(); - tag->time = total_time; - return tag; + tag_handler_invoke_duration(handler, handler_ctx, total_time); + return true; } static const char *const mpcdec_suffixes[] = { "mpc", NULL }; @@ -342,6 +342,6 @@ static const char *const mpcdec_suffixes[] = { "mpc", NULL }; const struct decoder_plugin mpcdec_decoder_plugin = { .name = "mpcdec", .stream_decode = mpcdec_decode, - .stream_tag = mpcdec_stream_tag, + .scan_stream = mpcdec_scan_stream, .suffixes = mpcdec_suffixes, }; diff --git a/src/decoder/mpg123_decoder_plugin.c b/src/decoder/mpg123_decoder_plugin.c index 224f3db2a..657a9c889 100644 --- a/src/decoder/mpg123_decoder_plugin.c +++ b/src/decoder/mpg123_decoder_plugin.c @@ -20,6 +20,7 @@ #include "config.h" /* must be first for large file support */ #include "decoder_api.h" #include "audio_check.h" +#include "tag_handler.h" #include <glib.h> @@ -192,41 +193,40 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs) mpg123_delete(handle); } -static struct tag * -mpd_mpg123_tag_dup(const char *path_fs) +static bool +mpd_mpg123_scan_file(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx) { struct audio_format audio_format; mpg123_handle *handle; int error; off_t num_samples; - struct tag *tag; handle = mpg123_new(NULL, &error); if (handle == NULL) { g_warning("mpg123_new() failed: %s", mpg123_plain_strerror(error)); - return NULL; + return false; } if (!mpd_mpg123_open(handle, path_fs, &audio_format)) { mpg123_delete(handle); - return NULL; + return false; } num_samples = mpg123_length(handle); if (num_samples <= 0) { mpg123_delete(handle); - return NULL; + return false; } - tag = tag_new(); - - tag->time = num_samples / audio_format.sample_rate; - /* ID3 tag support not yet implemented */ mpg123_delete(handle); - return tag; + + tag_handler_invoke_duration(handler, handler_ctx, + num_samples / audio_format.sample_rate); + return true; } static const char *const mpg123_suffixes[] = { @@ -240,6 +240,6 @@ const struct decoder_plugin mpg123_decoder_plugin = { .finish = mpd_mpg123_finish, .file_decode = mpd_mpg123_file_decode, /* streaming not yet implemented */ - .tag_dup = mpd_mpg123_tag_dup, + .scan_file = mpd_mpg123_scan_file, .suffixes = mpg123_suffixes, }; diff --git a/src/decoder/sidplay_decoder_plugin.cxx b/src/decoder/sidplay_decoder_plugin.cxx index 9aeec8b51..c4ef20496 100644 --- a/src/decoder/sidplay_decoder_plugin.cxx +++ b/src/decoder/sidplay_decoder_plugin.cxx @@ -21,6 +21,7 @@ extern "C" { #include "../decoder_api.h" +#include "tag_handler.h" } #include <errno.h> @@ -336,8 +337,9 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs) } while (cmd != DECODE_COMMAND_STOP); } -static struct tag * -sidplay_tag_dup(const char *path_fs) +static bool +sidplay_scan_file(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx) { int song_num=get_song_num(path_fs); char *path_container=get_container_name(path_fs); @@ -345,10 +347,9 @@ sidplay_tag_dup(const char *path_fs) SidTune tune(path_container, NULL, true); g_free(path_container); if (!tune) - return NULL; + return false; const SidTuneInfo &info = tune.getInfo(); - struct tag *tag = tag_new(); /* title */ const char *title; @@ -360,25 +361,28 @@ sidplay_tag_dup(const char *path_fs) if(info.songs>1) { char *tag_title=g_strdup_printf("%s (%d/%d)", title, song_num, info.songs); - tag_add_item(tag, TAG_TITLE, tag_title); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_TITLE, tag_title); g_free(tag_title); } else - tag_add_item(tag, TAG_TITLE, title); + tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, title); /* artist */ if (info.numberOfInfoStrings > 1 && info.infoString[1] != NULL) - tag_add_item(tag, TAG_ARTIST, info.infoString[1]); + tag_handler_invoke_tag(handler, handler_ctx, TAG_ARTIST, + info.infoString[1]); /* track */ char *track=g_strdup_printf("%d", song_num); - tag_add_item(tag, TAG_TRACK, track); + tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track); g_free(track); /* time */ int song_len=get_song_length(path_fs); - if(song_len!=-1) tag->time=song_len; + if (song_len >= 0) + tag_handler_invoke_duration(handler, handler_ctx, song_len); - return tag; + return true; } static char * @@ -421,7 +425,7 @@ const struct decoder_plugin sidplay_decoder_plugin = { sidplay_finish, NULL, /* stream_decode() */ sidplay_file_decode, - sidplay_tag_dup, + sidplay_scan_file, NULL, /* stream_tag() */ sidplay_container_scan, sidplay_suffixes, diff --git a/src/decoder/sndfile_decoder_plugin.c b/src/decoder/sndfile_decoder_plugin.c index 25952dfd5..8dd98236f 100644 --- a/src/decoder/sndfile_decoder_plugin.c +++ b/src/decoder/sndfile_decoder_plugin.c @@ -20,6 +20,7 @@ #include "config.h" #include "decoder_api.h" #include "audio_check.h" +#include "tag_handler.h" #include <sndfile.h> @@ -172,44 +173,47 @@ sndfile_stream_decode(struct decoder *decoder, struct input_stream *is) sf_close(sf); } -static struct tag * -sndfile_tag_dup(const char *path_fs) +static bool +sndfile_scan_file(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx) { SNDFILE *sf; SF_INFO info; - struct tag *tag; const char *p; info.format = 0; sf = sf_open(path_fs, SFM_READ, &info); if (sf == NULL) - return NULL; + return false; if (!audio_valid_sample_rate(info.samplerate)) { sf_close(sf); g_warning("Invalid sample rate in %s\n", path_fs); - return NULL; + return false; } - tag = tag_new(); - tag->time = info.frames / info.samplerate; + tag_handler_invoke_duration(handler, handler_ctx, + info.frames / info.samplerate); p = sf_get_string(sf, SF_STR_TITLE); if (p != NULL) - tag_add_item(tag, TAG_TITLE, p); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_TITLE, p); p = sf_get_string(sf, SF_STR_ARTIST); if (p != NULL) - tag_add_item(tag, TAG_ARTIST, p); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_ARTIST, p); p = sf_get_string(sf, SF_STR_DATE); if (p != NULL) - tag_add_item(tag, TAG_DATE, p); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_DATE, p); sf_close(sf); - return tag; + return true; } static const char *const sndfile_suffixes[] = { @@ -245,7 +249,7 @@ static const char *const sndfile_mime_types[] = { const struct decoder_plugin sndfile_decoder_plugin = { .name = "sndfile", .stream_decode = sndfile_stream_decode, - .tag_dup = sndfile_tag_dup, + .scan_file = sndfile_scan_file, .suffixes = sndfile_suffixes, .mime_types = sndfile_mime_types, }; diff --git a/src/decoder/vorbis_comments.c b/src/decoder/vorbis_comments.c index 9145507dc..e94ee3d0a 100644 --- a/src/decoder/vorbis_comments.c +++ b/src/decoder/vorbis_comments.c @@ -21,6 +21,7 @@ #include "vorbis_comments.h" #include "tag.h" #include "tag_table.h" +#include "tag_handler.h" #include "replay_gain_info.h" #include <glib.h> @@ -79,14 +80,15 @@ vorbis_comments_to_replay_gain(struct replay_gain_info *rgi, char **comments) * the comment value into the tag. */ static bool -vorbis_copy_comment(struct tag *tag, const char *comment, - const char *name, enum tag_type tag_type) +vorbis_copy_comment(const char *comment, + const char *name, enum tag_type tag_type, + const struct tag_handler *handler, void *handler_ctx) { const char *value; value = vorbis_comment_value(comment, name); if (value != NULL) { - tag_add_item(tag, tag_type, value); + tag_handler_invoke_tag(handler, handler_ctx, tag_type, value); return true; } @@ -101,27 +103,36 @@ static const struct tag_table vorbis_tags[] = { }; static void -vorbis_parse_comment(struct tag *tag, const char *comment) +vorbis_scan_comment(const char *comment, + const struct tag_handler *handler, void *handler_ctx) { - assert(tag != NULL); - for (const struct tag_table *i = vorbis_tags; i->name != NULL; ++i) - if (vorbis_copy_comment(tag, comment, i->name, i->type)) + 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(tag, comment, - tag_item_names[i], i)) + if (vorbis_copy_comment(comment, + tag_item_names[i], i, + handler, handler_ctx)) return; } +void +vorbis_comments_scan(char **comments, + const struct tag_handler *handler, void *handler_ctx) +{ + while (*comments) + vorbis_scan_comment(*comments++, + handler, handler_ctx); + +} + struct tag * vorbis_comments_to_tag(char **comments) { struct tag *tag = tag_new(); - - while (*comments) - vorbis_parse_comment(tag, *comments++); + vorbis_comments_scan(comments, &add_tag_handler, tag); if (tag_is_empty(tag)) { tag_free(tag); diff --git a/src/decoder/vorbis_comments.h b/src/decoder/vorbis_comments.h index 5d2c65e44..c15096930 100644 --- a/src/decoder/vorbis_comments.h +++ b/src/decoder/vorbis_comments.h @@ -25,10 +25,15 @@ #include <stdbool.h> struct replay_gain_info; +struct tag_handler; bool vorbis_comments_to_replay_gain(struct replay_gain_info *rgi, char **comments); +void +vorbis_comments_scan(char **comments, + const struct tag_handler *handler, void *handler_ctx); + struct tag * vorbis_comments_to_tag(char **comments); diff --git a/src/decoder/vorbis_decoder_plugin.c b/src/decoder/vorbis_decoder_plugin.c index f34693844..15cdc0ca9 100644 --- a/src/decoder/vorbis_decoder_plugin.c +++ b/src/decoder/vorbis_decoder_plugin.c @@ -22,6 +22,7 @@ #include "_ogg_common.h" #include "audio_check.h" #include "uri.h" +#include "tag_handler.h" #ifndef HAVE_TREMOR #define OV_EXCLUDE_STATIC_CALLBACKS @@ -268,24 +269,24 @@ vorbis_stream_decode(struct decoder *decoder, ov_clear(&vf); } -static struct tag * -vorbis_stream_tag(struct input_stream *is) +static bool +vorbis_scan_stream(struct input_stream *is, + const struct tag_handler *handler, void *handler_ctx) { struct vorbis_input_stream vis; OggVorbis_File vf; if (!vorbis_is_open(&vis, &vf, NULL, is)) - return NULL; + return false; - struct tag *tag = vorbis_comments_to_tag(ov_comment(&vf, -1)->user_comments); + tag_handler_invoke_duration(handler, handler_ctx, + (int)(ov_time_total(&vf, -1) + 0.5)); - if (tag == NULL) - tag = tag_new(); - tag->time = (int)(ov_time_total(&vf, -1) + 0.5); + vorbis_comments_scan(ov_comment(&vf, -1)->user_comments, + handler, handler_ctx); ov_clear(&vf); - - return tag; + return true; } static const char *const vorbis_suffixes[] = { @@ -307,7 +308,7 @@ static const char *const vorbis_mime_types[] = { const struct decoder_plugin vorbis_decoder_plugin = { .name = "vorbis", .stream_decode = vorbis_stream_decode, - .stream_tag = vorbis_stream_tag, + .scan_stream = vorbis_scan_stream, .suffixes = vorbis_suffixes, .mime_types = vorbis_mime_types }; diff --git a/src/decoder/wavpack_decoder_plugin.c b/src/decoder/wavpack_decoder_plugin.c index 4945b75ce..794b969c4 100644 --- a/src/decoder/wavpack_decoder_plugin.c +++ b/src/decoder/wavpack_decoder_plugin.c @@ -22,6 +22,7 @@ #include "audio_check.h" #include "path.h" #include "utils.h" +#include "tag_handler.h" #include <wavpack/wavpack.h> #include <glib.h> @@ -275,11 +276,11 @@ wavpack_replaygain(struct replay_gain_info *replay_gain_info, /* * Reads metainfo from the specified file. */ -static struct tag * -wavpack_tagdup(const char *fname) +static bool +wavpack_scan_file(const char *fname, + const struct tag_handler *handler, void *handler_ctx) { WavpackContext *wpc; - struct tag *tag; char error[ERRORLEN]; char *s; int size, allocated_size; @@ -290,12 +291,12 @@ wavpack_tagdup(const char *fname) "failed to open WavPack file \"%s\": %s\n", fname, error ); - return NULL; + return false; } - tag = tag_new(); - tag->time = WavpackGetNumSamples(wpc); - tag->time /= WavpackGetSampleRate(wpc); + tag_handler_invoke_duration(handler, handler_ctx, + WavpackGetNumSamples(wpc) / + WavpackGetSampleRate(wpc)); allocated_size = 0; s = NULL; @@ -315,7 +316,8 @@ wavpack_tagdup(const char *fname) } WavpackGetTagItem(wpc, tagtypes[i].name, s, size); - tag_add_item(tag, tagtypes[i].type, s); + tag_handler_invoke_tag(handler, handler_ctx, + tagtypes[i].type, s); } } @@ -323,7 +325,7 @@ wavpack_tagdup(const char *fname) WavpackCloseFile(wpc); - return tag; + return true; } /* @@ -576,7 +578,7 @@ const struct decoder_plugin wavpack_decoder_plugin = { .name = "wavpack", .stream_decode = wavpack_streamdecode, .file_decode = wavpack_filedecode, - .tag_dup = wavpack_tagdup, + .scan_file = wavpack_scan_file, .suffixes = wavpack_suffixes, .mime_types = wavpack_mime_types }; diff --git a/src/decoder/wildmidi_decoder_plugin.c b/src/decoder/wildmidi_decoder_plugin.c index 5bc36b4e3..a2224940d 100644 --- a/src/decoder/wildmidi_decoder_plugin.c +++ b/src/decoder/wildmidi_decoder_plugin.c @@ -19,6 +19,7 @@ #include "config.h" #include "decoder_api.h" +#include "tag_handler.h" #include <glib.h> @@ -111,25 +112,26 @@ wildmidi_file_decode(struct decoder *decoder, const char *path_fs) WildMidi_Close(wm); } -static struct tag * -wildmidi_tag_dup(const char *path_fs) +static bool +wildmidi_scan_file(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx) { midi *wm = WildMidi_Open(path_fs); if (wm == NULL) - return NULL; + return false; const struct _WM_Info *info = WildMidi_GetInfo(wm); if (info == NULL) { WildMidi_Close(wm); - return NULL; + return false; } - struct tag *tag = tag_new(); - tag->time = info->approx_total_samples / WILDMIDI_SAMPLE_RATE; + int duration = info->approx_total_samples / WILDMIDI_SAMPLE_RATE; + tag_handler_invoke_duration(handler, handler_ctx, duration); WildMidi_Close(wm); - return tag; + return true; } static const char *const wildmidi_suffixes[] = { @@ -142,6 +144,6 @@ const struct decoder_plugin wildmidi_decoder_plugin = { .init = wildmidi_init, .finish = wildmidi_finish, .file_decode = wildmidi_file_decode, - .tag_dup = wildmidi_tag_dup, + .scan_file = wildmidi_scan_file, .suffixes = wildmidi_suffixes, }; diff --git a/src/decoder_plugin.h b/src/decoder_plugin.h index 0ce1af53e..933ba6751 100644 --- a/src/decoder_plugin.h +++ b/src/decoder_plugin.h @@ -26,6 +26,7 @@ struct config_param; struct input_stream; struct tag; +struct tag_handler; /** * Opaque handle which the decoder plugin passes to the functions in @@ -70,18 +71,22 @@ struct decoder_plugin { void (*file_decode)(struct decoder *decoder, const char *path_fs); /** - * Read the tags of a local file. + * Scan metadata of a file. * - * @return NULL if the operation has failed + * @return false if the operation has failed */ - struct tag *(*tag_dup)(const char *path_fs); + bool (*scan_file)(const char *path_fs, + const struct tag_handler *handler, + void *handler_ctx); /** - * Read the tags of a stream. + * Scan metadata of a file. * - * @return NULL if the operation has failed + * @return false if the operation has failed */ - struct tag *(*stream_tag)(struct input_stream *is); + bool (*scan_stream)(struct input_stream *is, + const struct tag_handler *handler, + void *handler_ctx); /** * @brief Return a "virtual" filename for subtracks in @@ -150,25 +155,28 @@ decoder_plugin_file_decode(const struct decoder_plugin *plugin, /** * Read the tag of a file. */ -static inline struct tag * -decoder_plugin_tag_dup(const struct decoder_plugin *plugin, - const char *path_fs) +static inline bool +decoder_plugin_scan_file(const struct decoder_plugin *plugin, + const char *path_fs, + const struct tag_handler *handler, void *handler_ctx) { - return plugin->tag_dup != NULL - ? plugin->tag_dup(path_fs) - : NULL; + return plugin->scan_file != NULL + ? plugin->scan_file(path_fs, handler, handler_ctx) + : false; } /** * Read the tag of a stream. */ -static inline struct tag * -decoder_plugin_stream_tag(const struct decoder_plugin *plugin, - struct input_stream *is) +static inline bool +decoder_plugin_scan_stream(const struct decoder_plugin *plugin, + struct input_stream *is, + const struct tag_handler *handler, + void *handler_ctx) { - return plugin->stream_tag != NULL - ? plugin->stream_tag(is) - : NULL; + return plugin->scan_stream != NULL + ? plugin->scan_stream(is, handler, handler_ctx) + : false; } /** diff --git a/src/song_update.c b/src/song_update.c index e7279a6f6..d6f643dd9 100644 --- a/src/song_update.c +++ b/src/song_update.c @@ -27,6 +27,7 @@ #include "tag_ape.h" #include "tag_id3.h" #include "tag.h" +#include "tag_handler.h" #include "input_stream.h" #include <glib.h> @@ -136,12 +137,16 @@ song_file_update(struct song *song) do { /* load file tag */ - song->tag = decoder_plugin_tag_dup(plugin, path_fs); - if (song->tag != NULL) + song->tag = tag_new(); + if (decoder_plugin_scan_file(plugin, path_fs, + &add_tag_handler, song->tag)) break; + tag_free(song->tag); + song->tag = NULL; + /* fall back to stream tag */ - if (plugin->stream_tag != NULL) { + if (plugin->scan_stream != NULL) { /* open the input_stream (if not already open) */ if (is == NULL) { @@ -153,11 +158,15 @@ song_file_update(struct song *song) /* now try the stream_tag() method */ if (is != NULL) { - song->tag = decoder_plugin_stream_tag(plugin, - is); - if (song->tag != NULL) + song->tag = tag_new(); + if (decoder_plugin_scan_stream(plugin, is, + &add_tag_handler, + song->tag)) break; + tag_free(song->tag); + song->tag = NULL; + input_stream_lock_seek(is, 0, SEEK_SET, NULL); } } diff --git a/src/tag_ape.c b/src/tag_ape.c index 5a30531c0..93d935b75 100644 --- a/src/tag_ape.c +++ b/src/tag_ape.c @@ -21,6 +21,7 @@ #include "tag_ape.h" #include "tag.h" #include "tag_table.h" +#include "tag_handler.h" #include "ape.h" static const struct tag_table ape_tags[] = { @@ -39,20 +40,18 @@ tag_ape_name_parse(const char *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) +static void +tag_ape_import_item(unsigned long flags, + const char *key, const char *value, size_t value_length, + const struct tag_handler *handler, void *handler_ctx) { /* we only care about utf-8 text tags */ if ((flags & (0x3 << 1)) != 0) - return tag; + return; enum tag_type type = tag_ape_name_parse(key); if (type == TAG_NUM_OF_ITEM_TYPES) - return tag; - - if (tag == NULL) - tag = tag_new(); + return; const char *end = value + value_length; while (true) { @@ -60,20 +59,22 @@ tag_ape_import_item(struct tag *tag, unsigned long flags, const char *n = memchr(value, 0, end - value); if (n != NULL) { if (n > value) - tag_add_item_n(tag, type, value, n - value); + tag_handler_invoke_tag(handler, handler_ctx, + type, value); value = n + 1; } else { - if (end > value) - tag_add_item_n(tag, type, value, end - value); + char *p = g_strndup(value, end - value); + tag_handler_invoke_tag(handler, handler_ctx, + type, p); + g_free(p); break; } } - - return tag; } struct tag_ape_ctx { - struct tag *tag; + const struct tag_handler *handler; + void *handler_ctx; }; static bool @@ -82,16 +83,31 @@ tag_ape_callback(unsigned long flags, const char *key, { struct tag_ape_ctx *ctx = _ctx; - ctx->tag = tag_ape_import_item(ctx->tag, flags, key, - value, value_length); + tag_ape_import_item(flags, key, value, value_length, + ctx->handler, ctx->handler_ctx); return true; } +bool +tag_ape_scan2(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx) +{ + struct tag_ape_ctx ctx = { + .handler = handler, + .handler_ctx = handler_ctx, + }; + + return tag_ape_scan(path_fs, tag_ape_callback, &ctx); +} + struct tag * -tag_ape_load(const char *file) +tag_ape_load(const char *path_fs) { - struct tag_ape_ctx ctx = { .tag = NULL }; + struct tag *tag = tag_new(); + if (!tag_ape_scan2(path_fs, &add_tag_handler, tag)) { + tag_free(tag); + tag = NULL; + } - tag_ape_scan(file, tag_ape_callback, &ctx); - return ctx.tag; + return tag; } diff --git a/src/tag_ape.h b/src/tag_ape.h index eb0f1b8a5..2e8bfb3c5 100644 --- a/src/tag_ape.h +++ b/src/tag_ape.h @@ -20,6 +20,19 @@ #ifndef MPD_TAG_APE_H #define MPD_TAG_APE_H +#include <stdbool.h> + +struct tag_handler; + +/** + * Scan the APE tags of a file. + * + * @param path_fs the path of the file in filesystem encoding + */ +bool +tag_ape_scan2(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx); + /** * Loads the APE tag from a file. * diff --git a/src/tag_handler.c b/src/tag_handler.c new file mode 100644 index 000000000..32de7650d --- /dev/null +++ b/src/tag_handler.c @@ -0,0 +1,43 @@ +/* + * 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 "tag_handler.h" + +static void +add_tag_duration(unsigned seconds, void *ctx) +{ + struct tag *tag = ctx; + + tag->time = seconds; +} + +static void +add_tag_tag(enum tag_type type, const char *value, void *ctx) +{ + struct tag *tag = ctx; + + tag_add_item(tag, type, value); +} + +const struct tag_handler add_tag_handler = { + .duration = add_tag_duration, + .tag = add_tag_tag, +}; + diff --git a/src/tag_handler.h b/src/tag_handler.h new file mode 100644 index 000000000..13e40f38d --- /dev/null +++ b/src/tag_handler.h @@ -0,0 +1,76 @@ +/* + * 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_TAG_HANDLER_H +#define MPD_TAG_HANDLER_H + +#include "check.h" +#include "tag.h" + +#include <assert.h> + +/** + * A callback table for receiving metadata of a song. + */ +struct tag_handler { + /** + * Declare the duration of a song, in seconds. Do not call + * this when the duration could not be determined, because + * there is no magic value for "unknown duration". + */ + void (*duration)(unsigned seconds, void *ctx); + + /** + * A tag has been read. + * + * @param the value of the tag; the pointer will become + * invalid after returning + */ + void (*tag)(enum tag_type type, const char *value, void *ctx); +}; + +static inline void +tag_handler_invoke_duration(const struct tag_handler *handler, void *ctx, + unsigned seconds) +{ + assert(handler != NULL); + + if (handler->duration != NULL) + handler->duration(seconds, ctx); +} + +static inline void +tag_handler_invoke_tag(const struct tag_handler *handler, void *ctx, + enum tag_type type, const char *value) +{ + assert(handler != NULL); + assert((unsigned)type < TAG_NUM_OF_ITEM_TYPES); + assert(value != NULL); + + if (handler->tag != NULL) + handler->tag(type, value, ctx); +} + +/** + * This #tag_handler implementation adds tag values to a #tag object + * (casted from the context pointer). + */ +extern const struct tag_handler add_tag_handler; + +#endif diff --git a/src/tag_id3.c b/src/tag_id3.c index 1facf3c18..8becb481c 100644 --- a/src/tag_id3.c +++ b/src/tag_id3.c @@ -19,6 +19,7 @@ #include "config.h" #include "tag_id3.h" +#include "tag_handler.h" #include "tag_table.h" #include "tag.h" #include "riff.h" @@ -127,9 +128,9 @@ import_id3_string(bool is_id3v1, const id3_ucs4_t *ucs4) * - string list */ static void -tag_id3_import_text_frame(struct tag *dest, struct id3_tag *tag, - const struct id3_frame *frame, - enum tag_type type) +tag_id3_import_text_frame(struct id3_tag *tag, const struct id3_frame *frame, + enum tag_type type, + const struct tag_handler *handler, void *handler_ctx) { id3_ucs4_t const *ucs4; id3_utf8_t *utf8; @@ -165,7 +166,8 @@ tag_id3_import_text_frame(struct tag *dest, struct id3_tag *tag, if (utf8 == NULL) continue; - tag_add_item(dest, type, (char *)utf8); + tag_handler_invoke_tag(handler, handler_ctx, + type, (const char *)utf8); g_free(utf8); } } @@ -175,13 +177,14 @@ tag_id3_import_text_frame(struct tag *dest, struct id3_tag *tag, * 4.2). This is a wrapper for tag_id3_import_text_frame(). */ static void -tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id, - enum tag_type type) +tag_id3_import_text(struct id3_tag *tag, const char *id, enum tag_type type, + const struct tag_handler *handler, void *handler_ctx) { const struct id3_frame *frame; for (unsigned i = 0; (frame = id3_tag_findframe(tag, id, i)) != NULL; ++i) - tag_id3_import_text_frame(dest, tag, frame, type); + tag_id3_import_text_frame(tag, frame, type, + handler, handler_ctx); } /** @@ -194,9 +197,10 @@ tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id, * - full string (we use this one) */ static void -tag_id3_import_comment_frame(struct tag *dest, struct id3_tag *tag, - const struct id3_frame *frame, - enum tag_type type) +tag_id3_import_comment_frame(struct id3_tag *tag, + const struct id3_frame *frame, enum tag_type type, + const struct tag_handler *handler, + void *handler_ctx) { id3_ucs4_t const *ucs4; id3_utf8_t *utf8; @@ -218,7 +222,7 @@ tag_id3_import_comment_frame(struct tag *dest, struct id3_tag *tag, if (utf8 == NULL) return; - tag_add_item(dest, type, (char *)utf8); + tag_handler_invoke_tag(handler, handler_ctx, type, (const char *)utf8); g_free(utf8); } @@ -227,13 +231,14 @@ tag_id3_import_comment_frame(struct tag *dest, struct id3_tag *tag, * wrapper for tag_id3_import_comment_frame(). */ static void -tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id, - enum tag_type type) +tag_id3_import_comment(struct id3_tag *tag, const char *id, enum tag_type type, + const struct tag_handler *handler, void *handler_ctx) { const struct id3_frame *frame; for (unsigned i = 0; (frame = id3_tag_findframe(tag, id, i)) != NULL; ++i) - tag_id3_import_comment_frame(dest, tag, frame, type); + tag_id3_import_comment_frame(tag, frame, type, + handler, handler_ctx); } /** @@ -260,7 +265,9 @@ tag_id3_parse_txxx_name(const char *name) * Import all known MusicBrainz tags from TXXX frames. */ static void -tag_id3_import_musicbrainz(struct tag *mpd_tag, struct id3_tag *id3_tag) +tag_id3_import_musicbrainz(struct id3_tag *id3_tag, + const struct tag_handler *handler, + void *handler_ctx) { for (unsigned i = 0;; ++i) { const struct id3_frame *frame; @@ -285,7 +292,8 @@ tag_id3_import_musicbrainz(struct tag *mpd_tag, struct id3_tag *id3_tag) if (value == NULL) continue; - tag_add_item(mpd_tag, type, (const char*)value); + tag_handler_invoke_tag(handler, handler_ctx, + type, (const char*)value); free(value); } } @@ -294,7 +302,8 @@ tag_id3_import_musicbrainz(struct tag *mpd_tag, struct id3_tag *id3_tag) * Imports the MusicBrainz TrackId from the UFID tag. */ static void -tag_id3_import_ufid(struct tag *mpd_tag, struct id3_tag *id3_tag) +tag_id3_import_ufid(struct id3_tag *id3_tag, + const struct tag_handler *handler, void *handler_ctx) { for (unsigned i = 0;; ++i) { const struct id3_frame *frame; @@ -324,35 +333,54 @@ tag_id3_import_ufid(struct tag *mpd_tag, struct id3_tag *id3_tag) if (value == NULL || length == 0) continue; - tag_add_item_n(mpd_tag, TAG_MUSICBRAINZ_TRACKID, - (const char*)value, length); + char *p = g_strndup((const char *)value, length); + tag_handler_invoke_tag(handler, handler_ctx, + TAG_MUSICBRAINZ_TRACKID, p); + g_free(p); } } +static void +scan_id3_tag(struct id3_tag *tag, + const struct tag_handler *handler, void *handler_ctx) +{ + tag_id3_import_text(tag, ID3_FRAME_ARTIST, TAG_ARTIST, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST, + TAG_ALBUM_ARTIST, handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_ARTIST_SORT, + TAG_ARTIST_SORT, handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST_SORT, + TAG_ALBUM_ARTIST_SORT, handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_TITLE, TAG_TITLE, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_ALBUM, TAG_ALBUM, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_TRACK, TAG_TRACK, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_YEAR, TAG_DATE, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_GENRE, TAG_GENRE, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_COMPOSER, TAG_COMPOSER, + handler, handler_ctx); + tag_id3_import_text(tag, "TPE3", TAG_PERFORMER, + handler, handler_ctx); + tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler, handler_ctx); + tag_id3_import_comment(tag, ID3_FRAME_COMMENT, TAG_COMMENT, + handler, handler_ctx); + tag_id3_import_text(tag, ID3_FRAME_DISC, TAG_DISC, + handler, handler_ctx); + + tag_id3_import_musicbrainz(tag, handler, handler_ctx); + tag_id3_import_ufid(tag, handler, handler_ctx); +} + struct tag *tag_id3_import(struct id3_tag * tag) { struct tag *ret = tag_new(); - tag_id3_import_text(ret, tag, ID3_FRAME_ARTIST, TAG_ARTIST); - tag_id3_import_text(ret, tag, ID3_FRAME_ALBUM_ARTIST, - TAG_ALBUM_ARTIST); - tag_id3_import_text(ret, tag, ID3_FRAME_ARTIST_SORT, - TAG_ARTIST_SORT); - tag_id3_import_text(ret, tag, ID3_FRAME_ALBUM_ARTIST_SORT, - TAG_ALBUM_ARTIST_SORT); - tag_id3_import_text(ret, tag, ID3_FRAME_TITLE, TAG_TITLE); - tag_id3_import_text(ret, tag, ID3_FRAME_ALBUM, TAG_ALBUM); - tag_id3_import_text(ret, tag, ID3_FRAME_TRACK, TAG_TRACK); - tag_id3_import_text(ret, tag, ID3_FRAME_YEAR, TAG_DATE); - tag_id3_import_text(ret, tag, ID3_FRAME_GENRE, TAG_GENRE); - tag_id3_import_text(ret, tag, ID3_FRAME_COMPOSER, TAG_COMPOSER); - tag_id3_import_text(ret, tag, "TPE3", TAG_PERFORMER); - tag_id3_import_text(ret, tag, "TPE4", TAG_PERFORMER); - tag_id3_import_comment(ret, tag, ID3_FRAME_COMMENT, TAG_COMMENT); - tag_id3_import_text(ret, tag, ID3_FRAME_DISC, TAG_DISC); - - tag_id3_import_musicbrainz(ret, tag); - tag_id3_import_ufid(ret, tag); + scan_id3_tag(tag, &add_tag_handler, ret); if (tag_is_empty(ret)) { tag_free(ret); @@ -510,6 +538,36 @@ tag_id3_riff_aiff_load(FILE *file) return tag; } +bool +tag_id3_scan(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx) +{ + struct id3_tag *tag; + FILE *stream; + + stream = fopen(path_fs, "rb"); + if (!stream) { + g_debug("tag_id3_load: Failed to open file: '%s', %s", + path_fs, strerror(errno)); + return false; + } + + tag = tag_id3_find_from_beginning(stream); + if (tag == NULL) + tag = tag_id3_riff_aiff_load(stream); + if (!tag) + tag = tag_id3_find_from_end(stream); + + fclose(stream); + + if (!tag) + return false; + + scan_id3_tag(tag, handler, handler_ctx); + id3_tag_delete(tag); + return true; +} + struct tag *tag_id3_load(const char *file) { struct tag *ret; diff --git a/src/tag_id3.h b/src/tag_id3.h index 17dde4b36..fb5c7c653 100644 --- a/src/tag_id3.h +++ b/src/tag_id3.h @@ -22,9 +22,17 @@ #include "check.h" +#include <stdbool.h> + +struct tag_handler; struct tag; #ifdef HAVE_ID3TAG + +bool +tag_id3_scan(const char *path_fs, + const struct tag_handler *handler, void *handler_ctx); + struct id3_tag; struct tag *tag_id3_import(struct id3_tag *); @@ -34,6 +42,14 @@ struct tag *tag_id3_load(const char *file); #include <glib.h> +static inline bool +tag_id3_scan(G_GNUC_UNUSED const char *path_fs, + G_GNUC_UNUSED const struct tag_handler *handler, + G_GNUC_UNUSED void *handler_ctx) +{ + return false; +} + static inline struct tag * tag_id3_load(G_GNUC_UNUSED const char *file) { diff --git a/src/update_walk.c b/src/update_walk.c index 8b2df6ba7..53d782ae2 100644 --- a/src/update_walk.c +++ b/src/update_walk.c @@ -31,6 +31,8 @@ #include "decoder_plugin.h" #include "playlist_list.h" #include "conf.h" +#include "tag.h" +#include "tag_handler.h" #ifdef ENABLE_ARCHIVE #include "archive_list.h" @@ -511,7 +513,9 @@ update_container_file( struct directory* directory, child_path_fs = map_directory_child_fs(contdir, vtrack); - song->tag = plugin->tag_dup(child_path_fs); + song->tag = tag_new(); + decoder_plugin_scan_file(plugin, child_path_fs, + &add_tag_handler, song->tag); g_free(child_path_fs); directory_add_song(contdir, song); diff --git a/test/read_tags.c b/test/read_tags.c index 86d508a5c..9fcf1acfe 100644 --- a/test/read_tags.c +++ b/test/read_tags.c @@ -25,9 +25,9 @@ #include "input_stream.h" #include "audio_format.h" #include "pcm_volume.h" -#include "tag_pool.h" #include "tag_ape.h" #include "tag_id3.h" +#include "tag_handler.h" #include "idle.h" #include <glib.h> @@ -134,25 +134,31 @@ decoder_mixramp(G_GNUC_UNUSED struct decoder *decoder, g_free(mixramp_end); } +static bool empty = true; + static void -print_tag(const struct tag *tag) +print_duration(unsigned seconds, G_GNUC_UNUSED void *ctx) { - if (tag->time >= 0) - g_print("time=%d\n", tag->time); + g_print("duration=%d\n", seconds); +} - for (unsigned i = 0; i < tag->num_items; ++i) - g_print("%s=%s\n", - tag_item_names[tag->items[i]->type], - tag->items[i]->value); +static void +print_tag(enum tag_type type, const char *value, G_GNUC_UNUSED void *ctx) +{ + g_print("%s=%s\n", tag_item_names[type], value); + empty = false; } +static const struct tag_handler print_handler = { + .duration = print_duration, + .tag = print_tag, +}; + int main(int argc, char **argv) { GError *error = NULL; const char *decoder_name, *path; const struct decoder_plugin *plugin; - struct tag *tag; - bool empty; #ifdef HAVE_LOCALE_H /* initialize locale */ @@ -175,8 +181,6 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - tag_pool_init(); - if (!input_stream_global_init(&error)) { g_warning("%s", error->message); g_error_free(error); @@ -191,8 +195,9 @@ int main(int argc, char **argv) return 1; } - tag = decoder_plugin_tag_dup(plugin, path); - if (tag == NULL && plugin->stream_tag != NULL) { + bool success = decoder_plugin_scan_file(plugin, path, + &print_handler, NULL); + if (!success && plugin->scan_stream != NULL) { GMutex *mutex = g_mutex_new(); GCond *cond = g_cond_new(); @@ -206,7 +211,8 @@ int main(int argc, char **argv) return 1; } - tag = decoder_plugin_stream_tag(plugin, is); + success = decoder_plugin_scan_stream(plugin, is, + &print_handler, NULL); input_stream_close(is); g_cond_free(cond); @@ -217,27 +223,16 @@ int main(int argc, char **argv) input_stream_global_finish(); io_thread_deinit(); - if (tag == NULL) { + if (!success) { g_printerr("Failed to read tags\n"); return 1; } - print_tag(tag); - - empty = tag_is_empty(tag); - tag_free(tag); - if (empty) { - tag = tag_ape_load(path); - if (tag == NULL) - tag = tag_id3_load(path); - if (tag != NULL) { - print_tag(tag); - tag_free(tag); - } + tag_ape_scan2(path, &print_handler, NULL); + if (empty) + tag_id3_scan(path, &print_handler, NULL); } - tag_pool_deinit(); - return 0; } |