Attachment #520044: Patch 5: Ensure WebM GetBuffered() is threadsafe for bug #639391

View | Details | Raw Unified | Return to bug 639391
Collapse All | Expand All

(-)a/content/media/webm/nsWebMBufferedParser.cpp (-6 / +21 lines)
Line     Link Here 
 Lines 34-49    Link Here 
34
 * the provisions above, a recipient may use your version of this file under
34
 * the provisions above, a recipient may use your version of this file under
35
 * the terms of any one of the MPL, the GPL or the LGPL.
35
 * the terms of any one of the MPL, the GPL or the LGPL.
36
 *
36
 *
37
 * ***** END LICENSE BLOCK ***** */
37
 * ***** END LICENSE BLOCK ***** */
38
38
39
#include "nsAlgorithm.h"
39
#include "nsAlgorithm.h"
40
#include "nsWebMBufferedParser.h"
40
#include "nsWebMBufferedParser.h"
41
#include "nsTimeRanges.h"
41
#include "nsTimeRanges.h"
42
#include "nsThreadUtils.h"
43
44
using mozilla::MonitorAutoEnter;
42
45
43
static const double NS_PER_S = 1e9;
46
static const double NS_PER_S = 1e9;
44
static const double MS_PER_S = 1e3;
47
static const double MS_PER_S = 1e3;
45
48
46
static PRUint32
49
static PRUint32
47
VIntLength(unsigned char aFirstByte, PRUint32* aMask)
50
VIntLength(unsigned char aFirstByte, PRUint32* aMask)
48
{
51
{
49
  PRUint32 count = 1;
52
  PRUint32 count = 1;
 Lines 58-75   VIntLength(unsigned char aFirstByte, PRU Link Here 
58
  if (aMask) {
61
  if (aMask) {
59
    *aMask = mask;
62
    *aMask = mask;
60
  }
63
  }
61
  NS_ASSERTION(count >= 1 && count <= 8, "Insane VInt length.");
64
  NS_ASSERTION(count >= 1 && count <= 8, "Insane VInt length.");
62
  return count;
65
  return count;
63
}
66
}
64
67
65
void nsWebMBufferedParser::Append(const unsigned char* aBuffer, PRUint32 aLength,
68
void nsWebMBufferedParser::Append(const unsigned char* aBuffer, PRUint32 aLength,
66
                                  nsTArray<nsWebMTimeDataOffset>& aMapping)
69
                                  nsTArray<nsWebMTimeDataOffset>& aMapping,
70
                                  Monitor& aMonitor)
67
{
71
{
72
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
73
68
  static const unsigned char CLUSTER_ID[] = { 0x1f, 0x43, 0xb6, 0x75 };
74
  static const unsigned char CLUSTER_ID[] = { 0x1f, 0x43, 0xb6, 0x75 };
69
  static const unsigned char TIMECODE_ID = 0xe7;
75
  static const unsigned char TIMECODE_ID = 0xe7;
70
  static const unsigned char BLOCKGROUP_ID = 0xa0;
76
  static const unsigned char BLOCKGROUP_ID = 0xa0;
71
  static const unsigned char BLOCK_ID = 0xa1;
77
  static const unsigned char BLOCK_ID = 0xa1;
72
  static const unsigned char SIMPLEBLOCK_ID = 0xa3;
78
  static const unsigned char SIMPLEBLOCK_ID = 0xa3;
73
79
74
  const unsigned char* p = aBuffer;
80
  const unsigned char* p = aBuffer;
75
81
 Lines 162-181   void nsWebMBufferedParser::Append(const Link Here 
162
    case READ_BLOCK_TIMECODE:
168
    case READ_BLOCK_TIMECODE:
163
      if (mBlockTimecodeLength) {
169
      if (mBlockTimecodeLength) {
164
        mBlockTimecode <<= 8;
170
        mBlockTimecode <<= 8;
165
        mBlockTimecode |= *p++;
171
        mBlockTimecode |= *p++;
166
        mBlockTimecodeLength -= 1;
172
        mBlockTimecodeLength -= 1;
167
      } else {
173
      } else {
168
        // It's possible we've parsed this data before, so avoid inserting
174
        // It's possible we've parsed this data before, so avoid inserting
169
        // duplicate nsWebMTimeDataOffset entries.
175
        // duplicate nsWebMTimeDataOffset entries.
170
        PRUint32 idx;
176
        {
171
        if (!aMapping.GreatestIndexLtEq(mBlockOffset, idx)) {
177
          MonitorAutoEnter mon(aMonitor);
172
          nsWebMTimeDataOffset entry(mBlockOffset, mClusterTimecode + mBlockTimecode);
178
          PRUint32 idx;
173
          aMapping.InsertElementAt(idx, entry);
179
          if (!aMapping.GreatestIndexLtEq(mBlockOffset, idx)) {
180
            nsWebMTimeDataOffset entry(mBlockOffset, mClusterTimecode + mBlockTimecode);
181
            aMapping.InsertElementAt(idx, entry);
182
          }
174
        }
183
        }
175
184
176
        // Skip rest of block header and the block's payload.
185
        // Skip rest of block header and the block's payload.
177
        mBlockSize -= mVIntLength;
186
        mBlockSize -= mVIntLength;
178
        mBlockSize -= 2;
187
        mBlockSize -= 2;
179
        mSkipBytes = PRUint32(mBlockSize);
188
        mSkipBytes = PRUint32(mBlockSize);
180
        mState = SKIP_DATA;
189
        mState = SKIP_DATA;
181
        mNextState = ANY_BLOCK_SYNC;
190
        mNextState = ANY_BLOCK_SYNC;
 Lines 203-218   void nsWebMBufferedParser::Append(const Link Here 
203
  mCurrentOffset += aLength;
212
  mCurrentOffset += aLength;
204
}
213
}
205
214
206
void nsWebMBufferedState::CalculateBufferedForRange(nsTimeRanges* aBuffered,
215
void nsWebMBufferedState::CalculateBufferedForRange(nsTimeRanges* aBuffered,
207
                                                    PRInt64 aStartOffset, PRInt64 aEndOffset,
216
                                                    PRInt64 aStartOffset, PRInt64 aEndOffset,
208
                                                    PRUint64 aTimecodeScale,
217
                                                    PRUint64 aTimecodeScale,
209
                                                    PRInt64 aStartTimeOffsetNS)
218
                                                    PRInt64 aStartTimeOffsetNS)
210
{
219
{
220
  MonitorAutoEnter mon(mMonitor);
221
211
  // Find the first nsWebMTimeDataOffset at or after aStartOffset.
222
  // Find the first nsWebMTimeDataOffset at or after aStartOffset.
212
  PRUint32 start;
223
  PRUint32 start;
213
  mTimeMapping.GreatestIndexLtEq(aStartOffset, start);
224
  mTimeMapping.GreatestIndexLtEq(aStartOffset, start);
214
  if (start == mTimeMapping.Length()) {
225
  if (start == mTimeMapping.Length()) {
215
    return;
226
    return;
216
  }
227
  }
217
228
218
  // Find the first nsWebMTimeDataOffset at or before aEndOffset.
229
  // Find the first nsWebMTimeDataOffset at or before aEndOffset.
 Lines 246-261   void nsWebMBufferedState::CalculateBuffe Link Here 
246
257
247
  double startTime = (mTimeMapping[start].mTimecode * aTimecodeScale - aStartTimeOffsetNS) / NS_PER_S;
258
  double startTime = (mTimeMapping[start].mTimecode * aTimecodeScale - aStartTimeOffsetNS) / NS_PER_S;
248
  double endTime = (mTimeMapping[end].mTimecode * aTimecodeScale - aStartTimeOffsetNS) / NS_PER_S;
259
  double endTime = (mTimeMapping[end].mTimecode * aTimecodeScale - aStartTimeOffsetNS) / NS_PER_S;
249
  aBuffered->Add(startTime, endTime);
260
  aBuffered->Add(startTime, endTime);
250
}
261
}
251
262
252
void nsWebMBufferedState::NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset)
263
void nsWebMBufferedState::NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset)
253
{
264
{
265
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
254
  PRUint32 idx;
266
  PRUint32 idx;
255
  if (!mRangeParsers.GreatestIndexLtEq(aOffset, idx)) {
267
  if (!mRangeParsers.GreatestIndexLtEq(aOffset, idx)) {
256
    // If the incoming data overlaps an already parsed range, adjust the
268
    // If the incoming data overlaps an already parsed range, adjust the
257
    // buffer so that we only reparse the new data.  It's also possible to
269
    // buffer so that we only reparse the new data.  It's also possible to
258
    // have an overlap where the end of the incoming data is within an
270
    // have an overlap where the end of the incoming data is within an
259
    // already parsed range, but we don't bother handling that other than by
271
    // already parsed range, but we don't bother handling that other than by
260
    // avoiding storing duplicate timecodes when the parser runs.
272
    // avoiding storing duplicate timecodes when the parser runs.
261
    if (idx != mRangeParsers.Length() && mRangeParsers[idx].mStartOffset <= aOffset) {
273
    if (idx != mRangeParsers.Length() && mRangeParsers[idx].mStartOffset <= aOffset) {
 Lines 269-285   void nsWebMBufferedState::NotifyDataArri Link Here 
269
      NS_ASSERTION(adjust >= 0, "Overlap detection bug.");
281
      NS_ASSERTION(adjust >= 0, "Overlap detection bug.");
270
      aBuffer += adjust;
282
      aBuffer += adjust;
271
      aLength -= PRUint32(adjust);
283
      aLength -= PRUint32(adjust);
272
    } else {
284
    } else {
273
      mRangeParsers.InsertElementAt(idx, nsWebMBufferedParser(aOffset));
285
      mRangeParsers.InsertElementAt(idx, nsWebMBufferedParser(aOffset));
274
    }
286
    }
275
  }
287
  }
276
288
277
  mRangeParsers[idx].Append(reinterpret_cast<const unsigned char*>(aBuffer), aLength, mTimeMapping);
289
  mRangeParsers[idx].Append(reinterpret_cast<const unsigned char*>(aBuffer),
290
                            aLength,
291
                            mTimeMapping,
292
                            mMonitor);
278
293
279
  // Merge parsers with overlapping regions and clean up the remnants.
294
  // Merge parsers with overlapping regions and clean up the remnants.
280
  PRUint32 i = 0;
295
  PRUint32 i = 0;
281
  while (i + 1 < mRangeParsers.Length()) {
296
  while (i + 1 < mRangeParsers.Length()) {
282
    if (mRangeParsers[i].mCurrentOffset >= mRangeParsers[i + 1].mStartOffset) {
297
    if (mRangeParsers[i].mCurrentOffset >= mRangeParsers[i + 1].mStartOffset) {
283
      mRangeParsers[i + 1].mStartOffset = mRangeParsers[i].mStartOffset;
298
      mRangeParsers[i + 1].mStartOffset = mRangeParsers[i].mStartOffset;
284
      mRangeParsers.RemoveElementAt(i);
299
      mRangeParsers.RemoveElementAt(i);
285
    } else {
300
    } else {
(-)a/content/media/webm/nsWebMBufferedParser.h (-3 / +10 lines)
Line     Link Here 
 Lines 35-52    Link Here 
35
 * the terms of any one of the MPL, the GPL or the LGPL.
35
 * the terms of any one of the MPL, the GPL or the LGPL.
36
 *
36
 *
37
 * ***** END LICENSE BLOCK ***** */
37
 * ***** END LICENSE BLOCK ***** */
38
#if !defined(nsWebMBufferedParser_h_)
38
#if !defined(nsWebMBufferedParser_h_)
39
#define nsWebMBufferedParser_h_
39
#define nsWebMBufferedParser_h_
40
40
41
#include "nsISupportsImpl.h"
41
#include "nsISupportsImpl.h"
42
#include "nsTArray.h"
42
#include "nsTArray.h"
43
#include "mozilla/Monitor.h"
43
44
44
class nsTimeRanges;
45
class nsTimeRanges;
46
using mozilla::Monitor;
45
47
46
// Stores a stream byte offset and the scaled timecode of the block at
48
// Stores a stream byte offset and the scaled timecode of the block at
47
// that offset.  The timecode must be scaled by the stream's timecode
49
// that offset.  The timecode must be scaled by the stream's timecode
48
// scale before use.
50
// scale before use.
49
struct nsWebMTimeDataOffset
51
struct nsWebMTimeDataOffset
50
{
52
{
51
  nsWebMTimeDataOffset(PRInt64 aOffset, PRUint64 aTimecode)
53
  nsWebMTimeDataOffset(PRInt64 aOffset, PRUint64 aTimecode)
52
    : mOffset(aOffset), mTimecode(aTimecode)
54
    : mOffset(aOffset), mTimecode(aTimecode)
 Lines 72-90   struct nsWebMTimeDataOffset Link Here 
72
// within the stream.
74
// within the stream.
73
struct nsWebMBufferedParser
75
struct nsWebMBufferedParser
74
{
76
{
75
  nsWebMBufferedParser(PRInt64 aOffset)
77
  nsWebMBufferedParser(PRInt64 aOffset)
76
    : mStartOffset(aOffset), mCurrentOffset(aOffset), mState(CLUSTER_SYNC), mClusterIDPos(0)
78
    : mStartOffset(aOffset), mCurrentOffset(aOffset), mState(CLUSTER_SYNC), mClusterIDPos(0)
77
  {}
79
  {}
78
80
79
  // Steps the parser through aLength bytes of data.  Always consumes
81
  // Steps the parser through aLength bytes of data.  Always consumes
80
  // aLength bytes.  Updates mCurrentOffset before returning.
82
  // aLength bytes.  Updates mCurrentOffset before returning.  Acquires
83
  // aMonitor before using aMapping.
81
  void Append(const unsigned char* aBuffer, PRUint32 aLength,
84
  void Append(const unsigned char* aBuffer, PRUint32 aLength,
82
              nsTArray<nsWebMTimeDataOffset>& aMapping);
85
              nsTArray<nsWebMTimeDataOffset>& aMapping,
86
              Monitor& aMonitor);
83
87
84
  bool operator==(PRInt64 aOffset) const {
88
  bool operator==(PRInt64 aOffset) const {
85
    return mCurrentOffset == aOffset;
89
    return mCurrentOffset == aOffset;
86
  }
90
  }
87
91
88
  bool operator<(PRInt64 aOffset) const {
92
  bool operator<(PRInt64 aOffset) const {
89
    return mCurrentOffset < aOffset;
93
    return mCurrentOffset < aOffset;
90
  }
94
  }
 Lines 207-237   private: Link Here 
207
  PRUint32 mSkipBytes;
211
  PRUint32 mSkipBytes;
208
};
212
};
209
213
210
class nsWebMBufferedState
214
class nsWebMBufferedState
211
{
215
{
212
  NS_INLINE_DECL_REFCOUNTING(nsWebMBufferedState)
216
  NS_INLINE_DECL_REFCOUNTING(nsWebMBufferedState)
213
217
214
public:
218
public:
215
  nsWebMBufferedState() {
219
  nsWebMBufferedState() : mMonitor("nsWebMBufferedState") {
216
    MOZ_COUNT_CTOR(nsWebMBufferedState);
220
    MOZ_COUNT_CTOR(nsWebMBufferedState);
217
  }
221
  }
218
222
219
  ~nsWebMBufferedState() {
223
  ~nsWebMBufferedState() {
220
    MOZ_COUNT_DTOR(nsWebMBufferedState);
224
    MOZ_COUNT_DTOR(nsWebMBufferedState);
221
  }
225
  }
222
226
223
  void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset);
227
  void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset);
224
  void CalculateBufferedForRange(nsTimeRanges* aBuffered,
228
  void CalculateBufferedForRange(nsTimeRanges* aBuffered,
225
                                 PRInt64 aStartOffset, PRInt64 aEndOffset,
229
                                 PRInt64 aStartOffset, PRInt64 aEndOffset,
226
                                 PRUint64 aTimecodeScale,
230
                                 PRUint64 aTimecodeScale,
227
                                 PRInt64 aStartTimeOffsetNS);
231
                                 PRInt64 aStartTimeOffsetNS);
228
232
229
private:
233
private:
234
  // Synchronizes access to the mTimeMapping array.
235
  Monitor mMonitor;
236
230
  // Sorted (by offset) map of data offsets to timecodes.  Populated
237
  // Sorted (by offset) map of data offsets to timecodes.  Populated
231
  // on the main thread as data is received and parsed by nsWebMBufferedParsers.
238
  // on the main thread as data is received and parsed by nsWebMBufferedParsers.
232
  nsTArray<nsWebMTimeDataOffset> mTimeMapping;
239
  nsTArray<nsWebMTimeDataOffset> mTimeMapping;
233
240
234
  // Sorted (by offset) live parser instances.  Main thread only.
241
  // Sorted (by offset) live parser instances.  Main thread only.
235
  nsTArray<nsWebMBufferedParser> mRangeParsers;
242
  nsTArray<nsWebMBufferedParser> mRangeParsers;
236
};
243
};
237
244
(-)a/content/media/webm/nsWebMReader.cpp (-12 / +8 lines)
Line     Link Here 
 Lines 791-822   nsresult nsWebMReader::GetBuffered(nsTim Link Here 
791
791
792
  // Special case completely cached files.  This also handles local files.
792
  // Special case completely cached files.  This also handles local files.
793
  if (stream->IsDataCachedToEndOfStream(0)) {
793
  if (stream->IsDataCachedToEndOfStream(0)) {
794
    uint64_t duration = 0;
794
    uint64_t duration = 0;
795
    if (nestegg_duration(mContext, &duration) == 0) {
795
    if (nestegg_duration(mContext, &duration) == 0) {
796
      aBuffered->Add(0, duration / NS_PER_S);
796
      aBuffered->Add(0, duration / NS_PER_S);
797
    }
797
    }
798
  } else {
798
  } else {
799
    PRInt64 startOffset = stream->GetNextCachedData(0);
799
    nsMediaStream* stream = mDecoder->GetCurrentStream();
800
    nsTArray<nsByteRange> ranges;
801
    nsresult res = stream->GetCachedRanges(ranges);
802
    NS_ENSURE_SUCCESS(res, res);
803
800
    PRInt64 startTimeOffsetNS = aStartTime * NS_PER_MS;
804
    PRInt64 startTimeOffsetNS = aStartTime * NS_PER_MS;
801
    while (startOffset >= 0) {
805
    for (PRUint32 index = 0; index < ranges.Length(); index++) {
802
      PRInt64 endOffset = stream->GetCachedDataEnd(startOffset);
803
      NS_ASSERTION(startOffset < endOffset, "Cached range invalid");
804
805
      mBufferedState->CalculateBufferedForRange(aBuffered,
806
      mBufferedState->CalculateBufferedForRange(aBuffered,
806
                                                startOffset,
807
                                                ranges[index].mStart,
807
                                                endOffset,
808
                                                ranges[index].mEnd,
808
                                                timecodeScale,
809
                                                timecodeScale,
809
                                                startTimeOffsetNS);
810
                                                startTimeOffsetNS);
810
811
      // Advance to the next cached data range.
812
      startOffset = stream->GetNextCachedData(endOffset);
813
      NS_ASSERTION(startOffset == -1 || startOffset > endOffset,
814
                   "Next cached range invalid");
815
    }
811
    }
816
  }
812
  }
817
813
818
  return NS_OK;
814
  return NS_OK;
819
}
815
}
820
816
821
void nsWebMReader::NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset)
817
void nsWebMReader::NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRUint32 aOffset)
822
{
818
{

Return to bug 639391