Be more error resilient when checking the container and MP3 frames
Most players/tools can cope with MP3 files which have some bytes of junk after the ID3v2 tag just fine, e.g. ffmpeg shows just ``` [mp3 @ 0x559e1f4cbd80] Skipping 1670 bytes of junk at 1165. ``` and most players can play the file just fine. This change makes the tag editor also more resilient, allowing it to skip a certain amount of junk bytes before a known container is detected. It will also skip a certain amount of junk MPEG audio frames.
This commit is contained in:
parent
f371efe642
commit
40031b8ddf
|
@ -172,17 +172,20 @@ startParsingSignature:
|
||||||
stream().seekg(m_containerOffset, ios_base::beg);
|
stream().seekg(m_containerOffset, ios_base::beg);
|
||||||
stream().read(buff, sizeof(buff));
|
stream().read(buff, sizeof(buff));
|
||||||
|
|
||||||
// skip zero bytes/padding
|
// skip zero/junk bytes
|
||||||
// note: Only skipping 4 or more consecutive zero bytes at this point because some signatures start with up to 4 zero bytes.
|
// notes:
|
||||||
|
// - Only skipping 4 or more consecutive zero bytes at this point because some signatures start with up to 4 zero bytes.
|
||||||
|
// - It seems that most players/tools¹ skip junk bytes, at least when reading MP3 files. Hence the tagparser library is following
|
||||||
|
// the same approach. (¹e.g. ffmpeg: "[mp3 @ 0x559e1f4cbd80] Skipping 1670 bytes of junk at 1165.")
|
||||||
size_t bytesSkipped = 0;
|
size_t bytesSkipped = 0;
|
||||||
for (buffOffset = buff; buffOffset != buffEnd && !(*buffOffset); ++buffOffset, ++bytesSkipped)
|
for (buffOffset = buff; buffOffset != buffEnd && !(*buffOffset); ++buffOffset, ++bytesSkipped)
|
||||||
;
|
;
|
||||||
if (bytesSkipped >= 4) {
|
if (bytesSkipped >= 4) {
|
||||||
skipZeroBytes:
|
skipJunkBytes:
|
||||||
m_containerOffset += bytesSkipped;
|
m_containerOffset += bytesSkipped;
|
||||||
m_paddingSize += bytesSkipped;
|
m_paddingSize += bytesSkipped;
|
||||||
|
|
||||||
// give up after 0x100 bytes
|
// give up after 0x800 bytes
|
||||||
if ((bytesSkippedBeforeContainer += bytesSkipped) >= 0x800u) {
|
if ((bytesSkippedBeforeContainer += bytesSkipped) >= 0x800u) {
|
||||||
m_containerParsingStatus = ParsingStatus::NotSupported;
|
m_containerParsingStatus = ParsingStatus::NotSupported;
|
||||||
m_containerFormat = ContainerFormat::Unknown;
|
m_containerFormat = ContainerFormat::Unknown;
|
||||||
|
@ -255,10 +258,6 @@ startParsingSignature:
|
||||||
static_cast<OggContainer *>(m_container.get())->setChecksumValidationEnabled(m_forceFullParse);
|
static_cast<OggContainer *>(m_container.get())->setChecksumValidationEnabled(m_forceFullParse);
|
||||||
break;
|
break;
|
||||||
case ContainerFormat::Unknown:
|
case ContainerFormat::Unknown:
|
||||||
// skip the zero bytes after all
|
|
||||||
if (bytesSkipped) {
|
|
||||||
goto skipZeroBytes;
|
|
||||||
}
|
|
||||||
// check for magic numbers at odd offsets
|
// check for magic numbers at odd offsets
|
||||||
// -> check for tar (magic number at offset 0x101)
|
// -> check for tar (magic number at offset 0x101)
|
||||||
if (size() > 0x107) {
|
if (size() > 0x107) {
|
||||||
|
@ -269,16 +268,17 @@ startParsingSignature:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
// skip previously determined zero-bytes or try our luck on the next byte
|
||||||
|
if (!bytesSkipped) {
|
||||||
|
++bytesSkipped;
|
||||||
|
}
|
||||||
|
goto skipJunkBytes;
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytesSkippedBeforeContainer) {
|
if (bytesSkippedBeforeContainer) {
|
||||||
diag.emplace_back(DiagLevel::Warning,
|
diag.emplace_back(DiagLevel::Warning, argsToString(bytesSkippedBeforeContainer, " bytes of junk skipped"), context);
|
||||||
argsToString(bytesSkippedBeforeContainer,
|
|
||||||
m_id3v2Tags.empty() ? " zero-bytes skipped at the beginning of the file." : " zero-bytes skipped after the ID3v2 tag."),
|
|
||||||
context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set parsing status
|
// set parsing status
|
||||||
|
|
|
@ -41,15 +41,32 @@ void MpegAudioFrameStream::internalParseHeader(Diagnostics &diag)
|
||||||
m_size = static_cast<std::uint64_t>(m_istream->tellg()) + 125u - m_startOffset;
|
m_size = static_cast<std::uint64_t>(m_istream->tellg()) + 125u - m_startOffset;
|
||||||
}
|
}
|
||||||
m_istream->seekg(static_cast<streamoff>(m_startOffset), ios_base::beg);
|
m_istream->seekg(static_cast<streamoff>(m_startOffset), ios_base::beg);
|
||||||
// parse frames until the first non-empty frame is reached
|
// parse frames until the first valid, non-empty frame is reached
|
||||||
auto isFrameEmpty = false;
|
for (size_t invalidByteskipped = 0; m_frames.size() < 200 && invalidByteskipped <= 0x600u;) {
|
||||||
while (!isFrameEmpty && m_frames.size() < 200) {
|
MpegAudioFrame &frame = invalidByteskipped > 0 ? m_frames.back() : m_frames.emplace_back();
|
||||||
MpegAudioFrame &frame = m_frames.emplace_back();
|
try {
|
||||||
frame.parseHeader(m_reader, diag);
|
frame.parseHeader(m_reader, diag);
|
||||||
isFrameEmpty = frame.size();
|
} catch (const InvalidDataException &e) {
|
||||||
|
if (++invalidByteskipped > 1) {
|
||||||
|
diag.pop_back();
|
||||||
|
}
|
||||||
|
m_istream->seekg(-3, ios_base::cur);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (invalidByteskipped > 1) {
|
||||||
|
diag.emplace_back(DiagLevel::Critical, argsToString("The next ", invalidByteskipped, " bytes are junk as well."), context);
|
||||||
|
}
|
||||||
|
if (!frame.size()) {
|
||||||
|
continue; // likely just junk, check further frames
|
||||||
|
}
|
||||||
|
invalidByteskipped = 0;
|
||||||
if (frame.isProtectedByCrc()) {
|
if (frame.isProtectedByCrc()) {
|
||||||
m_istream->seekg(2, ios_base::cur);
|
m_istream->seekg(2, ios_base::cur);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!m_frames.back().isValid()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const MpegAudioFrame &frame = m_frames.back();
|
const MpegAudioFrame &frame = m_frames.back();
|
||||||
addInfo(frame, *this);
|
addInfo(frame, *this);
|
||||||
|
|
Loading…
Reference in New Issue