This allow to use these scanners for remote InputStream like SMB and NFS. --- src/tag/Aiff.cxx | 29 +++++---------- src/tag/Aiff.hxx | 4 +- src/tag/ApeLoader.cxx | 43 +++++++++++++++++----- src/tag/ApeLoader.hxx | 11 ++++++ src/tag/ApeTag.cxx | 19 ++++++++++ src/tag/ApeTag.hxx | 10 +++++ src/tag/Riff.cxx | 29 +++++---------- src/tag/Riff.hxx | 4 +- src/tag/TagId3.cxx | 100 ++++++++++++++++++++++++++++++++++---------------- src/tag/TagId3.hxx | 11 ++++-- 10 files changed, 172 insertions(+), 88 deletions(-)
diff --git a/src/tag/Aiff.cxx b/src/tag/Aiff.cxx index 7235b76..c4a2315 100644 --- a/src/tag/Aiff.cxx +++ b/src/tag/Aiff.cxx @@ -20,6 +20,7 @@ #include "config.h" /* must be first for large file support */ #include "Aiff.hxx" #include "util/Domain.hxx" +#include "util/Error.hxx" #include "system/ByteOrder.hxx" #include "Log.hxx" @@ -43,28 +44,16 @@ struct aiff_chunk_header { }; size_t -aiff_seek_id3(FILE *file) +aiff_seek_id3(InputStream &is) { - /* determine the file size */ - - struct stat st; - if (fstat(fileno(file), &st) < 0) { - LogErrno(aiff_domain, "Failed to stat file descriptor"); - return 0; - } - /* seek to the beginning and read the AIFF header */ - - if (fseek(file, 0, SEEK_SET) != 0) { - LogErrno(aiff_domain, "Failed to seek"); - return 0; - } + is.Rewind(IgnoreError()); aiff_header header; - size_t size = fread(&header, sizeof(header), 1, file); - if (size != 1 || + size_t size = is.ReadFull(&header, sizeof(header), IgnoreError()); + if (size != sizeof(header) || memcmp(header.id, "FORM", 4) != 0 || - FromBE32(header.size) > (uint32_t)st.st_size || + FromBE32(header.size) > is.GetSize() || (memcmp(header.format, "AIFF", 4) != 0 && memcmp(header.format, "AIFC", 4) != 0)) /* not a AIFF file */ @@ -74,8 +63,8 @@ aiff_seek_id3(FILE *file) /* read the chunk header */ aiff_chunk_header chunk; - size = fread(&chunk, sizeof(chunk), 1, file); - if (size != 1) + size = is.ReadFull(&chunk, sizeof(chunk), IgnoreError()); + if (size != sizeof(chunk)) return 0; size = FromBE32(chunk.size); @@ -92,7 +81,7 @@ aiff_seek_id3(FILE *file) /* found it! */ return size; - if (fseek(file, size, SEEK_CUR) != 0) + if (!is.Seek(size + is.GetOffset(), IgnoreError())) return 0; } } diff --git a/src/tag/Aiff.hxx b/src/tag/Aiff.hxx index f9b11b5..238e5ba 100644 --- a/src/tag/Aiff.hxx +++ b/src/tag/Aiff.hxx @@ -28,6 +28,8 @@ #include <stddef.h> #include <stdio.h> +#include "input/InputStream.hxx" + /** * Seeks the AIFF file to the ID3 chunk. * @@ -35,6 +37,6 @@ * AIFF file or no ID3 chunk was found */ size_t -aiff_seek_id3(FILE *file); +aiff_seek_id3(InputStream &is); #endif diff --git a/src/tag/ApeLoader.cxx b/src/tag/ApeLoader.cxx index 8c89f34..4e255b6 100644 --- a/src/tag/ApeLoader.cxx +++ b/src/tag/ApeLoader.cxx @@ -21,6 +21,9 @@ #include "ApeLoader.hxx" #include "system/ByteOrder.hxx" #include "fs/FileSystem.hxx" +#include "util/Error.hxx" +#include "thread/Mutex.hxx" +#include "thread/Cond.hxx" #include <stdint.h> #include <assert.h> @@ -36,31 +39,46 @@ struct ape_footer { unsigned char reserved[8]; }; -static bool -ape_scan_internal(FILE *fp, ApeTagCallback callback) +bool +tag_ape_scan(InputStream &is, ApeTagCallback callback) { + is.Lock(); + if (!is.KnownSize() || !is.IsSeekable()) + { + is.Unlock(); + return false; + } /* determine if file has an apeV2 tag */ struct ape_footer footer; - if (fseek(fp, -(long)sizeof(footer), SEEK_END) || - fread(&footer, 1, sizeof(footer), fp) != sizeof(footer) || + if (!is.Seek(is.GetSize() - (long)sizeof(footer), IgnoreError()) || + is.ReadFull(&footer, sizeof(footer), IgnoreError()) != sizeof(footer) || memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0 || FromLE32(footer.version) != 2000) + { + is.Rewind(IgnoreError()); + is.Unlock(); return false; + } /* find beginning of ape tag */ size_t remaining = FromLE32(footer.length); if (remaining <= sizeof(footer) + 10 || /* refuse to load more than one megabyte of tag data */ remaining > 1024 * 1024 || - fseek(fp, -(long)remaining, SEEK_END)) + !is.Seek(is.GetSize() - (long)remaining, IgnoreError())) { + is.Rewind(IgnoreError()); + is.Unlock(); return false; + } /* read tag into buffer */ remaining -= sizeof(footer); assert(remaining > 10); char *buffer = new char[remaining]; - if (fread(buffer, 1, remaining, fp) != remaining) { + if (is.ReadFull(buffer, remaining, IgnoreError()) != remaining) { + is.Rewind(IgnoreError()); + is.Unlock(); delete[] buffer; return false; } @@ -96,6 +114,8 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback) remaining -= size; } + is.Rewind(IgnoreError()); + is.Unlock(); delete[] buffer; return true; } @@ -103,11 +123,14 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback) bool tag_ape_scan(Path path_fs, ApeTagCallback callback) { - FILE *fp = FOpen(path_fs, PATH_LITERAL("rb")); - if (fp == nullptr) + Mutex mutex; + Cond cond; + InputStream *is = InputStream::OpenReady(path_fs.c_str(), mutex, + cond, IgnoreError()); + if (is == nullptr) return false; - bool success = ape_scan_internal(fp, callback); - fclose(fp); + bool success = tag_ape_scan(*is, callback); + delete is; return success; } diff --git a/src/tag/ApeLoader.hxx b/src/tag/ApeLoader.hxx index 1bdfe69..34ecf4b 100644 --- a/src/tag/ApeLoader.hxx +++ b/src/tag/ApeLoader.hxx @@ -21,6 +21,7 @@ #define MPD_APE_LOADER_HXX #include "check.h" +#include "input/InputStream.hxx" #include <functional> @@ -35,6 +36,16 @@ typedef std::function<bool(unsigned long flags, const char *key, /** * Scans the APE tag values from a file. * + * @param is the input stream to read from + * @return false if the file could not be opened or if no APE tag is + * present + */ +bool +tag_ape_scan(InputStream &is, ApeTagCallback callback); + +/** + * Scans the APE tag values from a file. + * * @param path_fs the path of the file in filesystem encoding * @return false if the file could not be opened or if no APE tag is * present diff --git a/src/tag/ApeTag.cxx b/src/tag/ApeTag.cxx index 49ae7a0..dbbd034 100644 --- a/src/tag/ApeTag.cxx +++ b/src/tag/ApeTag.cxx @@ -104,6 +104,25 @@ tag_ape_import_item(unsigned long flags, } bool +tag_ape_scan2(InputStream &is, + const struct tag_handler *handler, void *handler_ctx) +{ + bool recognized = false; + + auto callback = [handler, handler_ctx, &recognized] + (unsigned long flags, const char *key, + const char *value, + size_t value_length) { + recognized |= tag_ape_import_item(flags, key, value, + value_length, + handler, handler_ctx); + return true; + }; + + return tag_ape_scan(is, callback) && recognized; +} + +bool tag_ape_scan2(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { diff --git a/src/tag/ApeTag.hxx b/src/tag/ApeTag.hxx index 20d1d7b..7e5fa65 100644 --- a/src/tag/ApeTag.hxx +++ b/src/tag/ApeTag.hxx @@ -20,6 +20,7 @@ #ifndef MPD_APE_TAG_HXX #define MPD_APE_TAG_HXX +#include "input/InputStream.hxx" #include "TagTable.hxx" class Path; @@ -30,6 +31,15 @@ extern const struct tag_table ape_tags[]; /** * Scan the APE tags of a file. * + * @param is the locked input stream to read from + */ +bool +tag_ape_scan2(InputStream &is, + const tag_handler *handler, void *handler_ctx); + +/** + * Scan the APE tags of a file. + * * @param path_fs the path of the file in filesystem encoding */ bool diff --git a/src/tag/Riff.cxx b/src/tag/Riff.cxx index 7412258..1a29583 100644 --- a/src/tag/Riff.cxx +++ b/src/tag/Riff.cxx @@ -20,6 +20,7 @@ #include "config.h" /* must be first for large file support */ #include "Riff.hxx" #include "util/Domain.hxx" +#include "util/Error.hxx" #include "system/ByteOrder.hxx" #include "Log.hxx" @@ -43,28 +44,16 @@ struct riff_chunk_header { }; size_t -riff_seek_id3(FILE *file) +riff_seek_id3(InputStream &is) { - /* determine the file size */ - - struct stat st; - if (fstat(fileno(file), &st) < 0) { - LogErrno(riff_domain, "Failed to stat file descriptor"); - return 0; - } - /* seek to the beginning and read the RIFF header */ - - if (fseek(file, 0, SEEK_SET) != 0) { - LogErrno(riff_domain, "Failed to seek"); - return 0; - } + is.Rewind(IgnoreError()); riff_header header; - size_t size = fread(&header, sizeof(header), 1, file); - if (size != 1 || + size_t size = is.ReadFull(&header, sizeof(header), IgnoreError()); + if (size != sizeof(header) || memcmp(header.id, "RIFF", 4) != 0 || - FromLE32(header.size) > (uint32_t)st.st_size) + FromLE32(header.size) > is.GetSize()) /* not a RIFF file */ return 0; @@ -72,8 +61,8 @@ riff_seek_id3(FILE *file) /* read the chunk header */ riff_chunk_header chunk; - size = fread(&chunk, sizeof(chunk), 1, file); - if (size != 1) + size = is.ReadFull(&chunk, sizeof(chunk), IgnoreError()); + if (size != sizeof(chunk)) return 0; size = FromLE32(chunk.size); @@ -91,7 +80,7 @@ riff_seek_id3(FILE *file) /* found it! */ return size; - if (fseek(file, size, SEEK_CUR) != 0) + if (!is.Seek(size + is.GetOffset(), IgnoreError())) return 0; } } diff --git a/src/tag/Riff.hxx b/src/tag/Riff.hxx index b157168..5deabcb 100644 --- a/src/tag/Riff.hxx +++ b/src/tag/Riff.hxx @@ -28,6 +28,8 @@ #include <stddef.h> #include <stdio.h> +#include "input/InputStream.hxx" + /** * Seeks the RIFF file to the ID3 chunk. * @@ -35,6 +37,6 @@ * RIFF file or no ID3 chunk was found */ size_t -riff_seek_id3(FILE *file); +riff_seek_id3(InputStream &is); #endif diff --git a/src/tag/TagId3.cxx b/src/tag/TagId3.cxx index 98f85da..32443e9 100644 --- a/src/tag/TagId3.cxx +++ b/src/tag/TagId3.cxx @@ -32,6 +32,8 @@ #include "Aiff.hxx" #include "fs/Path.hxx" #include "fs/FileSystem.hxx" +#include "thread/Mutex.hxx" +#include "thread/Cond.hxx" #ifdef HAVE_GLIB #include <glib.h> @@ -394,28 +396,46 @@ tag_id3_import(struct id3_tag *tag) } static size_t -fill_buffer(void *buf, size_t size, FILE *stream, long offset, int whence) +fill_buffer(void *buf, size_t size, InputStream &is, long offset, int whence) { - if (fseek(stream, offset, whence) != 0) return 0; - return fread(buf, 1, size, stream); + offset_type absolute_offset; + switch (whence) + { + case SEEK_SET: + absolute_offset = offset; + break; + case SEEK_CUR: + absolute_offset = offset + is.GetOffset(); + break; + case SEEK_END: + absolute_offset = is.GetSize() + offset; + break; + default: + assert(false); + gcc_unreachable(); + } + if (!is.Seek(absolute_offset, IgnoreError())) + return 0; + + return is.ReadFull(buf, size, IgnoreError()); } static long -get_id3v2_footer_size(FILE *stream, long offset, int whence) +get_id3v2_footer_size(InputStream &is, long offset, int whence) { id3_byte_t buf[ID3_TAG_QUERYSIZE]; - size_t bufsize = fill_buffer(buf, ID3_TAG_QUERYSIZE, stream, offset, whence); + size_t bufsize = fill_buffer(buf, ID3_TAG_QUERYSIZE, is, offset, whence); if (bufsize == 0) return 0; return id3_tag_query(buf, bufsize); } static struct id3_tag * -tag_id3_read(FILE *stream, long offset, int whence) +tag_id3_read(InputStream &is, long offset, int whence) { /* It's ok if we get less than we asked for */ id3_byte_t query_buffer[ID3_TAG_QUERYSIZE]; size_t query_buffer_size = fill_buffer(query_buffer, ID3_TAG_QUERYSIZE, - stream, offset, whence); + is, offset, whence); if (query_buffer_size <= 0) return nullptr; @@ -426,7 +446,7 @@ tag_id3_read(FILE *stream, long offset, int whence) /* Found a tag. Allocate a buffer and read it in. */ id3_byte_t *tag_buffer = new id3_byte_t[tag_size]; int tag_buffer_size = fill_buffer(tag_buffer, tag_size, - stream, offset, whence); + is, offset, whence); if (tag_buffer_size < tag_size) { delete[] tag_buffer; return nullptr; @@ -438,9 +458,9 @@ tag_id3_read(FILE *stream, long offset, int whence) } static struct id3_tag * -tag_id3_find_from_beginning(FILE *stream) +tag_id3_find_from_beginning(InputStream &is) { - id3_tag *tag = tag_id3_read(stream, 0, SEEK_SET); + id3_tag *tag = tag_id3_read(is, 0, SEEK_SET); if (!tag) { return nullptr; } else if (tag_is_id3v1(tag)) { @@ -458,7 +478,7 @@ tag_id3_find_from_beginning(FILE *stream) break; /* Get the tag specified by the SEEK frame */ - id3_tag *seektag = tag_id3_read(stream, seek, SEEK_CUR); + id3_tag *seektag = tag_id3_read(is, seek, SEEK_CUR); if (!seektag || tag_is_id3v1(seektag)) break; @@ -471,18 +491,18 @@ tag_id3_find_from_beginning(FILE *stream) } static struct id3_tag * -tag_id3_find_from_end(FILE *stream) +tag_id3_find_from_end(InputStream &is) { /* Get an id3v1 tag from the end of file for later use */ - id3_tag *v1tag = tag_id3_read(stream, -128, SEEK_END); + id3_tag *v1tag = tag_id3_read(is, -128, SEEK_END); /* Get the id3v2 tag size from the footer (located before v1tag) */ - int tagsize = get_id3v2_footer_size(stream, (v1tag ? -128 : 0) - 10, SEEK_END); + int tagsize = get_id3v2_footer_size(is, (v1tag ? -128 : 0) - 10, SEEK_END); if (tagsize >= 0) return v1tag; /* Get the tag which the footer belongs to */ - id3_tag *tag = tag_id3_read(stream, tagsize, SEEK_CUR); + id3_tag *tag = tag_id3_read(is, tagsize, SEEK_CUR); if (!tag) return v1tag; @@ -493,11 +513,11 @@ tag_id3_find_from_end(FILE *stream) } static struct id3_tag * -tag_id3_riff_aiff_load(FILE *file) +tag_id3_riff_aiff_load(InputStream &is) { - size_t size = riff_seek_id3(file); + size_t size = riff_seek_id3(is); if (size == 0) - size = aiff_seek_id3(file); + size = aiff_seek_id3(is); if (size == 0) return nullptr; @@ -506,8 +526,8 @@ tag_id3_riff_aiff_load(FILE *file) return nullptr; id3_byte_t *buffer = new id3_byte_t[size]; - size_t ret = fread(buffer, size, 1, file); - if (ret != 1) { + size_t ret = is.ReadFull(buffer, size, IgnoreError()); + if (ret != size) { LogWarning(id3_domain, "Failed to read RIFF chunk"); delete[] buffer; return nullptr; @@ -519,31 +539,31 @@ tag_id3_riff_aiff_load(FILE *file) } struct id3_tag * -tag_id3_load(Path path_fs, Error &error) +tag_id3_load(InputStream &is, Error &error) { - FILE *file = FOpen(path_fs, PATH_LITERAL("rb")); - if (file == nullptr) { - error.FormatErrno("Failed to open file %s", path_fs.c_str()); + is.Lock(); + if (!is.KnownSize() || !is.IsSeekable()) + { + is.Unlock(); return nullptr; } - - struct id3_tag *tag = tag_id3_find_from_beginning(file); + struct id3_tag *tag = tag_id3_find_from_beginning(is); if (tag == nullptr) { - tag = tag_id3_riff_aiff_load(file); + tag = tag_id3_riff_aiff_load(is); if (tag == nullptr) - tag = tag_id3_find_from_end(file); + tag = tag_id3_find_from_end(is); } - - fclose(file); + is.Rewind(error); + is.Unlock(); return tag; } bool -tag_id3_scan(Path path_fs, +tag_id3_scan(InputStream &is, const struct tag_handler *handler, void *handler_ctx) { Error error; - struct id3_tag *tag = tag_id3_load(path_fs, error); + struct id3_tag *tag = tag_id3_load(is, error); if (tag == nullptr) { if (error.IsDefined()) LogError(error); @@ -555,3 +575,19 @@ tag_id3_scan(Path path_fs, id3_tag_delete(tag); return true; } + +bool +tag_id3_scan(Path path_fs, + const struct tag_handler *handler, void *handler_ctx) +{ + Mutex mutex; + Cond cond; + InputStream *is = InputStream::OpenReady(path_fs.c_str(), mutex, + cond, IgnoreError()); + if (is == nullptr) + return false; + + bool success = tag_id3_scan(*is, handler, handler_ctx); + delete is; + return success; +} diff --git a/src/tag/TagId3.hxx b/src/tag/TagId3.hxx index 94dfb17..d3b987b 100644 --- a/src/tag/TagId3.hxx +++ b/src/tag/TagId3.hxx @@ -22,6 +22,7 @@ #include "check.h" #include "Compiler.h" +#include "input/InputStream.hxx" class Path; struct tag_handler; @@ -32,6 +33,10 @@ class Error; #ifdef ENABLE_ID3TAG bool +tag_id3_scan(InputStream &is, + const tag_handler *handler, void *handler_ctx); + +bool tag_id3_scan(Path path_fs, const tag_handler *handler, void *handler_ctx); @@ -46,7 +51,7 @@ tag_id3_import(id3_tag *); * Error will be set) */ struct id3_tag * -tag_id3_load(Path path_fs, Error &error); +tag_id3_load(InputStream &is, Error &error); /** * Import all tags from the provided id3_tag *tag @@ -58,10 +63,8 @@ scan_id3_tag(id3_tag *tag, #else -#include "fs/Path.hxx" - static inline bool -tag_id3_scan(gcc_unused Path path_fs, +tag_id3_scan(gcc_unused InputStream &is, gcc_unused const tag_handler *handler, gcc_unused void *handler_ctx) { -- 2.1.4 _______________________________________________ mpd-devel mailing list mpd-devel@musicpd.org http://mailman.blarg.de/listinfo/mpd-devel