From df09346b20bf24f443257894660f06c2d9f1ff2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Martinez?= Date: Tue, 27 Jan 2026 23:03:02 +0100 Subject: [PATCH 1/2] + ID3v2: support of Olympus voice recorder metadata --- Source/MediaInfo/Tag/File_Id3v2.cpp | 29 +++++++++++++++++++++++++++++ Source/MediaInfo/Tag/File_Id3v2.h | 1 + 2 files changed, 30 insertions(+) diff --git a/Source/MediaInfo/Tag/File_Id3v2.cpp b/Source/MediaInfo/Tag/File_Id3v2.cpp index f50447f707..13847bdd86 100644 --- a/Source/MediaInfo/Tag/File_Id3v2.cpp +++ b/Source/MediaInfo/Tag/File_Id3v2.cpp @@ -231,6 +231,7 @@ namespace Elements const int32u WPUB=0x57505542; const int32u WXXX=0x57585858; const int32u XRVA=0x58525641; + const int32u XOLY=0x584F4C59; const int32u BUF=0x425546; const int32u CNT=0x434E56; const int32u COM=0x434F4D; @@ -709,6 +710,7 @@ void File_Id3v2::Data_Parse() CASE_INFO(WPAY, "Payment"); CASE_INFO(WPUB, "Publishers official webpage"); CASE_INFO(WXXX, "User defined URL link frame"); + CASE_INFO(XOLY, "Olympus"); CASE_INFO(XRVA, "Relative volume adjustment (2)"); CASE_INFO(BUF, "Recommended buffer size"); CASE_INFO(CNT, "Play counter"); @@ -1156,6 +1158,33 @@ void File_Id3v2::WXXX() Fill_Name(); } +//--------------------------------------------------------------------------- +void File_Id3v2::XOLY() +{ + string Model; + Skip_C4( "Format?"); + Skip_L2( "(Unknown)"); + Skip_L2( "(Unknown)"); + Skip_L2( "(Unknown)"); + Skip_L2( "(Unknown)"); + Get_String(16, Model, "Model"); + Skip_L2( "(Unknown)"); + Skip_L2( "(Unknown)"); + Skip_L2( "(Unknown)"); + Skip_L2( "(Unknown)"); + Skip_L2( "(Unknown)"); + Skip_String(12, "Start time"); + Skip_String(12, "End time"); + Skip_String( 6, "Duration"); + Skip_XX(Element_Size-Element_Offset, "(Unknown)"); + + FILLING_BEGIN(); + Fill(Stream_General, 0, General_Encoded_Hardware_CompanyName, "Olympus"); + Fill(Stream_General, 0, General_Encoded_Hardware_Model, Model); + Fill(Stream_Audio, 0, Audio_Duration, "10000"); + FILLING_END(); +} + //*************************************************************************** // Helpers //*************************************************************************** diff --git a/Source/MediaInfo/Tag/File_Id3v2.h b/Source/MediaInfo/Tag/File_Id3v2.h index 7ed6e21a45..decea9cb4b 100644 --- a/Source/MediaInfo/Tag/File_Id3v2.h +++ b/Source/MediaInfo/Tag/File_Id3v2.h @@ -150,6 +150,7 @@ private : void WPAY() {W___();} void WPUB() {W___();} void WXXX(); + void XOLY(); void BUF() {Skip_XX(Element_Size, "Data");} void CNT() {Skip_XX(Element_Size, "Data");} void COM() {COMM();} From fb28dea6dd01a9901b90bebbdcb98d2db931cca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Martinez?= Date: Tue, 27 Jan 2026 23:03:34 +0100 Subject: [PATCH 2/2] + MPEG Audio: compare duration with Olympus voice recorder metadata --- Source/MediaInfo/Audio/File_Mpega.cpp | 7 ++++- Source/MediaInfo/File__Analyze.cpp | 28 +++++++++++++++++++ Source/MediaInfo/File__Analyze.h | 2 ++ Source/MediaInfo/File__Analyze_MinimizeSize.h | 2 ++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Source/MediaInfo/Audio/File_Mpega.cpp b/Source/MediaInfo/Audio/File_Mpega.cpp index 719a5cb92a..ec552a9d33 100644 --- a/Source/MediaInfo/Audio/File_Mpega.cpp +++ b/Source/MediaInfo/Audio/File_Mpega.cpp @@ -526,7 +526,12 @@ void File_Mpega::Streams_Finish() if (FrameInfo.PTS!=(int64u)-1 && FrameInfo.PTS>PTS_Begin) { - Fill(Stream_Audio, 0, Audio_Duration, float64_int64s(((float64)(FrameInfo.PTS-PTS_Begin))/1000000)); + auto Duration = ((float64)(FrameInfo.PTS - PTS_Begin)) / 1000000; + const auto& ExpectedDurationS = Retrieve_Const(Stream_Audio, 0, Audio_Duration); + if (!ExpectedDurationS.empty()) { + DurationIssue(float64_int64s(Duration), ExpectedDurationS.To_int64u(), false, "MPEG-Audio"); + } + Fill(Stream_Audio, 0, Audio_Duration, Duration, 0, true); if (Retrieve(Stream_Audio, 0, Audio_BitRate_Mode)==__T("CBR") && ID<4 && sampling_frequency<4) { int16u Samples; diff --git a/Source/MediaInfo/File__Analyze.cpp b/Source/MediaInfo/File__Analyze.cpp index 5185494c1d..b625e1716c 100644 --- a/Source/MediaInfo/File__Analyze.cpp +++ b/Source/MediaInfo/File__Analyze.cpp @@ -4530,6 +4530,34 @@ void File__Analyze::IsTruncated(int64u ExpectedSize, bool MoreThan, const char* #endif //MEDIAINFO_CONFORMANCE +//--------------------------------------------------------------------------- +#if MEDIAINFO_CONFORMANCE +void File__Analyze::DurationIssue(int64u ActualDuration, int64u ExpectedDuration, bool MoreThan, const char* Prefix) +{ + auto ExpectedDuration_Min = ExpectedDuration; + auto ExpectedDurationI = ((int64u)(ExpectedDuration / 1000)) * 1000; + float64 Multiplier; + if (ExpectedDuration - ExpectedDurationI == 0) { // If precision is second only, we tolerate +/- 1s + ExpectedDuration_Min -= 1000; + Multiplier = 1000; + } + else { + ExpectedDuration_Min += 1; + Multiplier = 1; + } + if (ActualDuration < ExpectedDuration_Min) { + auto ActualTC = TimeCode((double)ActualDuration / 1000, 999, TimeCode::Timed()).ToString(); + auto ForPercentage = to_string_with_percent(ActualDuration, ExpectedDuration, false); + auto ForPercentage_CutPos = ForPercentage.find(' '); + if (ForPercentage_CutPos != string::npos) { + ActualTC += ForPercentage.substr(ForPercentage_CutPos); + } + auto ExpectedTC = TimeCode((double)ExpectedDuration / 1000, (Multiplier - 1) ? 0 : 999, TimeCode::Timed()).ToString(); + Fill_Conformance(BuildConformanceName(ParserName, Prefix ? Prefix : "", "Duration").c_str(), "Duration is less than expected duration (actual " + ActualTC + ", expected " + ExpectedTC + ')'); + } +} +#endif //MEDIAINFO_CONFORMANCE + //--------------------------------------------------------------------------- #if MEDIAINFO_CONFORMANCE void File__Analyze::RanOutOfData(const char* Prefix) diff --git a/Source/MediaInfo/File__Analyze.h b/Source/MediaInfo/File__Analyze.h index 0831119fc4..f9c47d83f5 100644 --- a/Source/MediaInfo/File__Analyze.h +++ b/Source/MediaInfo/File__Analyze.h @@ -1557,6 +1557,7 @@ public : virtual string CreateElementName(); string BuildConformanceName(const string& ParserName, const char* Prefix, const char* Suffix); void IsTruncated(int64u ExpectedSize = (int64u)-1, bool MoreThan = false, const char* Prefix = nullptr); + void DurationIssue(int64u ActualDuration, int64u ExpectedDuration = (int64u)-1, bool MoreThan = false, const char* Prefix = nullptr); void RanOutOfData(const char* Prefix = nullptr); void SynchLost(const char* Prefix = nullptr, int64u CountOfBytes = 0, bool AreZero = false); #else //MEDIAINFO_CONFORMANCE @@ -1568,6 +1569,7 @@ public : string CreateElementName() { return {}; } string BuildConformanceName(const string& ParserName, const char* Prefix, const char* Suffix) { return {}; } void IsTruncated(int64u ExpectedSize = (int64u)-1, bool MoreThan = false, const char* = nullptr) {} + void DurationIssue(int64u ActualDuration, int64u ExpectedDuration = (int64u)-1, bool MoreThan = false, const char* Prefix = nullptr) {} void RanOutOfData(const char* = nullptr) { Trusted_IsNot(); } void SynchLost(const char* = nullptr, int64u CountOfBytes = 0, bool AreZero = false) { Trusted_IsNot(); } #endif //MEDIAINFO_CONFORMANCE diff --git a/Source/MediaInfo/File__Analyze_MinimizeSize.h b/Source/MediaInfo/File__Analyze_MinimizeSize.h index 42f4626e52..2ba6b3287f 100644 --- a/Source/MediaInfo/File__Analyze_MinimizeSize.h +++ b/Source/MediaInfo/File__Analyze_MinimizeSize.h @@ -1458,6 +1458,7 @@ public : virtual string CreateElementName(); string BuildConformanceName(const string& ParserName, const char* Prefix, const char* Suffix); void IsTruncated(int64u ExpectedSize = (int64u)-1, bool MoreThan = false, const char* Prefix = nullptr); + void DurationIssue(int64u ActualDuration, int64u ExpectedDuration = (int64u)-1, bool MoreThan = false, const char* Prefix = nullptr); void RanOutOfData(const char* Prefix = nullptr); void SynchLost(const char* Prefix = nullptr, int64u CountOfBytes = 0, bool AreZero = false); #else //MEDIAINFO_CONFORMANCE @@ -1469,6 +1470,7 @@ public : string CreateElementName() { return {}; } string BuildConformanceName(const string& ParserName, const char* Prefix, const char* Suffix) { return {}; } void IsTruncated(int64u ExpectedSize = (int64u)-1, bool MoreThan = false, const char* = nullptr) {} + void DurationIssue(int64u ActualDuration, int64u ExpectedDuration = (int64u)-1, bool MoreThan = false, const char* Prefix = nullptr) {} void RanOutOfData(const char* = nullptr) { Trusted_IsNot(); } void SynchLost(const char* = nullptr, int64u CountOfBytes = 0, bool AreZero = false) { Trusted_IsNot(); } #endif //MEDIAINFO_CONFORMANCE