Switch to using QXmlStreamReader instead of SAX parser (part #1).
This commit is contained in:
parent
87bc566b26
commit
5aff7b6547
@ -35,7 +35,7 @@
|
|||||||
#define VER_LAMEXP_MINOR_LO 6
|
#define VER_LAMEXP_MINOR_LO 6
|
||||||
#define VER_LAMEXP_TYPE Alpha
|
#define VER_LAMEXP_TYPE Alpha
|
||||||
#define VER_LAMEXP_PATCH 7
|
#define VER_LAMEXP_PATCH 7
|
||||||
#define VER_LAMEXP_BUILD 2037
|
#define VER_LAMEXP_BUILD 2040
|
||||||
#define VER_LAMEXP_CONFG 2002
|
#define VER_LAMEXP_CONFG 2002
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QXmlSimpleReader>
|
#include <QXmlSimpleReader>
|
||||||
#include <QXmlInputSource>
|
#include <QXmlInputSource>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
#include <QStack>
|
#include <QStack>
|
||||||
|
|
||||||
//CRT
|
//CRT
|
||||||
@ -57,7 +58,33 @@
|
|||||||
#define IS_SEC(SEC) (key.startsWith((SEC "_"), Qt::CaseInsensitive))
|
#define IS_SEC(SEC) (key.startsWith((SEC "_"), Qt::CaseInsensitive))
|
||||||
#define FIRST_TOK(STR) (STR.split(" ", QString::SkipEmptyParts).first())
|
#define FIRST_TOK(STR) (STR.split(" ", QString::SkipEmptyParts).first())
|
||||||
|
|
||||||
#define STR_EQ(A,B) ((A).compare((B), Qt::CaseInsensitive) == 0)
|
#define STRICMP(A,B) ((A).compare((B), Qt::CaseInsensitive) == 0)
|
||||||
|
|
||||||
|
#define ADD_PROPTERY_MAPPING(TYPE, NAME) do \
|
||||||
|
{ \
|
||||||
|
builder->insert(qMakePair(trackType_##TYPE, QString::fromLatin1(#NAME)), propertyId_##TYPE##_##NAME); \
|
||||||
|
} \
|
||||||
|
while(0)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Static Data
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
propertyId_gen_format,
|
||||||
|
propertyId_gen_format_profile,
|
||||||
|
propertyId_gen_duration,
|
||||||
|
propertyId_aud_format,
|
||||||
|
propertyId_aud_format_version,
|
||||||
|
propertyId_aud_format_profile,
|
||||||
|
propertyId_aud_channel_s_,
|
||||||
|
propertyId_aud_samplingrate
|
||||||
|
}
|
||||||
|
MI_propertyId_t;
|
||||||
|
|
||||||
|
static QReadWriteLock g_properties_lock;
|
||||||
|
static QScopedPointer<const QMap<QPair<AnalyzeTask::MI_trackType_t, QString>, MI_propertyId_t>> g_properties_data;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// XML Content Handler
|
// XML Content Handler
|
||||||
@ -132,6 +159,26 @@ AnalyzeTask::AnalyzeTask(const int taskId, const QString &inputFile, QAtomicInt
|
|||||||
{
|
{
|
||||||
qFatal("Invalid path to MediaInfo binary. Tool not initialized properly.");
|
qFatal("Invalid path to MediaInfo binary. Tool not initialized properly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QReadLocker rdLocker(&g_properties_lock);
|
||||||
|
if (g_properties_data.isNull())
|
||||||
|
{
|
||||||
|
rdLocker.unlock();
|
||||||
|
QWriteLocker wrLocker(&g_properties_lock);
|
||||||
|
if (g_properties_data.isNull())
|
||||||
|
{
|
||||||
|
QMap<QPair<MI_trackType_t, QString>, MI_propertyId_t> *const builder = new QMap<QPair<MI_trackType_t, QString>, MI_propertyId_t>();
|
||||||
|
ADD_PROPTERY_MAPPING(gen, format);
|
||||||
|
ADD_PROPTERY_MAPPING(gen, format_profile);
|
||||||
|
ADD_PROPTERY_MAPPING(gen, duration);
|
||||||
|
ADD_PROPTERY_MAPPING(aud, format);
|
||||||
|
ADD_PROPTERY_MAPPING(aud, format_version);
|
||||||
|
ADD_PROPTERY_MAPPING(aud, format_profile);
|
||||||
|
ADD_PROPTERY_MAPPING(aud, channel_s_);
|
||||||
|
ADD_PROPTERY_MAPPING(aud, samplingrate);
|
||||||
|
g_properties_data.reset(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnalyzeTask::~AnalyzeTask(void)
|
AnalyzeTask::~AnalyzeTask(void)
|
||||||
@ -317,7 +364,7 @@ const AudioFileModel& AnalyzeTask::analyzeMediaFile(const QString &filePath, Aud
|
|||||||
data += dataNext;
|
data += dataNext;
|
||||||
}
|
}
|
||||||
|
|
||||||
//qDebug("!!!--START-->>>\n%s\n<<<--END--!!!", data.constData());
|
qDebug("!!!--START-->>>\n%s\n<<<--END--!!!", data.constData());
|
||||||
return parseMediaInfo(data, audioFile);
|
return parseMediaInfo(data, audioFile);
|
||||||
|
|
||||||
/* if(audioFile.metaInfo().title().isEmpty())
|
/* if(audioFile.metaInfo().title().isEmpty())
|
||||||
@ -356,44 +403,106 @@ const AudioFileModel& AnalyzeTask::analyzeMediaFile(const QString &filePath, Aud
|
|||||||
|
|
||||||
const AudioFileModel& AnalyzeTask::parseMediaInfo(const QByteArray &data, AudioFileModel &audioFile)
|
const AudioFileModel& AnalyzeTask::parseMediaInfo(const QByteArray &data, AudioFileModel &audioFile)
|
||||||
{
|
{
|
||||||
QXmlInputSource xmlSource;
|
QMap<QString, MI_trackType_t> trackTypes;
|
||||||
xmlSource.setData(data);
|
trackTypes.insert("general", trackType_gen);
|
||||||
|
trackTypes.insert("audio", trackType_aud);
|
||||||
|
|
||||||
QScopedPointer<QXmlDefaultHandler> xmlHandler(new AnalyzeTask_XmlHandler(audioFile, m_mediaInfoVer));
|
QXmlStreamReader xmlStream(data);
|
||||||
|
bool firstFile = true;
|
||||||
|
QSet<MI_trackType_t> tracksFound;
|
||||||
|
|
||||||
QXmlSimpleReader xmlReader;
|
if (findNextElement(QLatin1String("MediaInfo"), xmlStream))
|
||||||
xmlReader.setContentHandler(xmlHandler.data());
|
|
||||||
xmlReader.setErrorHandler(xmlHandler.data());
|
|
||||||
|
|
||||||
if (xmlReader.parse(xmlSource))
|
|
||||||
{
|
{
|
||||||
while (xmlReader.parseContinue()) {/*continue*/}
|
const QStringRef version = xmlStream.attributes().value(QLatin1String("version"));
|
||||||
|
if (version.isEmpty() || (!STRICMP(version, QString().sprintf("0.%u.%02u", m_mediaInfoVer / 100U, m_mediaInfoVer % 100))))
|
||||||
|
{
|
||||||
|
qWarning("Invalid version property \"%s\" was detected!", MUTILS_UTF8(version));
|
||||||
|
return audioFile;
|
||||||
|
}
|
||||||
|
while (findNextElement(QLatin1String("File"), xmlStream))
|
||||||
|
{
|
||||||
|
if (firstFile)
|
||||||
|
{
|
||||||
|
firstFile = false;
|
||||||
|
while (findNextElement(QLatin1String("Track"), xmlStream))
|
||||||
|
{
|
||||||
|
const QString typeAttr = xmlStream.attributes().value(QLatin1String("type")).toString().simplified().toLower();
|
||||||
|
const MI_trackType_t trackType = trackTypes.value(typeAttr, MI_trackType_t(-1));
|
||||||
|
if (trackType != MI_trackType_t(-1))
|
||||||
|
{
|
||||||
|
if (!tracksFound.contains(trackType))
|
||||||
|
{
|
||||||
|
tracksFound << trackType;
|
||||||
|
parseTrackInfo(xmlStream, trackType, audioFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning("Skipping non-primary '%s' track!", MUTILS_UTF8(typeAttr));
|
||||||
|
xmlStream.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning("Skipping non-primary file!");
|
||||||
|
xmlStream.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(audioFile.metaInfo().title().isEmpty())
|
if (!(audioFile.techInfo().containerType().isEmpty() || audioFile.techInfo().audioType().isEmpty()))
|
||||||
{
|
{
|
||||||
QString baseName = QFileInfo(audioFile.filePath()).fileName();
|
if (audioFile.metaInfo().title().isEmpty())
|
||||||
int index;
|
|
||||||
if((index = baseName.lastIndexOf("."))>= 0)
|
|
||||||
{
|
{
|
||||||
baseName = baseName.left(index);
|
QString baseName = QFileInfo(audioFile.filePath()).fileName();
|
||||||
|
int index;
|
||||||
|
if ((index = baseName.lastIndexOf(".")) >= 0)
|
||||||
|
{
|
||||||
|
baseName = baseName.left(index);
|
||||||
|
}
|
||||||
|
baseName = baseName.replace("_", " ").simplified();
|
||||||
|
if ((index = baseName.lastIndexOf(" - ")) >= 0)
|
||||||
|
{
|
||||||
|
baseName = baseName.mid(index + 3).trimmed();
|
||||||
|
}
|
||||||
|
audioFile.metaInfo().setTitle(baseName);
|
||||||
}
|
}
|
||||||
baseName = baseName.replace("_", " ").simplified();
|
if ((audioFile.techInfo().audioType().compare("PCM", Qt::CaseInsensitive) == 0) && (audioFile.techInfo().audioProfile().compare("Float", Qt::CaseInsensitive) == 0))
|
||||||
if((index = baseName.lastIndexOf(" - ")) >= 0)
|
|
||||||
{
|
{
|
||||||
baseName = baseName.mid(index + 3).trimmed();
|
if (audioFile.techInfo().audioBitdepth() == 32) audioFile.techInfo().setAudioBitdepth(AudioFileModel::BITDEPTH_IEEE_FLOAT32);
|
||||||
}
|
}
|
||||||
audioFile.metaInfo().setTitle(baseName);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if ((audioFile.techInfo().audioType().compare("PCM", Qt::CaseInsensitive) == 0) && (audioFile.techInfo().audioProfile().compare("Float", Qt::CaseInsensitive) == 0))
|
|
||||||
{
|
{
|
||||||
if (audioFile.techInfo().audioBitdepth() == 32) audioFile.techInfo().setAudioBitdepth(AudioFileModel::BITDEPTH_IEEE_FLOAT32);
|
qWarning("Audio file format could *not* be recognized!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return audioFile;
|
return audioFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnalyzeTask::parseTrackInfo(QXmlStreamReader &xmlStream, const MI_trackType_t trackType, AudioFileModel &audioFile)
|
||||||
|
{
|
||||||
|
while (xmlStream.readNextStartElement())
|
||||||
|
{
|
||||||
|
qWarning("%d::%s", trackType, MUTILS_UTF8(xmlStream.name()));
|
||||||
|
const MI_propertyId_t idx = g_properties_data->value(qMakePair(trackType, xmlStream.name().toString().simplified().toLower()), MI_propertyId_t(-1));
|
||||||
|
if (idx != MI_propertyId_t(-1))
|
||||||
|
{
|
||||||
|
const QString value = xmlStream.readElementText(QXmlStreamReader::SkipChildElements).simplified();
|
||||||
|
if (!value.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning("--> %d: \"%s\"", idx, MUTILS_UTF8(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xmlStream.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
bool AnalyzeTask::checkFile_CDDA(QFile &file)
|
bool AnalyzeTask::checkFile_CDDA(QFile &file)
|
||||||
{
|
{
|
||||||
file.reset();
|
file.reset();
|
||||||
@ -544,12 +653,16 @@ bool AnalyzeTask::analyzeAvisynthFile(const QString &filePath, AudioFileModel &i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int AnalyzeTask::parseYear(const QString &str)
|
// ---------------------------------------------------------
|
||||||
|
// Utility Functions
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
quint32 AnalyzeTask::parseYear(const QString &str)
|
||||||
{
|
{
|
||||||
if(str.startsWith("UTC", Qt::CaseInsensitive))
|
if (str.startsWith("UTC", Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
QDate date = QDate::fromString(str.mid(3).trimmed().left(10), "yyyy-MM-dd");
|
QDate date = QDate::fromString(str.mid(3).trimmed().left(10), "yyyy-MM-dd");
|
||||||
if(date.isValid())
|
if (date.isValid())
|
||||||
{
|
{
|
||||||
return date.year();
|
return date.year();
|
||||||
}
|
}
|
||||||
@ -562,7 +675,7 @@ unsigned int AnalyzeTask::parseYear(const QString &str)
|
|||||||
{
|
{
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
int year = str.toInt(&ok);
|
int year = str.toInt(&ok);
|
||||||
if(ok && year > 0)
|
if (ok && year > 0)
|
||||||
{
|
{
|
||||||
return year;
|
return year;
|
||||||
}
|
}
|
||||||
@ -573,6 +686,19 @@ unsigned int AnalyzeTask::parseYear(const QString &str)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AnalyzeTask::findNextElement(const QString &name, QXmlStreamReader &xmlStream)
|
||||||
|
{
|
||||||
|
while (xmlStream.readNextStartElement())
|
||||||
|
{
|
||||||
|
if (STRICMP(xmlStream.name(), name))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
xmlStream.skipCurrentElement();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// XML Content Handler Implementation
|
// XML Content Handler Implementation
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
@ -627,26 +753,26 @@ bool AnalyzeTask_XmlHandler::startElement(const QString &namespaceURI, const QSt
|
|||||||
switch (m_stack.size())
|
switch (m_stack.size())
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
if (!STR_EQ(qName, "mediaInfo"))
|
if (!STRICMP(qName, "mediaInfo"))
|
||||||
{
|
{
|
||||||
qWarning("Invalid XML structure was detected! (1)");
|
qWarning("Invalid XML structure was detected! (1)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!STR_EQ(atts.value("version"), QString().sprintf("0.%u.%02u", m_version / 100U, m_version % 100)))
|
if (!STRICMP(atts.value("version"), QString().sprintf("0.%u.%02u", m_version / 100U, m_version % 100)))
|
||||||
{
|
{
|
||||||
qWarning("Invalid version property was detected!");
|
qWarning("Invalid version property was detected!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case 2:
|
case 2:
|
||||||
if (!STR_EQ(qName, "file"))
|
if (!STRICMP(qName, "file"))
|
||||||
{
|
{
|
||||||
qWarning("Invalid XML structure was detected! (2)");
|
qWarning("Invalid XML structure was detected! (2)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case 3:
|
case 3:
|
||||||
if (!STR_EQ(qName, "track"))
|
if (!STRICMP(qName, "track"))
|
||||||
{
|
{
|
||||||
qWarning("Invalid XML structure was detected! (3)");
|
qWarning("Invalid XML structure was detected! (3)");
|
||||||
return false;
|
return false;
|
||||||
@ -654,11 +780,11 @@ bool AnalyzeTask_XmlHandler::startElement(const QString &namespaceURI, const QSt
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
const QString value = atts.value("type").trimmed();
|
const QString value = atts.value("type").trimmed();
|
||||||
if (STR_EQ(value, "general"))
|
if (STRICMP(value, "general"))
|
||||||
{
|
{
|
||||||
m_trackType = trackType_gen;
|
m_trackType = trackType_gen;
|
||||||
}
|
}
|
||||||
else if (STR_EQ(value, "audio"))
|
else if (STRICMP(value, "audio"))
|
||||||
{
|
{
|
||||||
if (m_trackIdx++)
|
if (m_trackIdx++)
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,7 @@ class AudioFileModel;
|
|||||||
class QFile;
|
class QFile;
|
||||||
class QDir;
|
class QDir;
|
||||||
class QFileInfo;
|
class QFileInfo;
|
||||||
class LockedFile;
|
class QXmlStreamReader;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Splash Thread
|
// Splash Thread
|
||||||
@ -48,14 +48,23 @@ public:
|
|||||||
AnalyzeTask(const int taskId, const QString &inputFile, QAtomicInt &abortFlag);
|
AnalyzeTask(const int taskId, const QString &inputFile, QAtomicInt &abortFlag);
|
||||||
~AnalyzeTask(void);
|
~AnalyzeTask(void);
|
||||||
|
|
||||||
enum fileType_t
|
typedef enum
|
||||||
{
|
{
|
||||||
fileTypeNormal = 0,
|
fileTypeNormal = 0,
|
||||||
fileTypeCDDA = 1,
|
fileTypeCDDA = 1,
|
||||||
fileTypeDenied = 2,
|
fileTypeDenied = 2,
|
||||||
fileTypeCueSheet = 3,
|
fileTypeCueSheet = 3,
|
||||||
fileTypeUnknown = 4
|
fileTypeUnknown = 4
|
||||||
};
|
}
|
||||||
|
fileType_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
trackType_non = 0,
|
||||||
|
trackType_gen = 1,
|
||||||
|
trackType_aud = 2,
|
||||||
|
}
|
||||||
|
MI_trackType_t;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileAnalyzed(const unsigned int taskId, const int fileType, const AudioFileModel &file);
|
void fileAnalyzed(const unsigned int taskId, const int fileType, const AudioFileModel &file);
|
||||||
@ -69,14 +78,15 @@ private:
|
|||||||
const AudioFileModel& analyzeFile(const QString &filePath, AudioFileModel &audioFile, int *const type);
|
const AudioFileModel& analyzeFile(const QString &filePath, AudioFileModel &audioFile, int *const type);
|
||||||
const AudioFileModel& analyzeMediaFile(const QString &filePath, AudioFileModel &audioFile);
|
const AudioFileModel& analyzeMediaFile(const QString &filePath, AudioFileModel &audioFile);
|
||||||
const AudioFileModel& parseMediaInfo(const QByteArray &data, AudioFileModel &audioFile);
|
const AudioFileModel& parseMediaInfo(const QByteArray &data, AudioFileModel &audioFile);
|
||||||
//void updateInfo(AudioFileModel &audioFile, bool &skipNext, QPair<quint32, quint32> &id_val, quint32 &coverType, QByteArray &coverData, const QString &key, const QString &value);
|
void parseTrackInfo(QXmlStreamReader &xmlStream, const MI_trackType_t trackType, AudioFileModel &audioFile);
|
||||||
unsigned int parseYear(const QString &str);
|
|
||||||
bool checkFile_CDDA(QFile &file);
|
bool checkFile_CDDA(QFile &file);
|
||||||
void retrieveCover(AudioFileModel &audioFile, const quint32 coverType, const QByteArray &coverData);
|
void retrieveCover(AudioFileModel &audioFile, const quint32 coverType, const QByteArray &coverData);
|
||||||
bool analyzeAvisynthFile(const QString &filePath, AudioFileModel &info);
|
bool analyzeAvisynthFile(const QString &filePath, AudioFileModel &info);
|
||||||
|
|
||||||
const unsigned int m_taskId;
|
static quint32 parseYear(const QString &str);
|
||||||
|
static bool findNextElement(const QString &name, QXmlStreamReader &xmlStream);
|
||||||
|
|
||||||
|
const unsigned int m_taskId;
|
||||||
const QString m_mediaInfoBin;
|
const QString m_mediaInfoBin;
|
||||||
const quint32 m_mediaInfoVer;
|
const quint32 m_mediaInfoVer;
|
||||||
const QString m_avs2wavBin;
|
const QString m_avs2wavBin;
|
||||||
|
Loading…
Reference in New Issue
Block a user