#604: M1236639 M1257931 M1256590 M1291543 M1363647 + backbugs

This commit is contained in:
Cameron Kaiser 2020-06-14 22:01:40 -07:00
parent 7850b17a7d
commit 80ece6a151
2 changed files with 97 additions and 22 deletions

View File

@ -157,7 +157,6 @@ MP3TrackDemuxer::SeekPosition() const {
return pos;
}
#ifdef ENABLE_TESTS
const FrameParser::Frame&
MP3TrackDemuxer::LastFrame() const {
return mParser.PrevFrame();
@ -167,7 +166,6 @@ RefPtr<MediaRawData>
MP3TrackDemuxer::DemuxSample() {
return GetNextFrame(FindNextFrame());
}
#endif
const ID3Parser::ID3Header&
MP3TrackDemuxer::ID3Header() const {
@ -204,7 +202,7 @@ MP3TrackDemuxer::FastSeek(const TimeUnit& aTime) {
if (!aTime.ToMicroseconds()) {
// Quick seek to the beginning of the stream.
mFrameIndex = 0;
} else if (vbr.IsTOCPresent()) {
} else if (vbr.IsTOCPresent() && Duration().ToMicroseconds() > 0) {
// Use TOC for more precise seeking.
const float durationFrac = static_cast<float>(aTime.ToMicroseconds()) /
Duration().ToMicroseconds();
@ -347,7 +345,7 @@ MP3TrackDemuxer::Duration() const {
int64_t numFrames = 0;
const auto numAudioFrames = mParser.VBRInfo().NumAudioFrames();
if (numAudioFrames) {
if (mParser.VBRInfo().IsValid() && numAudioFrames.valueOr(0) + 1 > 1) {
// VBR headers don't include the VBR header frame.
numFrames = numAudioFrames.value() + 1;
} else {
@ -356,7 +354,9 @@ MP3TrackDemuxer::Duration() const {
// Unknown length, we can't estimate duration.
return TimeUnit::FromMicroseconds(-1);
}
numFrames = (streamLen - mFirstFrameOffset) / AverageFrameLength();
if (AverageFrameLength() > 0) {
numFrames = (streamLen - mFirstFrameOffset) / AverageFrameLength();
}
}
return Duration(numFrames);
}
@ -371,6 +371,55 @@ MP3TrackDemuxer::Duration(int64_t aNumFrames) const {
return TimeUnit::FromMicroseconds(aNumFrames * usPerFrame);
}
MediaByteRange
MP3TrackDemuxer::FindFirstFrame() {
// Get engough successive frames to avoid invalid frame from cut stream.
// However, some website use very short mp3 file so using the same value as Chrome.
static const int MIN_SUCCESSIVE_FRAMES = 3;
MediaByteRange candidateFrame = FindNextFrame();
int numSuccFrames = candidateFrame.Length() > 0;
MediaByteRange currentFrame = candidateFrame;
MP3LOGV("FindFirst() first candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64,
candidateFrame.mStart, candidateFrame.Length());
while (candidateFrame.Length() && numSuccFrames < MIN_SUCCESSIVE_FRAMES) {
mParser.EndFrameSession();
mOffset = currentFrame.mEnd;
const MediaByteRange prevFrame = currentFrame;
// FindNextFrame() here will only return frames consistent with our candidate frame.
currentFrame = FindNextFrame();
numSuccFrames += currentFrame.Length() > 0;
// Multiple successive false positives, which wouldn't be caught by the consistency
// checks alone, can be detected by wrong alignment (non-zero gap between frames).
const int64_t frameSeparation = currentFrame.mStart - prevFrame.mEnd;
if (!currentFrame.Length() || frameSeparation != 0) {
MP3LOGV("FindFirst() not enough successive frames detected, "
"rejecting candidate frame: successiveFrames=%d, last Length()=%" PRIu64
", last frameSeparation=%" PRId64, numSuccFrames, currentFrame.Length(),
frameSeparation);
mParser.ResetFrameData();
mOffset = candidateFrame.mStart + 1;
candidateFrame = FindNextFrame();
numSuccFrames = candidateFrame.Length() > 0;
currentFrame = candidateFrame;
MP3LOGV("FindFirst() new candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64,
candidateFrame.mStart, candidateFrame.Length());
}
}
if (numSuccFrames >= MIN_SUCCESSIVE_FRAMES) {
MP3LOG("FindFirst() accepting candidate frame: "
"successiveFrames=%d", numSuccFrames);
} else {
MP3LOG("FindFirst() no suitable first frame found");
}
return candidateFrame;
}
static bool
VerifyFrameConsistency(
const FrameParser::Frame& aFrame1, const FrameParser::Frame& aFrame2) {
@ -511,7 +560,6 @@ MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) {
if (mNumParsedFrames == 1) {
// First frame parsed, let's read VBR info if available.
// TODO: read info that helps with seeking (bug 1163667).
ByteReader reader(frame->Data(), frame->Size());
mParser.ParseVBRHeader(&reader);
reader.DiscardRemaining();
@ -532,7 +580,7 @@ MP3TrackDemuxer::OffsetFromFrameIndex(int64_t aFrameIndex) const {
int64_t offset = 0;
const auto& vbr = mParser.VBRInfo();
if (vbr.NumBytes() && vbr.NumAudioFrames()) {
if (vbr.IsComplete()) {
offset = mFirstFrameOffset + aFrameIndex * vbr.NumBytes().value() /
vbr.NumAudioFrames().value();
} else if (AverageFrameLength() > 0) {
@ -548,7 +596,7 @@ MP3TrackDemuxer::FrameIndexFromOffset(int64_t aOffset) const {
int64_t frameIndex = 0;
const auto& vbr = mParser.VBRInfo();
if (vbr.NumBytes() && vbr.NumAudioFrames()) {
if (vbr.IsComplete()) {
frameIndex = static_cast<float>(aOffset - mFirstFrameOffset) /
vbr.NumBytes().value() * vbr.NumAudioFrames().value();
frameIndex = std::min<int64_t>(vbr.NumAudioFrames().value(), frameIndex);
@ -624,7 +672,7 @@ MP3TrackDemuxer::AverageFrameLength() const {
return static_cast<double>(mTotalFrameLen) / mNumParsedFrames;
}
const auto& vbr = mParser.VBRInfo();
if (vbr.NumBytes() && vbr.NumAudioFrames()) {
if (vbr.IsComplete() && vbr.NumAudioFrames().value() + 1) {
return static_cast<double>(vbr.NumBytes().value()) /
(vbr.NumAudioFrames().value() + 1);
}
@ -651,15 +699,20 @@ FrameParser::Reset() {
mFrame.Reset();
}
void
FrameParser::ResetFrameData() {
mFrame.Reset();
mFirstFrame.Reset();
mPrevFrame.Reset();
}
void
FrameParser::EndFrameSession() {
if (!mID3Parser.Header().IsValid()) {
// Reset ID3 tags only if we have not parsed a valid ID3 header yet.
mID3Parser.Reset();
}
#ifdef ENABLE_TESTS
mPrevFrame = mFrame;
#endif
mFrame.Reset();
}
@ -668,12 +721,10 @@ FrameParser::CurrentFrame() const {
return mFrame;
}
#ifdef ENABLE_TESTS
const FrameParser::Frame&
FrameParser::PrevFrame() const {
return mPrevFrame;
}
#endif
const FrameParser::Frame&
FrameParser::FirstFrame() const {
@ -960,6 +1011,21 @@ FrameParser::VBRHeader::IsTOCPresent() const {
return mTOC.size() == vbr_header::TOC_SIZE;
}
bool
FrameParser::VBRHeader::IsValid() const {
return mType != NONE;
}
bool
FrameParser::VBRHeader::IsComplete() const {
return IsValid() &&
mNumAudioFrames.valueOr(0) > 0 &&
mNumBytes.valueOr(0) > 0 &&
// We don't care about the scale for any computations here.
// mScale < 101 &&
true;
}
int64_t
FrameParser::VBRHeader::Offset(float aDurationFac) const {
if (!IsTOCPresent()) {
@ -1047,7 +1113,7 @@ FrameParser::VBRHeader::ParseVBRI(ByteReader* aReader) {
MOZ_ASSERT(aReader);
// ParseVBRI assumes that the ByteReader offset points to the beginning of a frame,
// therefore as a simple check, we look for the presence of a frame sync at that position.
MOZ_ASSERT(aReader->PeekU16() & 0xFFE0);
MOZ_ASSERT((aReader->PeekU16() & 0xFFE0) == 0xFFE0);
const size_t prevReaderOffset = aReader->Offset();
// VBRI have a fixed relative position, so let's check for it there.

View File

@ -232,6 +232,12 @@ public:
// Returns true iff Xing/Info TOC (table of contents) is present.
bool IsTOCPresent() const;
// Returns whether the header is valid (type XING or VBRI).
bool IsValid() const;
// Returns whether the header is valid and contains reasonable non-zero field values.
bool IsComplete() const;
// Returns the byte offset for the given duration percentage as a factor
// (0: begin, 1.0: end).
int64_t Offset(float aDurationFac) const;
@ -298,10 +304,8 @@ public:
// Returns the currently parsed frame. Reset via Reset or EndFrameSession.
const Frame& CurrentFrame() const;
#ifdef ENABLE_TESTS
// Returns the previously parsed frame. Reset via Reset.
const Frame& PrevFrame() const;
#endif
// Returns the first parsed frame. Reset via Reset.
const Frame& FirstFrame() const;
@ -312,9 +316,13 @@ public:
// Returns the parsed VBR header info. Note: check for validity by type.
const VBRHeader& VBRInfo() const;
// Resets the parser. Don't use between frames as first frame data is reset.
// Resets the parser.
void Reset();
// Resets all frame data, but not the ID3Header.
// Don't use between frames as first frame data is reset.
void ResetFrameData();
// Clear the last parsed frame to allow for next frame parsing, i.e.:
// - sets PrevFrame to CurrentFrame
// - resets the CurrentFrame
@ -344,9 +352,7 @@ private:
// previously parsed frame for debugging and the currently parsed frame.
Frame mFirstFrame;
Frame mFrame;
#ifdef ENABLE_TESTS
Frame mPrevFrame;
#endif
};
// The MP3 demuxer used to extract MPEG frames and side information out of
@ -373,10 +379,8 @@ public:
// Returns the estimated current seek position time.
media::TimeUnit SeekPosition() const;
#ifdef ENABLE_TESTS
const FrameParser::Frame& LastFrame() const;
RefPtr<MediaRawData> DemuxSample();
#endif
const ID3Parser::ID3Header& ID3Header() const;
const FrameParser::VBRHeader& VBRInfo() const;
@ -401,7 +405,12 @@ private:
// Seeks by scanning the stream up to the given time for more accurate results.
media::TimeUnit ScanUntil(const media::TimeUnit& aTime);
// Finds the next valid frame and returns its byte range.
// Finds the first valid frame and returns its byte range if found
// or a null-byte range otherwise.
MediaByteRange FindFirstFrame();
// Finds the next valid frame and returns its byte range if found
// or a null-byte range otherwise.
MediaByteRange FindNextFrame();
// Skips the next frame given the provided byte range.