mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
Merge pull request #799 from TsudaKageyu/filetype-detection
Enable FileRef to detect file types by the actual content of a stream.
This commit is contained in:
commit
1fb310ec1f
1
NEWS
1
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)
|
||||
|
@ -83,6 +83,18 @@ public:
|
||||
Properties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asftag.h"
|
||||
@ -473,6 +474,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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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();
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tfilestream.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <trefcounter.h>
|
||||
@ -59,41 +60,14 @@ 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)
|
||||
{
|
||||
return arg->name();
|
||||
}
|
||||
|
||||
template <>
|
||||
FileName toFileName<FileName>(FileName arg)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
File *resolveFileType(T arg, bool readProperties,
|
||||
AudioProperties::ReadStyle style);
|
||||
|
||||
template <>
|
||||
File *resolveFileType<IOStream *>(IOStream *arg, bool readProperties,
|
||||
AudioProperties::ReadStyle style)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
File *resolveFileType<FileName>(FileName arg, bool readProperties,
|
||||
AudioProperties::ReadStyle style)
|
||||
File *detectByResolvers(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
ResolverList::ConstIterator it = fileTypeResolvers.begin();
|
||||
for(; it != fileTypeResolvers.end(); ++it) {
|
||||
File *file = (*it)->createFile(arg, readProperties, style);
|
||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
@ -101,18 +75,15 @@ namespace
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
File* createInternal(T arg, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = resolveFileType(arg, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
// Detect the file type based on the file extension.
|
||||
|
||||
File* detectByExtension(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const String s = toFileName(arg).toString();
|
||||
const String s = stream->name().toString();
|
||||
#else
|
||||
const String s(toFileName(arg));
|
||||
const String s(stream->name());
|
||||
#endif
|
||||
|
||||
String ext;
|
||||
@ -127,49 +98,163 @@ namespace
|
||||
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(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
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;
|
||||
}
|
||||
|
||||
// Detect the file type based on the actual content of the stream.
|
||||
|
||||
File *detectByContent(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
|
||||
#ifdef _WIN32
|
||||
const String s = fileName.toString();
|
||||
#else
|
||||
const String s(fileName);
|
||||
#endif
|
||||
|
||||
String ext;
|
||||
const int pos = s.rfind(".");
|
||||
if(pos != -1)
|
||||
ext = s.substr(pos + 1).upper();
|
||||
|
||||
if(ext.isEmpty())
|
||||
return 0;
|
||||
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -95,6 +95,18 @@ public:
|
||||
bool scanned;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
|
||||
|
@ -75,6 +75,19 @@ public:
|
||||
Properties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -76,6 +76,55 @@ public:
|
||||
Properties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include <xiphcomment.h>
|
||||
#include "oggflacfile.h"
|
||||
@ -65,6 +66,18 @@ public:
|
||||
int commentPacket;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "opusfile.h"
|
||||
|
||||
@ -53,6 +54,18 @@ public:
|
||||
Properties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "speexfile.h"
|
||||
|
||||
@ -53,6 +54,18 @@ public:
|
||||
Properties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -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
|
||||
@ -59,6 +59,18 @@ namespace TagLib {
|
||||
static const char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 };
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <id3v2tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "aifffile.h"
|
||||
|
||||
@ -53,6 +54,18 @@ public:
|
||||
bool hasID3v2;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -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"
|
||||
@ -60,6 +61,18 @@ public:
|
||||
bool hasInfo;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,18 @@ public:
|
||||
Properties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -71,6 +71,18 @@ public:
|
||||
Properties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 &);
|
||||
|
@ -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()
|
||||
{
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user