aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTsuda Kageyu <tsuda.kageyu@gmail.com>2017-06-12 13:04:15 +0900
committerGitHub <noreply@github.com>2017-06-12 13:04:15 +0900
commit1fb310ec1fec60b9d6f8aae641905d1cd95b2069 (patch)
tree9116c154bdf832d5743139818cf6f66663a17796
parentc8bcd153fe4a1c9c792dd8cd404226b19a3fc1c7 (diff)
parentc2fe93c12b70f3ae5604564558144282af1e48c2 (diff)
Merge pull request #799 from TsudaKageyu/filetype-detection
Enable FileRef to detect file types by the actual content of a stream.
-rw-r--r--NEWS1
-rw-r--r--taglib/ape/apefile.cpp12
-rw-r--r--taglib/ape/apefile.h9
-rw-r--r--taglib/asf/asffile.cpp13
-rw-r--r--taglib/asf/asffile.h9
-rw-r--r--taglib/fileref.cpp259
-rw-r--r--taglib/fileref.h4
-rw-r--r--taglib/flac/flacfile.cpp12
-rw-r--r--taglib/flac/flacfile.h9
-rw-r--r--taglib/mp4/mp4file.cpp18
-rw-r--r--taglib/mp4/mp4file.h9
-rw-r--r--taglib/mpc/mpcfile.cpp13
-rw-r--r--taglib/mpc/mpcfile.h9
-rw-r--r--taglib/mpeg/mpegfile.cpp49
-rw-r--r--taglib/mpeg/mpegfile.h9
-rw-r--r--taglib/mpeg/mpegheader.cpp21
-rw-r--r--taglib/mpeg/mpegutils.h6
-rw-r--r--taglib/ogg/flac/oggflacfile.cpp13
-rw-r--r--taglib/ogg/flac/oggflacfile.h8
-rw-r--r--taglib/ogg/opus/opusfile.cpp13
-rw-r--r--taglib/ogg/opus/opusfile.h9
-rw-r--r--taglib/ogg/speex/speexfile.cpp13
-rw-r--r--taglib/ogg/speex/speexfile.h9
-rw-r--r--taglib/ogg/vorbis/vorbisfile.cpp14
-rw-r--r--taglib/ogg/vorbis/vorbisfile.h8
-rw-r--r--taglib/riff/aiff/aifffile.cpp13
-rw-r--r--taglib/riff/aiff/aifffile.h8
-rw-r--r--taglib/riff/wav/wavfile.cpp21
-rw-r--r--taglib/riff/wav/wavfile.h9
-rw-r--r--taglib/tagutils.cpp26
-rw-r--r--taglib/tagutils.h6
-rw-r--r--taglib/trueaudio/trueaudiofile.cpp12
-rw-r--r--taglib/trueaudio/trueaudiofile.h9
-rw-r--r--taglib/wavpack/wavpackfile.cpp12
-rw-r--r--taglib/wavpack/wavpackfile.h8
-rw-r--r--tests/test_fileref.cpp76
36 files changed, 663 insertions, 86 deletions
diff --git a/NEWS b/NEWS
index 67908264..4a7abb56 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,7 @@
* Fixed handling of 'rate' atoms in MP4 files.
* Fixed possible file corruptions when saving Ogg files.
* Better handling of invalid UTF-8 sequences.
+ * Marked FileRef::create() deprecated. It returns null.
* Several smaller bug fixes and performance improvements.
TagLib 1.11.1 (Oct 24, 2016)
diff --git a/taglib/ape/apefile.cpp b/taglib/ape/apefile.cpp
index 9f298aaf..a10c1f64 100644
--- a/taglib/ape/apefile.cpp
+++ b/taglib/ape/apefile.cpp
@@ -84,6 +84,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool APE::File::isSupported(IOStream *stream)
+{
+ // An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
+
+ const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
+ return (buffer.find("MAC ") >= 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ape/apefile.h b/taglib/ape/apefile.h
index cfb19ff7..267778ba 100644
--- a/taglib/ape/apefile.h
+++ b/taglib/ape/apefile.h
@@ -211,6 +211,15 @@ namespace TagLib {
*/
bool hasID3v1Tag() const;
+ /*!
+ * Returns whether or not the given \a stream can be opened as an APE
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp
index 8f395265..d5a80bca 100644
--- a/taglib/asf/asffile.cpp
+++ b/taglib/asf/asffile.cpp
@@ -27,6 +27,7 @@
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tstring.h>
+#include <tagutils.h>
#include "asffile.h"
#include "asftag.h"
@@ -474,6 +475,18 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
}
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool ASF::File::isSupported(IOStream *stream)
+{
+ // An ASF file has to start with the designated GUID.
+
+ const ByteVector id = Utils::readHeader(stream, 16, false);
+ return (id == headerGuid);
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/asf/asffile.h b/taglib/asf/asffile.h
index b674da79..05cf4ee2 100644
--- a/taglib/asf/asffile.h
+++ b/taglib/asf/asffile.h
@@ -115,6 +115,15 @@ namespace TagLib {
*/
virtual bool save();
+ /*!
+ * Returns whether or not the given \a stream can be opened as an ASF
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
void read();
diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp
index dca69f6a..935c371b 100644
--- a/taglib/fileref.cpp
+++ b/taglib/fileref.cpp
@@ -28,6 +28,7 @@
***************************************************************************/
#include <tfile.h>
+#include <tfilestream.h>
#include <tstring.h>
#include <tdebug.h>
#include <trefcounter.h>
@@ -59,60 +60,148 @@ namespace
typedef List<const FileRef::FileTypeResolver *> ResolverList;
ResolverList fileTypeResolvers;
- // Templatized internal functions. T should be String or IOStream*.
+ // Detect the file type by user-defined resolvers.
- template <typename T>
- FileName toFileName(T arg);
-
- template <>
- FileName toFileName<IOStream *>(IOStream *arg)
+ File *detectByResolvers(FileName fileName, bool readAudioProperties,
+ AudioProperties::ReadStyle audioPropertiesStyle)
{
- return arg->name();
- }
+ ResolverList::ConstIterator it = fileTypeResolvers.begin();
+ for(; it != fileTypeResolvers.end(); ++it) {
+ File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
+ if(file)
+ return file;
+ }
- template <>
- FileName toFileName<FileName>(FileName arg)
- {
- return arg;
+ return 0;
}
- template <typename T>
- File *resolveFileType(T arg, bool readProperties,
- AudioProperties::ReadStyle style);
+ // Detect the file type based on the file extension.
- template <>
- File *resolveFileType<IOStream *>(IOStream *arg, bool readProperties,
- AudioProperties::ReadStyle style)
+ File* detectByExtension(IOStream *stream, bool readAudioProperties,
+ AudioProperties::ReadStyle audioPropertiesStyle)
{
+#ifdef _WIN32
+ const String s = stream->name().toString();
+#else
+ const String s(stream->name());
+#endif
+
+ String ext;
+ const int pos = s.rfind(".");
+ if(pos != -1)
+ ext = s.substr(pos + 1).upper();
+
+ // If this list is updated, the method defaultFileExtensions() should also be
+ // updated. However at some point that list should be created at the same time
+ // that a default file type resolver is created.
+
+ if(ext.isEmpty())
+ return 0;
+
+ // .oga can be any audio in the Ogg container. So leave it to content-based detection.
+
+ if(ext == "MP3")
+ return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+ if(ext == "OGG")
+ return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "FLAC")
+ return new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+ if(ext == "MPC")
+ return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "WV")
+ return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "SPX")
+ return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "OPUS")
+ return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "TTA")
+ return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
+ return new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "WMA" || ext == "ASF")
+ return new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
+ return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "WAV")
+ return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "APE")
+ return new APE::File(stream, readAudioProperties, audioPropertiesStyle);
+ // module, nst and wow are possible but uncommon extensions
+ if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
+ return new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "S3M")
+ return new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "IT")
+ return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
+ if(ext == "XM")
+ return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
+
return 0;
}
- template <>
- File *resolveFileType<FileName>(FileName arg, bool readProperties,
- AudioProperties::ReadStyle style)
+ // Detect the file type based on the actual content of the stream.
+
+ File *detectByContent(IOStream *stream, bool readAudioProperties,
+ AudioProperties::ReadStyle audioPropertiesStyle)
{
- ResolverList::ConstIterator it = fileTypeResolvers.begin();
- for(; it != fileTypeResolvers.end(); ++it) {
- File *file = (*it)->createFile(arg, readProperties, style);
- if(file)
+ File *file = 0;
+
+ if(MPEG::File::isSupported(stream))
+ file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+ else if(Ogg::Vorbis::File::isSupported(stream))
+ file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(Ogg::FLAC::File::isSupported(stream))
+ file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(FLAC::File::isSupported(stream))
+ file = new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+ else if(MPC::File::isSupported(stream))
+ file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(WavPack::File::isSupported(stream))
+ file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(Ogg::Speex::File::isSupported(stream))
+ file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(Ogg::Opus::File::isSupported(stream))
+ file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(TrueAudio::File::isSupported(stream))
+ file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(MP4::File::isSupported(stream))
+ file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(ASF::File::isSupported(stream))
+ file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(RIFF::AIFF::File::isSupported(stream))
+ file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(RIFF::WAV::File::isSupported(stream))
+ file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
+ else if(APE::File::isSupported(stream))
+ file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
+
+ // isSupported() only does a quick check, so double check the file here.
+
+ if(file) {
+ if(file->isValid())
return file;
+ else
+ delete file;
}
return 0;
}
- template <typename T>
- File* createInternal(T arg, bool readAudioProperties,
+ // Internal function that supports FileRef::create().
+ // This looks redundant, but necessary in order not to change the previous
+ // behavior of FileRef::create().
+
+ File* createInternal(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
- File *file = resolveFileType(arg, readAudioProperties, audioPropertiesStyle);
+ File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
#ifdef _WIN32
- const String s = toFileName(arg).toString();
+ const String s = fileName.toString();
#else
- const String s(toFileName(arg));
+ const String s(fileName);
#endif
String ext;
@@ -120,56 +209,52 @@ namespace
if(pos != -1)
ext = s.substr(pos + 1).upper();
- // If this list is updated, the method defaultFileExtensions() should also be
- // updated. However at some point that list should be created at the same time
- // that a default file type resolver is created.
-
if(ext.isEmpty())
return 0;
if(ext == "MP3")
- return new MPEG::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+ return new MPEG::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
- return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OGA") {
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
- File *file = new Ogg::FLAC::File(arg, readAudioProperties, audioPropertiesStyle);
+ File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(file->isValid())
return file;
delete file;
- return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
}
if(ext == "FLAC")
- return new FLAC::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
+ return new FLAC::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "MPC")
- return new MPC::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
- return new WavPack::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
- return new Ogg::Speex::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS")
- return new Ogg::Opus::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
- return new TrueAudio::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
- return new MP4::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
- return new ASF::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
- return new RIFF::AIFF::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
- return new RIFF::WAV::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
- return new APE::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
- return new Mod::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
- return new S3M::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
- return new IT::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
- return new XM::File(arg, readAudioProperties, audioPropertiesStyle);
+ return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
return 0;
}
@@ -178,15 +263,18 @@ namespace
class FileRef::FileRefPrivate : public RefCounter
{
public:
- FileRefPrivate(File *f) :
+ FileRefPrivate() :
RefCounter(),
- file(f) {}
+ file(0),
+ stream(0) {}
~FileRefPrivate() {
delete file;
+ delete stream;
}
- File *file;
+ File *file;
+ IOStream *stream;
};
////////////////////////////////////////////////////////////////////////////////
@@ -194,24 +282,27 @@ public:
////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef() :
- d(new FileRefPrivate(0))
+ d(new FileRefPrivate())
{
}
FileRef::FileRef(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) :
- d(new FileRefPrivate(createInternal(fileName, readAudioProperties, audioPropertiesStyle)))
+ d(new FileRefPrivate())
{
+ parse(fileName, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
- d(new FileRefPrivate(createInternal(stream, readAudioProperties, audioPropertiesStyle)))
+ d(new FileRefPrivate())
{
+ parse(stream, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileRef(File *file) :
- d(new FileRefPrivate(file))
+ d(new FileRefPrivate())
{
+ d->file = file;
}
FileRef::FileRef(const FileRef &ref) :
@@ -333,3 +424,51 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
{
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void FileRef::parse(FileName fileName, bool readAudioProperties,
+ AudioProperties::ReadStyle audioPropertiesStyle)
+{
+ // Try user-defined resolvers.
+
+ d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
+ if(d->file)
+ return;
+
+ // Try to resolve file types based on the file extension.
+
+ d->stream = new FileStream(fileName);
+ d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
+ if(d->file)
+ return;
+
+ // At last, try to resolve file types based on the actual content.
+
+ d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
+ if(d->file)
+ return;
+
+ // Stream have to be closed here if failed to resolve file types.
+
+ delete d->stream;
+ d->stream = 0;
+}
+
+void FileRef::parse(IOStream *stream, bool readAudioProperties,
+ AudioProperties::ReadStyle audioPropertiesStyle)
+{
+ // User-defined resolvers won't work with a stream.
+
+ // Try to resolve file types based on the file extension.
+
+ d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
+ if(d->file)
+ return;
+
+ // At last, try to resolve file types based on the actual content of the file.
+
+ d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
+}
diff --git a/taglib/fileref.h b/taglib/fileref.h
index a12b1a9b..c36f54cb 100644
--- a/taglib/fileref.h
+++ b/taglib/fileref.h
@@ -274,8 +274,10 @@ namespace TagLib {
bool readAudioProperties = true,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
-
private:
+ void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
+ void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
+
class FileRefPrivate;
FileRefPrivate *d;
};
diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp
index b6b72960..7f437194 100644
--- a/taglib/flac/flacfile.cpp
+++ b/taglib/flac/flacfile.cpp
@@ -96,6 +96,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool FLAC::File::isSupported(IOStream *stream)
+{
+ // A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
+
+ const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
+ return (buffer.find("fLaC") >= 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/flac/flacfile.h b/taglib/flac/flacfile.h
index 65d85679..645090e0 100644
--- a/taglib/flac/flacfile.h
+++ b/taglib/flac/flacfile.h
@@ -318,6 +318,15 @@ namespace TagLib {
*/
bool hasID3v2Tag() const;
+ /*!
+ * Returns whether or not the given \a stream can be opened as a FLAC
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp
index 3733fb40..5ad8396d 100644
--- a/taglib/mp4/mp4file.cpp
+++ b/taglib/mp4/mp4file.cpp
@@ -26,6 +26,8 @@
#include <tdebug.h>
#include <tstring.h>
#include <tpropertymap.h>
+#include <tagutils.h>
+
#include "mp4atom.h"
#include "mp4tag.h"
#include "mp4file.h"
@@ -69,6 +71,22 @@ public:
MP4::Properties *properties;
};
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool MP4::File::isSupported(IOStream *stream)
+{
+ // An MP4 file has to have an "ftyp" box first.
+
+ const ByteVector id = Utils::readHeader(stream, 8, false);
+ return id.containsAt("ftyp", 4);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
diff --git a/taglib/mp4/mp4file.h b/taglib/mp4/mp4file.h
index 3840bd02..8a46d17d 100644
--- a/taglib/mp4/mp4file.h
+++ b/taglib/mp4/mp4file.h
@@ -120,6 +120,15 @@ namespace TagLib {
*/
bool hasMP4Tag() const;
+ /*!
+ * Returns whether or not the given \a stream can be opened as an ASF
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
void read(bool readProperties);
diff --git a/taglib/mpc/mpcfile.cpp b/taglib/mpc/mpcfile.cpp
index daf24c8f..0ffaf893 100644
--- a/taglib/mpc/mpcfile.cpp
+++ b/taglib/mpc/mpcfile.cpp
@@ -76,6 +76,19 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool MPC::File::isSupported(IOStream *stream)
+{
+ // A newer MPC file has to start with "MPCK" or "MP+", but older files don't
+ // have keys to do a quick check.
+
+ const ByteVector id = Utils::readHeader(stream, 4, false);
+ return (id == "MPCK" || id.startsWith("MP+"));
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/mpc/mpcfile.h b/taglib/mpc/mpcfile.h
index 541724dc..89a866e3 100644
--- a/taglib/mpc/mpcfile.h
+++ b/taglib/mpc/mpcfile.h
@@ -214,6 +214,15 @@ namespace TagLib {
*/
bool hasAPETag() const;
+ /*!
+ * Returns whether or not the given \a stream can be opened as an MPC
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp
index 74bf779b..217b03dd 100644
--- a/taglib/mpeg/mpegfile.cpp
+++ b/taglib/mpeg/mpegfile.cpp
@@ -77,6 +77,55 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+ // Dummy file class to make a stream work with MPEG::Header.
+
+ class AdapterFile : public TagLib::File
+ {
+ public:
+ AdapterFile(IOStream *stream) : File(stream) {}
+
+ Tag *tag() const { return 0; }
+ AudioProperties *audioProperties() const { return 0; }
+ bool save() { return false; }
+ };
+}
+
+bool MPEG::File::isSupported(IOStream *stream)
+{
+ if(!stream || !stream->isOpen())
+ return false;
+
+ // An MPEG file has MPEG frame headers. An ID3v2 tag may precede.
+
+ // MPEG frame headers are really confusing with irrelevant binary data.
+ // So we check if a frame header is really valid.
+
+ long headerOffset;
+ const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true, &headerOffset);
+
+ const long originalPosition = stream->tell();
+ AdapterFile file(stream);
+
+ for(unsigned int i = 0; i < buffer.size() - 1; ++i) {
+ if(isFrameSync(buffer, i)) {
+ const Header header(&file, headerOffset + i, true);
+ if(header.isValid()) {
+ stream->seek(originalPosition);
+ return true;
+ }
+ }
+ }
+
+ stream->seek(originalPosition);
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/mpeg/mpegfile.h b/taglib/mpeg/mpegfile.h
index e9e97387..2d2dff00 100644
--- a/taglib/mpeg/mpegfile.h
+++ b/taglib/mpeg/mpegfile.h
@@ -370,6 +370,15 @@ namespace TagLib {
*/
bool hasAPETag() const;
+ /*!
+ * Returns whether or not the given \a stream can be opened as an MPEG
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/mpeg/mpegheader.cpp b/taglib/mpeg/mpegheader.cpp
index 610b0320..5a5015d6 100644
--- a/taglib/mpeg/mpegheader.cpp
+++ b/taglib/mpeg/mpegheader.cpp
@@ -197,10 +197,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->version = Version2;
else if(versionBits == 3)
d->version = Version1;
- else {
- debug("MPEG::Header::parse() -- Invalid MPEG version bits.");
+ else
return;
- }
// Set the MPEG layer
@@ -212,10 +210,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->layer = 2;
else if(layerBits == 3)
d->layer = 1;
- else {
- debug("MPEG::Header::parse() -- Invalid MPEG layer bits.");
+ else
return;
- }
d->protectionEnabled = (static_cast<unsigned char>(data[1] & 0x01) == 0);
@@ -244,10 +240,8 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->bitrate = bitrates[versionIndex][layerIndex][bitrateIndex];
- if(d->bitrate == 0) {
- debug("MPEG::Header::parse() -- Invalid bit rate.");
+ if(d->bitrate == 0)
return;
- }
// Set the sample rate
@@ -264,7 +258,6 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
d->sampleRate = sampleRates[d->version][samplerateIndex];
if(d->sampleRate == 0) {
- debug("MPEG::Header::parse() -- Invalid sample rate.");
return;
}
@@ -311,20 +304,16 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength)
file->seek(offset + d->frameLength);
const ByteVector nextData = file->readBlock(4);
- if(nextData.size() < 4) {
- debug("MPEG::Header::parse() -- Could not read the next frame header.");
+ if(nextData.size() < 4)
return;
- }
const unsigned int HeaderMask = 0xfffe0c00;
const unsigned int header = data.toUInt(0, true) & HeaderMask;
const unsigned int nextHeader = nextData.toUInt(0, true) & HeaderMask;
- if(header != nextHeader) {
- debug("MPEG::Header::parse() -- The next frame was not consistent with this frame.");
+ if(header != nextHeader)
return;
- }
}
// Now that we're done parsing, set this to be a valid frame.
diff --git a/taglib/mpeg/mpegutils.h b/taglib/mpeg/mpegutils.h
index 1cee918a..31b45a43 100644
--- a/taglib/mpeg/mpegutils.h
+++ b/taglib/mpeg/mpegutils.h
@@ -45,12 +45,12 @@ namespace TagLib
* \note This does not check the length of the vector, since this is an
* internal utility function.
*/
- inline bool isFrameSync(const ByteVector &bytes)
+ inline bool isFrameSync(const ByteVector &bytes, unsigned int offset = 0)
{
// 0xFF in the second byte is possible in theory, but it's very unlikely.
- const unsigned char b1 = bytes[0];
- const unsigned char b2 = bytes[1];
+ const unsigned char b1 = bytes[offset + 0];
+ const unsigned char b2 = bytes[offset + 1];
return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0);
}
diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp
index fe4d8830..53d04508 100644
--- a/taglib/ogg/flac/oggflacfile.cpp
+++ b/taglib/ogg/flac/oggflacfile.cpp
@@ -27,6 +27,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include <xiphcomment.h>
#include "oggflacfile.h"
@@ -66,6 +67,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Ogg::FLAC::File::isSupported(IOStream *stream)
+{
+ // An Ogg FLAC file has IDs "OggS" and "fLaC" somewhere.
+
+ const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
+ return (buffer.find("OggS") >= 0 && buffer.find("fLaC") >= 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/flac/oggflacfile.h b/taglib/ogg/flac/oggflacfile.h
index 05762f9b..b2686e45 100644
--- a/taglib/ogg/flac/oggflacfile.h
+++ b/taglib/ogg/flac/oggflacfile.h
@@ -143,6 +143,14 @@ namespace TagLib {
*/
bool hasXiphComment() const;
+ /*!
+ * Check if the given \a stream can be opened as an Ogg FLAC file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/ogg/opus/opusfile.cpp b/taglib/ogg/opus/opusfile.cpp
index ff1bfe2d..d4f191ad 100644
--- a/taglib/ogg/opus/opusfile.cpp
+++ b/taglib/ogg/opus/opusfile.cpp
@@ -30,6 +30,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include "opusfile.h"
@@ -54,6 +55,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Ogg::Opus::File::isSupported(IOStream *stream)
+{
+ // An Opus file has IDs "OggS" and "OpusHead" somewhere.
+
+ const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
+ return (buffer.find("OggS") >= 0 && buffer.find("OpusHead") >= 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/opus/opusfile.h b/taglib/ogg/opus/opusfile.h
index b718f0d7..0e094eae 100644
--- a/taglib/ogg/opus/opusfile.h
+++ b/taglib/ogg/opus/opusfile.h
@@ -113,6 +113,15 @@ namespace TagLib {
*/
virtual bool save();
+ /*!
+ * Returns whether or not the given \a stream can be opened as an Opus
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/ogg/speex/speexfile.cpp b/taglib/ogg/speex/speexfile.cpp
index 7af71d50..b3c8a636 100644
--- a/taglib/ogg/speex/speexfile.cpp
+++ b/taglib/ogg/speex/speexfile.cpp
@@ -30,6 +30,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include "speexfile.h"
@@ -54,6 +55,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Ogg::Speex::File::isSupported(IOStream *stream)
+{
+ // A Speex file has IDs "OggS" and "Speex " somewhere.
+
+ const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
+ return (buffer.find("OggS") >= 0 && buffer.find("Speex ") >= 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/speex/speexfile.h b/taglib/ogg/speex/speexfile.h
index 58b001dd..1be7113c 100644
--- a/taglib/ogg/speex/speexfile.h
+++ b/taglib/ogg/speex/speexfile.h
@@ -113,6 +113,15 @@ namespace TagLib {
*/
virtual bool save();
+ /*!
+ * Returns whether or not the given \a stream can be opened as a Speex
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/ogg/vorbis/vorbisfile.cpp b/taglib/ogg/vorbis/vorbisfile.cpp
index 2773bd3b..b4f221ab 100644
--- a/taglib/ogg/vorbis/vorbisfile.cpp
+++ b/taglib/ogg/vorbis/vorbisfile.cpp
@@ -28,10 +28,10 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include "vorbisfile.h"
-
using namespace TagLib;
class Vorbis::File::FilePrivate
@@ -60,6 +60,18 @@ namespace TagLib {
}
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Vorbis::File::isSupported(IOStream *stream)
+{
+ // An Ogg Vorbis file has IDs "OggS" and "\x01vorbis" somewhere.
+
+ const ByteVector buffer = Utils::readHeader(stream, bufferSize(), false);
+ return (buffer.find("OggS") >= 0 && buffer.find("\x01vorbis") >= 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/ogg/vorbis/vorbisfile.h b/taglib/ogg/vorbis/vorbisfile.h
index 9e71dcbe..04c0c04e 100644
--- a/taglib/ogg/vorbis/vorbisfile.h
+++ b/taglib/ogg/vorbis/vorbisfile.h
@@ -121,6 +121,14 @@ namespace TagLib {
*/
virtual bool save();
+ /*!
+ * Check if the given \a stream can be opened as an Ogg Vorbis file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/riff/aiff/aifffile.cpp b/taglib/riff/aiff/aifffile.cpp
index 1a29938c..4f9c868e 100644
--- a/taglib/riff/aiff/aifffile.cpp
+++ b/taglib/riff/aiff/aifffile.cpp
@@ -28,6 +28,7 @@
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
+#include <tagutils.h>
#include "aifffile.h"
@@ -54,6 +55,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool RIFF::AIFF::File::isSupported(IOStream *stream)
+{
+ // An AIFF file has to start with "FORM????AIFF" or "FORM????AIFC".
+
+ const ByteVector id = Utils::readHeader(stream, 12, false);
+ return (id.startsWith("FORM") && (id.containsAt("AIFF", 8) || id.containsAt("AIFC", 8)));
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/riff/aiff/aifffile.h b/taglib/riff/aiff/aifffile.h
index a79d76b2..5ba1a279 100644
--- a/taglib/riff/aiff/aifffile.h
+++ b/taglib/riff/aiff/aifffile.h
@@ -126,6 +126,14 @@ namespace TagLib {
*/
bool hasID3v2Tag() const;
+ /*!
+ * Check if the given \a stream can be opened as an AIFF file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp
index 79ff9167..0ebe21c3 100644
--- a/taglib/riff/wav/wavfile.cpp
+++ b/taglib/riff/wav/wavfile.cpp
@@ -23,10 +23,11 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
-#include "tbytevector.h"
-#include "tdebug.h"
-#include "tstringlist.h"
-#include "tpropertymap.h"
+#include <tbytevector.h>
+#include <tdebug.h>
+#include <tstringlist.h>
+#include <tpropertymap.h>
+#include <tagutils.h>
#include "wavfile.h"
#include "id3v2tag.h"
@@ -61,6 +62,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool RIFF::WAV::File::isSupported(IOStream *stream)
+{
+ // A WAV file has to start with "RIFF????WAVE".
+
+ const ByteVector id = Utils::readHeader(stream, 12, false);
+ return (id.startsWith("RIFF") && id.containsAt("WAVE", 8));
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/riff/wav/wavfile.h b/taglib/riff/wav/wavfile.h
index 80f17a85..f6c190ed 100644
--- a/taglib/riff/wav/wavfile.h
+++ b/taglib/riff/wav/wavfile.h
@@ -175,6 +175,15 @@ namespace TagLib {
*/
bool hasInfoTag() const;
+ /*!
+ * Returns whether or not the given \a stream can be opened as a WAV
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/tagutils.cpp b/taglib/tagutils.cpp
index dc047040..d6d92406 100644
--- a/taglib/tagutils.cpp
+++ b/taglib/tagutils.cpp
@@ -77,3 +77,29 @@ long Utils::findAPE(File *file, long id3v1Location)
return -1;
}
+
+ByteVector TagLib::Utils::readHeader(IOStream *stream, unsigned int length,
+ bool skipID3v2, long *headerOffset)
+{
+ if(!stream || !stream->isOpen())
+ return ByteVector();
+
+ const long originalPosition = stream->tell();
+ long bufferOffset = 0;
+
+ if(skipID3v2) {
+ stream->seek(0);
+ const ByteVector data = stream->readBlock(ID3v2::Header::size());
+ if(data.startsWith(ID3v2::Header::fileIdentifier()))
+ bufferOffset = ID3v2::Header(data).completeTagSize();
+ }
+
+ stream->seek(bufferOffset);
+ const ByteVector header = stream->readBlock(length);
+ stream->seek(originalPosition);
+
+ if(headerOffset)
+ *headerOffset = bufferOffset;
+
+ return header;
+}
diff --git a/taglib/tagutils.h b/taglib/tagutils.h
index fb11d1e0..4488a32b 100644
--- a/taglib/tagutils.h
+++ b/taglib/tagutils.h
@@ -30,9 +30,12 @@
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
+#include <tbytevector.h>
+
namespace TagLib {
class File;
+ class IOStream;
namespace Utils {
@@ -41,6 +44,9 @@ namespace TagLib {
long findID3v2(File *file);
long findAPE(File *file, long id3v1Location);
+
+ ByteVector readHeader(IOStream *stream, unsigned int length, bool skipID3v2,
+ long *headerOffset = 0);
}
}
diff --git a/taglib/trueaudio/trueaudiofile.cpp b/taglib/trueaudio/trueaudiofile.cpp
index fc123ba3..e4de436e 100644
--- a/taglib/trueaudio/trueaudiofile.cpp
+++ b/taglib/trueaudio/trueaudiofile.cpp
@@ -74,6 +74,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool TrueAudio::File::isSupported(IOStream *stream)
+{
+ // A TrueAudio file has to start with "TTA". An ID3v2 tag may precede.
+
+ const ByteVector id = Utils::readHeader(stream, 3, true);
+ return (id == "TTA");
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/trueaudio/trueaudiofile.h b/taglib/trueaudio/trueaudiofile.h
index 4bcb722a..3737ac63 100644
--- a/taglib/trueaudio/trueaudiofile.h
+++ b/taglib/trueaudio/trueaudiofile.h
@@ -235,6 +235,15 @@ namespace TagLib {
*/
bool hasID3v2Tag() const;
+ /*!
+ * Returns whether or not the given \a stream can be opened as a TrueAudio
+ * file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/taglib/wavpack/wavpackfile.cpp b/taglib/wavpack/wavpackfile.cpp
index ef92f4bd..01bdba36 100644
--- a/taglib/wavpack/wavpackfile.cpp
+++ b/taglib/wavpack/wavpackfile.cpp
@@ -72,6 +72,18 @@ public:
};
////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+bool WavPack::File::isSupported(IOStream *stream)
+{
+ // A WavPack file has to start with "wvpk".
+
+ const ByteVector id = Utils::readHeader(stream, 4, false);
+ return (id == "wvpk");
+}
+
+////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
diff --git a/taglib/wavpack/wavpackfile.h b/taglib/wavpack/wavpackfile.h
index 7e0bd27a..ccc4ef6e 100644
--- a/taglib/wavpack/wavpackfile.h
+++ b/taglib/wavpack/wavpackfile.h
@@ -200,6 +200,14 @@ namespace TagLib {
*/
bool hasAPETag() const;
+ /*!
+ * Check if the given \a stream can be opened as a WavPack file.
+ *
+ * \note This method is designed to do a quick check. The result may
+ * not necessarily be correct.
+ */
+ static bool isSupported(IOStream *stream);
+
private:
File(const File &);
File &operator=(const File &);
diff --git a/tests/test_fileref.cpp b/tests/test_fileref.cpp
index 1b899975..e766a891 100644
--- a/tests/test_fileref.cpp
+++ b/tests/test_fileref.cpp
@@ -39,9 +39,10 @@
#include <wavfile.h>
#include <apefile.h>
#include <aifffile.h>
+#include <tfilestream.h>
+#include <tbytevectorstream.h>
#include <cppunit/extensions/HelperMacros.h>
#include "utils.h"
-#include <tfilestream.h>
using namespace std;
using namespace TagLib;
@@ -79,6 +80,7 @@ class TestFileRef : public CppUnit::TestFixture
CPPUNIT_TEST(testAIFF_1);
CPPUNIT_TEST(testAIFF_2);
CPPUNIT_TEST(testUnsupported);
+ CPPUNIT_TEST(testCreate);
CPPUNIT_TEST(testFileResolver);
CPPUNIT_TEST_SUITE_END();
@@ -129,6 +131,7 @@ public:
CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7);
CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080);
}
+
{
FileStream fs(newname.c_str());
FileRef f(&fs);
@@ -140,6 +143,64 @@ public:
CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm"));
CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7);
CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080);
+ f.tag()->setArtist("test artist");
+ f.tag()->setTitle("test title");
+ f.tag()->setGenre("Test!");
+ f.tag()->setAlbum("albummmm");
+ f.tag()->setTrack(5);
+ f.tag()->setYear(2020);
+ f.save();
+ }
+
+ ByteVector fileContent;
+ {
+ FileStream fs(newname.c_str());
+ FileRef f(&fs);
+ CPPUNIT_ASSERT(dynamic_cast<T*>(f.file()));
+ CPPUNIT_ASSERT(!f.isNull());
+ CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5);
+ CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020);
+
+ fs.seek(0);
+ fileContent = fs.readBlock(fs.length());
+ }
+
+ {
+ ByteVectorStream bs(fileContent);
+ FileRef f(&bs);
+ CPPUNIT_ASSERT(dynamic_cast<T*>(f.file()));
+ CPPUNIT_ASSERT(!f.isNull());
+ CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("test artist"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("test title"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("Test!"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("albummmm"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)5);
+ CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2020);
+ f.tag()->setArtist("ttest artist");
+ f.tag()->setTitle("ytest title");
+ f.tag()->setGenre("uTest!");
+ f.tag()->setAlbum("ialbummmm");
+ f.tag()->setTrack(7);
+ f.tag()->setYear(2080);
+ f.save();
+
+ fileContent = *bs.data();
+ }
+ {
+ ByteVectorStream bs(fileContent);
+ FileRef f(&bs);
+ CPPUNIT_ASSERT(dynamic_cast<T*>(f.file()));
+ CPPUNIT_ASSERT(!f.isNull());
+ CPPUNIT_ASSERT_EQUAL(f.tag()->artist(), String("ttest artist"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->title(), String("ytest title"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->genre(), String("uTest!"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->album(), String("ialbummmm"));
+ CPPUNIT_ASSERT_EQUAL(f.tag()->track(), (unsigned int)7);
+ CPPUNIT_ASSERT_EQUAL(f.tag()->year(), (unsigned int)2080);
}
}
@@ -237,6 +298,19 @@ public:
CPPUNIT_ASSERT(f2.isNull());
}
+ void testCreate()
+ {
+ // This is depricated. But worth it to test.
+
+ File *f = FileRef::create(TEST_FILE_PATH_C("empty_vorbis.oga"));
+ CPPUNIT_ASSERT(dynamic_cast<Ogg::Vorbis::File*>(f));
+ delete f;
+
+ f = FileRef::create(TEST_FILE_PATH_C("xing.mp3"));
+ CPPUNIT_ASSERT(dynamic_cast<MPEG::File*>(f));
+ delete f;
+ }
+
void testFileResolver()
{
{