From 6d0a42e587debf2f395bb081e6142f4fb648b0fd Mon Sep 17 00:00:00 2001 From: Fedor Date: Thu, 7 May 2020 14:46:01 +0300 Subject: [PATCH] [MSE] 1362440 Fix WebM segments with Tags elements not properly handled. --- dom/media/mediasource/ContainerParser.cpp | 57 +++++++---------------- dom/media/webm/WebMBufferedParser.cpp | 15 ++++++ dom/media/webm/WebMBufferedParser.h | 17 ++++++- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/dom/media/mediasource/ContainerParser.cpp b/dom/media/mediasource/ContainerParser.cpp index b4dcfde8a..9711d4fb6 100644 --- a/dom/media/mediasource/ContainerParser.cpp +++ b/dom/media/mediasource/ContainerParser.cpp @@ -127,58 +127,37 @@ public: MediaResult IsInitSegmentPresent(MediaByteBuffer* aData) override { ContainerParser::IsInitSegmentPresent(aData); - // XXX: This is overly primitive, needs to collect data as it's appended - // to the SB and handle, rather than assuming everything is present in a - // single aData segment. - // 0x1a45dfa3 // EBML - // ... - // DocType == "webm" - // ... - // 0x18538067 // Segment (must be "unknown" size or contain a value large - // enough to include the Segment Information and Tracks - // elements that follow) - // 0x1549a966 // -> Segment Info - // 0x1654ae6b // -> One or more Tracks - - // 0x1a45dfa3 // EBML if (aData->Length() < 4) { return NS_ERROR_NOT_AVAILABLE; } - if ((*aData)[0] == 0x1a && (*aData)[1] == 0x45 && (*aData)[2] == 0xdf && - (*aData)[3] == 0xa3) { - return NS_OK; + WebMBufferedParser parser(0); + nsTArray mapping; + ReentrantMonitor dummy("dummy"); + bool result = parser.Append(aData->Elements(), aData->Length(), mapping, + dummy); + if (!result) { + return MediaResult(NS_ERROR_FAILURE, RESULT_DETAIL("Invalid webm content")); } - return MediaResult(NS_ERROR_FAILURE, RESULT_DETAIL("Invalid webm content")); + return parser.mInitEndOffset > 0 ? NS_OK : NS_ERROR_NOT_AVAILABLE; } MediaResult IsMediaSegmentPresent(MediaByteBuffer* aData) override { ContainerParser::IsMediaSegmentPresent(aData); - // XXX: This is overly primitive, needs to collect data as it's appended - // to the SB and handle, rather than assuming everything is present in a - // single aData segment. - // 0x1a45dfa3 // EBML - // ... - // DocType == "webm" - // ... - // 0x18538067 // Segment (must be "unknown" size) - // 0x1549a966 // -> Segment Info - // 0x1654ae6b // -> One or more Tracks - - // 0x1f43b675 // Cluster if (aData->Length() < 4) { return NS_ERROR_NOT_AVAILABLE; } - if ((*aData)[0] == 0x1f && (*aData)[1] == 0x43 && (*aData)[2] == 0xb6 && - (*aData)[3] == 0x75) { - return NS_OK; + + WebMBufferedParser parser(0); + nsTArray mapping; + ReentrantMonitor dummy("dummy"); + parser.AppendMediaSegmentOnly(); + bool result = parser.Append(aData->Elements(), aData->Length(), mapping, + dummy); + if (!result) { + return MediaResult(NS_ERROR_FAILURE, RESULT_DETAIL("Invalid webm content")); } - // 0x1c53bb6b // Cues - if ((*aData)[0] == 0x1c && (*aData)[1] == 0x53 && (*aData)[2] == 0xbb && - (*aData)[3] == 0x6b) { - return NS_OK; - } - return MediaResult(NS_ERROR_FAILURE, RESULT_DETAIL("Invalid webm content")); + return parser.GetClusterOffset() >= 0 ? NS_OK : NS_ERROR_NOT_AVAILABLE; } MediaResult ParseStartAndEndTimestamps(MediaByteBuffer* aData, diff --git a/dom/media/webm/WebMBufferedParser.cpp b/dom/media/webm/WebMBufferedParser.cpp index 21154ab4b..979308cf0 100644 --- a/dom/media/webm/WebMBufferedParser.cpp +++ b/dom/media/webm/WebMBufferedParser.cpp @@ -113,6 +113,7 @@ bool WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength, } else { mClusterEndOffset = -1; } + mGotClusterTimecode = false; mState = READ_ELEMENT_ID; break; case BLOCKGROUP_ID: @@ -121,6 +122,11 @@ bool WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength, case SIMPLEBLOCK_ID: /* FALLTHROUGH */ case BLOCK_ID: + if (!mGotClusterTimecode) { + WEBM_DEBUG("The Timecode element must appear before any Block or " + "SimpleBlock elements in a Cluster"); + return false; + } mBlockSize = mElement.mSize.mValue; mBlockTimecode = 0; mBlockTimecodeLength = BLOCK_TIMECODE_LENGTH; @@ -164,6 +170,7 @@ bool WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength, break; case READ_TIMECODESCALE: if (!mGotTimecodeScale) { + WEBM_DEBUG("Should get the SegmentInfo first"); return false; } mTimecodeScale = mVInt.mValue; @@ -171,6 +178,7 @@ bool WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength, break; case READ_CLUSTER_TIMECODE: mClusterTimecode = mVInt.mValue; + mGotClusterTimecode = true; mState = READ_ELEMENT_ID; break; case READ_BLOCK_TIMECODE: @@ -190,6 +198,7 @@ bool WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength, // Don't insert invalid negative timecodes. if (mBlockTimecode >= 0 || mClusterTimecode >= uint16_t(abs(mBlockTimecode))) { if (!mGotTimecodeScale) { + WEBM_DEBUG("Should get the TimecodeScale first"); return false; } uint64_t absTimecode = mClusterTimecode + mBlockTimecode; @@ -266,6 +275,12 @@ WebMBufferedParser::EndSegmentOffset(int64_t aOffset) return mBlockEndOffset; } +int64_t +WebMBufferedParser::GetClusterOffset() const +{ + return mClusterOffset; +} + // SyncOffsetComparator and TimeComparator are slightly confusing, in that // the nsTArray they're used with (mTimeMapping) is sorted by mEndOffset and // these comparators are used on the other fields of WebMTimeDataOffset. diff --git a/dom/media/webm/WebMBufferedParser.h b/dom/media/webm/WebMBufferedParser.h index bc3de4ba0..4245561fc 100644 --- a/dom/media/webm/WebMBufferedParser.h +++ b/dom/media/webm/WebMBufferedParser.h @@ -67,7 +67,7 @@ struct WebMBufferedParser , mVIntLeft(0) , mBlockSize(0) , mClusterTimecode(0) - , mClusterOffset(0) + , mClusterOffset(-1) , mClusterEndOffset(-1) , mBlockOffset(0) , mBlockTimecode(0) @@ -75,6 +75,7 @@ struct WebMBufferedParser , mSkipBytes(0) , mTimecodeScale(1000000) , mGotTimecodeScale(false) + , mGotClusterTimecode(false) { if (mStartOffset != 0) { mState = FIND_CLUSTER_SYNC; @@ -86,6 +87,12 @@ struct WebMBufferedParser return mTimecodeScale; } + // Use this function when we would only feed media segment for the parser. + void AppendMediaSegmentOnly() + { + mGotTimecodeScale = true; + } + // If this parser is not expected to parse a segment info, it must be told // the appropriate timecode scale to use from elsewhere. void SetTimecodeScale(uint32_t aTimecodeScale) { @@ -114,6 +121,9 @@ struct WebMBufferedParser // This allows to determine the end of the interval containg aOffset. int64_t EndSegmentOffset(int64_t aOffset); + // Return the Cluster offset, return -1 if we can't find the Cluster. + int64_t GetClusterOffset() const; + // The offset at which this parser started parsing. Used to merge // adjacent parsers, in which case the later parser adopts the earlier // parser's mStartOffset. @@ -231,7 +241,7 @@ private: // Start offset of the cluster currently being parsed. Used as the sync // point offset for the offset-to-time mapping as each block timecode is - // been parsed. + // been parsed. -1 if unknown. int64_t mClusterOffset; // End offset of the cluster currently being parsed. -1 if unknown. @@ -260,6 +270,9 @@ private: // True if we read the timecode scale from the segment info or have // confirmed that the default value is to be used. bool mGotTimecodeScale; + + // True if we've read the cluster time code. + bool mGotClusterTimecode; }; class WebMBufferedState final