Switch to using QXmlStreamReader instead of SAX parser (part #1).

This commit is contained in:
LoRd_MuldeR 2017-10-31 13:00:28 +01:00
parent 87bc566b26
commit 5aff7b6547
3 changed files with 176 additions and 40 deletions

View File

@ -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
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@ -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++)
{ {

View File

@ -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;