From 9b01f44804597719de3a71b520745fda6e4df820 Mon Sep 17 00:00:00 2001 From: LoRd_MuldeR Date: Sat, 4 Nov 2017 18:23:10 +0100 Subject: [PATCH] Adapted MediaInfo parsing code for new MediaInfo XML output. aka "...when MediaInfo changes its XML output format *one* day after we implemented parsing of XML-based MediaInfo output" ;-) --- src/Config.h | 2 +- src/Thread_FileAnalyzer_Task.cpp | 262 +++++++++++++++---------------- src/Thread_FileAnalyzer_Task.h | 13 +- 3 files changed, 132 insertions(+), 145 deletions(-) diff --git a/src/Config.h b/src/Config.h index c46718bf..4c512e0e 100644 --- a/src/Config.h +++ b/src/Config.h @@ -35,7 +35,7 @@ #define VER_LAMEXP_MINOR_LO 6 #define VER_LAMEXP_TYPE Alpha #define VER_LAMEXP_PATCH 8 -#define VER_LAMEXP_BUILD 2048 +#define VER_LAMEXP_BUILD 2050 #define VER_LAMEXP_CONFG 2002 /////////////////////////////////////////////////////////////////////////////// diff --git a/src/Thread_FileAnalyzer_Task.cpp b/src/Thread_FileAnalyzer_Task.cpp index b4cb41fd..e099d61c 100644 --- a/src/Thread_FileAnalyzer_Task.cpp +++ b/src/Thread_FileAnalyzer_Task.cpp @@ -31,6 +31,7 @@ //MUtils #include #include +#include #include //Qt @@ -66,7 +67,7 @@ while(0) #define ADD_PROPTERY_MAPPING_2(TYPE, MI_NAME, LX_NAME) do \ { \ - builder->insert(qMakePair(trackType_##TYPE, QString::fromLatin1(#MI_NAME)), propertyId_##LX_NAME); \ + builder->insert(qMakePair(AnalyzeTask::trackType_##TYPE, QString::fromLatin1(#MI_NAME)), AnalyzeTask::propertyId_##LX_NAME); \ } \ while(0) @@ -80,6 +81,85 @@ while(0) #define STRICMP(A,B) ((A).compare((B), Qt::CaseInsensitive) == 0) +//////////////////////////////////////////////////////////// +// Static initialization +//////////////////////////////////////////////////////////// + +class AnalyzeTask_StaticInit_MediaInfoIdx : public MUtils::Lazy, AnalyzeTask::MI_propertyId_t>> +{ + virtual QMap, AnalyzeTask::MI_propertyId_t> *create() + { + QMap, AnalyzeTask::MI_propertyId_t> *const builder = new QMap, AnalyzeTask::MI_propertyId_t>(); + ADD_PROPTERY_MAPPING_2(gen, format, container); + ADD_PROPTERY_MAPPING_2(gen, format_profile, container_profile); + ADD_PROPTERY_MAPPING_1(gen, duration); + ADD_PROPTERY_MAPPING_1(gen, title); + ADD_PROPTERY_MAPPING_2(gen, track, title); + ADD_PROPTERY_MAPPING_1(gen, artist); + ADD_PROPTERY_MAPPING_2(gen, performer, artist); + ADD_PROPTERY_MAPPING_1(gen, album); + ADD_PROPTERY_MAPPING_1(gen, genre); + ADD_PROPTERY_MAPPING_1(gen, released_date); + ADD_PROPTERY_MAPPING_2(gen, recorded_date, released_date); + ADD_PROPTERY_MAPPING_1(gen, track_position); + ADD_PROPTERY_MAPPING_1(gen, comment); + ADD_PROPTERY_MAPPING_1(aud, format); + ADD_PROPTERY_MAPPING_1(aud, format_version); + ADD_PROPTERY_MAPPING_1(aud, format_profile); + ADD_PROPTERY_MAPPING_1(aud, duration); + ADD_PROPTERY_MAPPING_1(aud, channel_s_); + ADD_PROPTERY_MAPPING_1(aud, samplingrate); + ADD_PROPTERY_MAPPING_1(aud, bitdepth); + ADD_PROPTERY_MAPPING_1(aud, bitrate); + ADD_PROPTERY_MAPPING_1(aud, bitrate_mode); + ADD_PROPTERY_MAPPING_1(aud, encoded_library); + ADD_PROPTERY_MAPPING_2(gen, cover_mime, cover_mime); + ADD_PROPTERY_MAPPING_2(gen, cover_data, cover_data); + return builder; + } +} +s_mediaInfoIdx; + +class AnalyzeTask_StaticInit_AvisynthIdx : public MUtils::Lazy> +{ + virtual QMap *create() + { + QMap *const builder = new QMap(); + builder->insert(QLatin1String("totalseconds"), AnalyzeTask::propertyId_duration); + builder->insert(QLatin1String("samplespersec"), AnalyzeTask::propertyId_samplingrate); + builder->insert(QLatin1String("channels"), AnalyzeTask::propertyId_channel_s_); + builder->insert(QLatin1String("bitspersample"), AnalyzeTask::propertyId_bitdepth); + return builder; + } +} +s_avisynthIdx; + +class AnalyzeTask_StaticInit_MimiTypes : public MUtils::Lazy> +{ + virtual QMap *create() + { + QMap *const builder = new QMap(); + for (size_t i = 0U; MIME_TYPES[i].type; ++i) + { + builder->insert(QString::fromLatin1(MIME_TYPES[i].type), QString::fromLatin1(MIME_TYPES[i].ext[0])); + } + return builder; + } +} +s_mimeTypes; + +class AnalyzeTask_StaticInit_TrackTypes : public MUtils::Lazy> +{ + virtual QMap *create() + { + QMap *const builder = new QMap(); + builder->insert("general", AnalyzeTask::trackType_gen); + builder->insert("audio", AnalyzeTask::trackType_aud); + return builder; + } +} +s_trackTypes; + //////////////////////////////////////////////////////////// // Constructor //////////////////////////////////////////////////////////// @@ -92,10 +172,10 @@ AnalyzeTask::AnalyzeTask(const int taskId, const QString &inputFile, QAtomicInt m_mediaInfoVer(lamexp_tools_version("mediainfo.exe")), m_avs2wavBin(lamexp_tools_lookup("avs2wav.exe")), m_abortFlag(abortFlag), - m_mediaInfoIdx(initMediaInfoIdx()), - m_avisynthIdx(initAvisynthIdx()), - m_mimeTypes(initMimeTypes()), - m_trackTypes(initTrackTypes()) + m_mediaInfoIdx(*s_mediaInfoIdx), + m_avisynthIdx(*s_avisynthIdx), + m_mimeTypes(*s_mimeTypes), + m_trackTypes(*s_trackTypes) { if(m_mediaInfoBin.isEmpty() || m_avs2wavBin.isEmpty()) { @@ -108,122 +188,6 @@ AnalyzeTask::~AnalyzeTask(void) emit taskCompleted(m_taskId); } -//////////////////////////////////////////////////////////// -// Static initialization -//////////////////////////////////////////////////////////// - -QReadWriteLock AnalyzeTask::s_lock; -QScopedPointer, AnalyzeTask::MI_propertyId_t>> AnalyzeTask::s_pMediaInfoIdx; -QScopedPointer> AnalyzeTask::s_pAvisynthIdx; -QScopedPointer> AnalyzeTask::s_pMimeTypes; -QScopedPointer> AnalyzeTask::s_pTrackTypes; - -const QMap, AnalyzeTask::MI_propertyId_t> &AnalyzeTask::initMediaInfoIdx(void) -{ - QReadLocker rdLocker(&s_lock); - if (s_pMediaInfoIdx.isNull()) - { - rdLocker.unlock(); - QWriteLocker wrLocker(&s_lock); - if (s_pMediaInfoIdx.isNull()) - { - QMap, MI_propertyId_t> *const builder = new QMap, MI_propertyId_t>(); - ADD_PROPTERY_MAPPING_2(gen, format, container); - ADD_PROPTERY_MAPPING_2(gen, format_profile, container_profile); - ADD_PROPTERY_MAPPING_1(gen, duration); - ADD_PROPTERY_MAPPING_1(gen, title); - ADD_PROPTERY_MAPPING_2(gen, track, title); - ADD_PROPTERY_MAPPING_1(gen, artist); - ADD_PROPTERY_MAPPING_2(gen, performer, artist); - ADD_PROPTERY_MAPPING_1(gen, album); - ADD_PROPTERY_MAPPING_1(gen, genre); - ADD_PROPTERY_MAPPING_1(gen, released_date); - ADD_PROPTERY_MAPPING_2(gen, recorded_date, released_date); - ADD_PROPTERY_MAPPING_1(gen, track_position); - ADD_PROPTERY_MAPPING_1(gen, comment); - ADD_PROPTERY_MAPPING_1(aud, format); - ADD_PROPTERY_MAPPING_1(aud, format_version); - ADD_PROPTERY_MAPPING_1(aud, format_profile); - ADD_PROPTERY_MAPPING_1(aud, duration); - ADD_PROPTERY_MAPPING_1(aud, channel_s_); - ADD_PROPTERY_MAPPING_1(aud, samplingrate); - ADD_PROPTERY_MAPPING_1(aud, bitdepth); - ADD_PROPTERY_MAPPING_1(aud, bitrate); - ADD_PROPTERY_MAPPING_1(aud, bitrate_mode); - ADD_PROPTERY_MAPPING_1(aud, encoded_library); - ADD_PROPTERY_MAPPING_2(gen, cover_mime, cover_mime); - ADD_PROPTERY_MAPPING_2(gen, cover_data, cover_data); - s_pMediaInfoIdx.reset(builder); - } - wrLocker.unlock(); - rdLocker.relock(); - } - return (*s_pMediaInfoIdx); -} - -const QMap &AnalyzeTask::initAvisynthIdx(void) -{ - QReadLocker rdLocker(&s_lock); - if (s_pAvisynthIdx.isNull()) - { - rdLocker.unlock(); - QWriteLocker wrLocker(&s_lock); - if (s_pAvisynthIdx.isNull()) - { - QMap *const builder = new QMap(); - builder->insert(QLatin1String("totalseconds"), propertyId_duration); - builder->insert(QLatin1String("samplespersec"), propertyId_samplingrate); - builder->insert(QLatin1String("channels"), propertyId_channel_s_); - builder->insert(QLatin1String("bitspersample"), propertyId_bitdepth); - s_pAvisynthIdx.reset(builder); - } - wrLocker.unlock(); - rdLocker.relock(); - } - return (*s_pAvisynthIdx); -} -const QMap &AnalyzeTask::initTrackTypes(void) -{ - QReadLocker rdLocker(&s_lock); - if (s_pTrackTypes.isNull()) - { - rdLocker.unlock(); - QWriteLocker wrLocker(&s_lock); - if (s_pTrackTypes.isNull()) - { - QMap *const builder = new QMap(); - builder->insert("general", trackType_gen); - builder->insert("audio", trackType_aud); - s_pTrackTypes.reset(builder); - } - wrLocker.unlock(); - rdLocker.relock(); - } - return (*s_pTrackTypes); -} - -const QMap &AnalyzeTask::initMimeTypes(void) -{ - QReadLocker rdLocker(&s_lock); - if (s_pMimeTypes.isNull()) - { - rdLocker.unlock(); - QWriteLocker wrLocker(&s_lock); - if (s_pMimeTypes.isNull()) - { - QMap *const builder = new QMap(); - for (size_t i = 0U; MIME_TYPES[i].type; ++i) - { - builder->insert(QString::fromLatin1(MIME_TYPES[i].type), QString::fromLatin1(MIME_TYPES[i].ext[0])); - } - s_pMimeTypes.reset(builder); - } - wrLocker.unlock(); - rdLocker.relock(); - } - return (*s_pMimeTypes); -} - //////////////////////////////////////////////////////////// // Thread Main //////////////////////////////////////////////////////////// @@ -412,27 +376,43 @@ const AudioFileModel& AnalyzeTask::analyzeMediaFile(const QString &filePath, Aud const AudioFileModel& AnalyzeTask::parseMediaInfo(const QByteArray &data, AudioFileModel &audioFile) { QXmlStreamReader xmlStream(data); - bool firstFile = true; + bool firstMediaFile = true; if (findNextElement(QLatin1String("MediaInfo"), xmlStream)) { - const QString version = findAttribute(QLatin1String("Version"), xmlStream.attributes()); - if (version.isEmpty() || (!STRICMP(version, QString().sprintf("0.%u.%02u", m_mediaInfoVer / 100U, m_mediaInfoVer % 100)))) + const QString versionXml = findAttribute(QLatin1String("Version"), xmlStream.attributes()); + if (versionXml.isEmpty() || (!checkVersionStr(versionXml, 2U, 0U))) { - qWarning("Invalid version property \"%s\" was detected!", MUTILS_UTF8(version)); + qWarning("Invalid file format version property: \"%s\"", MUTILS_UTF8(versionXml)); return audioFile; } - while (findNextElement(QLatin1String("File"), xmlStream)) + if (findNextElement(QLatin1String("CreatingLibrary"), xmlStream)) { - if (firstFile) + const QString versionLib = findAttribute(QLatin1String("Version"), xmlStream.attributes()); + const QString identifier = xmlStream.readElementText(QXmlStreamReader::SkipChildElements).simplified(); + if (!STRICMP(identifier, QLatin1String("MediaInfoLib"))) { - firstFile = false; - parseFileInfo(xmlStream, audioFile); + qWarning("Invalid library identiofier property: \"%s\"", MUTILS_UTF8(identifier)); + return audioFile; } - else + if (versionLib.isEmpty() || (!checkVersionStr(versionLib, m_mediaInfoVer / 100U, m_mediaInfoVer % 100U))) { - qWarning("Skipping non-primary file!"); - xmlStream.skipCurrentElement(); + qWarning("Invalid library version property: \"%s\"", MUTILS_UTF8(versionLib)); + return audioFile; + } + while (findNextElement(QLatin1String("Media"), xmlStream)) + { + qWarning("Found a media!"); + if (firstMediaFile || audioFile.techInfo().containerType().isEmpty() || audioFile.techInfo().audioType().isEmpty()) + { + firstMediaFile = false; + parseFileInfo(xmlStream, audioFile); + } + else + { + qWarning("Skipping non-primary file!"); + xmlStream.skipCurrentElement(); + } } } } @@ -473,6 +453,7 @@ void AnalyzeTask::parseFileInfo(QXmlStreamReader &xmlStream, AudioFileModel &aud MI_trackType_t trackType; while (findNextElement(QLatin1String("Track"), xmlStream)) { + qWarning("Found a track!"); const QString typeString = findAttribute(QLatin1String("Type"), xmlStream.attributes()); if ((trackType = m_trackTypes.value(typeString.toLower(), MI_trackType_t(-1))) != MI_trackType_t(-1)) { @@ -785,6 +766,23 @@ QString AnalyzeTask::findAttribute(const QString &name, const QXmlStreamAttribut return QString(); } +bool AnalyzeTask::checkVersionStr(const QString &str, const quint32 expectedMajor, const quint32 expectedMinor) +{ + QRegExp version("^(\\d+)\\.(\\d+)($|\\.)"); + if (version.indexIn(str) >= 0) + { + quint32 actual[2]; + if (MUtils::regexp_parse_uint32(version, actual, 2)) + { + if ((actual[0] == expectedMajor) && (actual[1] >= expectedMinor)) + { + return true; + } + } + } + return false; +} + //////////////////////////////////////////////////////////// // Public Functions //////////////////////////////////////////////////////////// diff --git a/src/Thread_FileAnalyzer_Task.h b/src/Thread_FileAnalyzer_Task.h index 9b30a63a..e4a65729 100644 --- a/src/Thread_FileAnalyzer_Task.h +++ b/src/Thread_FileAnalyzer_Task.h @@ -57,7 +57,6 @@ public: } fileType_t; -protected: typedef enum { trackType_non = 0, @@ -111,11 +110,6 @@ private: bool checkFile_CDDA(QFile &file); bool analyzeAvisynthFile(const QString &filePath, AudioFileModel &info); - static const QMap, MI_propertyId_t> &initMediaInfoIdx(void); - static const QMap &initAvisynthIdx(void); - static const QMap &initMimeTypes(void); - static const QMap &initTrackTypes(void); - static QString decodeStr(const QString &str, const QString &encoding); static bool parseUnsigned(const QString &str, quint32 &value); static bool parseDuration(const QString &str, quint32 &value); @@ -124,6 +118,7 @@ private: static QString cleanAsciiStr(const QString &str); static bool findNextElement(const QString &name, QXmlStreamReader &xmlStream); static QString findAttribute(const QString &name, const QXmlStreamAttributes &xmlAttributes); + static bool checkVersionStr(const QString &str, const quint32 expectedMajor, const quint32 expectedMinor); const QMap, MI_propertyId_t> &m_mediaInfoIdx; const QMap &m_avisynthIdx; @@ -137,10 +132,4 @@ private: const QString m_inputFile; QAtomicInt &m_abortFlag; - - static QReadWriteLock s_lock; - static QScopedPointer, MI_propertyId_t>> s_pMediaInfoIdx; - static QScopedPointer> s_pAvisynthIdx; - static QScopedPointer> s_pMimeTypes; - static QScopedPointer> s_pTrackTypes; };