Attachment #520039: Patch 1: Push Ogg specific seeking stuff down into nsOggReader for bug #639391

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

(-)a/content/media/nsBuiltinDecoderReader.cpp (-78 lines)
Line     Link Here 
 Lines 225-318   nsresult nsBuiltinDecoderReader::ResetDe Link Here 
225
  nsresult res = NS_OK;
225
  nsresult res = NS_OK;
226
226
227
  mVideoQueue.Reset();
227
  mVideoQueue.Reset();
228
  mAudioQueue.Reset();
228
  mAudioQueue.Reset();
229
229
230
  return res;
230
  return res;
231
}
231
}
232
232
233
nsresult nsBuiltinDecoderReader::GetBufferedBytes(nsTArray<ByteRange>& aRanges)
234
{
235
  NS_ASSERTION(mDecoder->OnStateMachineThread(),
236
               "Should be on state machine thread.");
237
  mMonitor.AssertCurrentThreadIn();
238
  PRInt64 startOffset = mDataOffset;
239
  nsMediaStream* stream = mDecoder->GetCurrentStream();
240
  while (PR_TRUE) {
241
    PRInt64 endOffset = stream->GetCachedDataEnd(startOffset);
242
    if (endOffset == startOffset) {
243
      // Uncached at startOffset.
244
      endOffset = stream->GetNextCachedData(startOffset);
245
      if (endOffset == -1) {
246
        // Uncached at startOffset until endOffset of stream, or we're at
247
        // the end of stream.
248
        break;
249
      }
250
    } else {
251
      // Bytes [startOffset..endOffset] are cached.
252
      PRInt64 startTime = -1;
253
      PRInt64 endTime = -1;
254
      if (NS_FAILED(ResetDecode())) {
255
        return NS_ERROR_FAILURE;
256
      }
257
      FindStartTime(startOffset, startTime);
258
      if (startTime != -1 &&
259
          ((endTime = FindEndTime(endOffset)) != -1))
260
      {
261
        NS_ASSERTION(startOffset < endOffset,
262
                     "Start offset must be before end offset");
263
        NS_ASSERTION(startTime < endTime,
264
                     "Start time must be before end time");
265
        aRanges.AppendElement(ByteRange(startOffset,
266
                                        endOffset,
267
                                        startTime,
268
                                        endTime));
269
      }
270
    }
271
    startOffset = endOffset;
272
  }
273
  if (NS_FAILED(ResetDecode())) {
274
    return NS_ERROR_FAILURE;
275
  }
276
  return NS_OK;
277
}
278
279
ByteRange
280
nsBuiltinDecoderReader::GetSeekRange(const nsTArray<ByteRange>& ranges,
281
                                     PRInt64 aTarget,
282
                                     PRInt64 aStartTime,
283
                                     PRInt64 aEndTime,
284
                                     PRBool aExact)
285
{
286
  NS_ASSERTION(mDecoder->OnStateMachineThread(),
287
               "Should be on state machine thread.");
288
  PRInt64 so = mDataOffset;
289
  PRInt64 eo = mDecoder->GetCurrentStream()->GetLength();
290
  PRInt64 st = aStartTime;
291
  PRInt64 et = aEndTime;
292
  for (PRUint32 i = 0; i < ranges.Length(); i++) {
293
    const ByteRange &r = ranges[i];
294
    if (r.mTimeStart < aTarget) {
295
      so = r.mOffsetStart;
296
      st = r.mTimeStart;
297
    }
298
    if (r.mTimeEnd >= aTarget && r.mTimeEnd < et) {
299
      eo = r.mOffsetEnd;
300
      et = r.mTimeEnd;
301
    }
302
303
    if (r.mTimeStart < aTarget && aTarget <= r.mTimeEnd) {
304
      // Target lies exactly in this range.
305
      return ranges[i];
306
    }
307
  }
308
  return aExact ? ByteRange() : ByteRange(so, eo, st, et);
309
}
310
311
VideoData* nsBuiltinDecoderReader::FindStartTime(PRInt64 aOffset,
233
VideoData* nsBuiltinDecoderReader::FindStartTime(PRInt64 aOffset,
312
                                                 PRInt64& aOutStartTime)
234
                                                 PRInt64& aOutStartTime)
313
{
235
{
314
  NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread.");
236
  NS_ASSERTION(mDecoder->OnStateMachineThread(), "Should be on state machine thread.");
315
237
316
  if (NS_FAILED(ResetDecode())) {
238
  if (NS_FAILED(ResetDecode())) {
317
    return nsnull;
239
    return nsnull;
318
  }
240
  }
(-)a/content/media/nsBuiltinDecoderReader.h (-53 lines)
Line     Link Here 
 Lines 400-448   template <class T> class MediaQueue : pr Link Here 
400
private:
400
private:
401
  Monitor mMonitor;
401
  Monitor mMonitor;
402
402
403
  // PR_TRUE when we've decoded the last frame of data in the
403
  // PR_TRUE when we've decoded the last frame of data in the
404
  // bitstream for which we're queueing sample-data.
404
  // bitstream for which we're queueing sample-data.
405
  PRBool mEndOfStream;
405
  PRBool mEndOfStream;
406
};
406
};
407
407
408
// Represents a section of contiguous media, with a start and end offset,
409
// and the timestamps of the start and end of that range. Used to denote the
410
// extremities of a range to seek in.
411
class ByteRange {
412
public:
413
  ByteRange()
414
    : mOffsetStart(0),
415
      mOffsetEnd(0),
416
      mTimeStart(0),
417
      mTimeEnd(0)
418
  {}
419
420
  ByteRange(PRInt64 aOffsetStart,
421
            PRInt64 aOffsetEnd,
422
            PRInt64 aTimeStart,
423
            PRInt64 aTimeEnd)
424
    : mOffsetStart(aOffsetStart),
425
      mOffsetEnd(aOffsetEnd),
426
      mTimeStart(aTimeStart),
427
      mTimeEnd(aTimeEnd)
428
  {}
429
430
  PRBool IsNull() const {
431
    return mOffsetStart == 0 &&
432
           mOffsetEnd == 0 &&
433
           mTimeStart == 0 &&
434
           mTimeEnd == 0;
435
  }
436
437
  PRInt64 mOffsetStart, mOffsetEnd; // in bytes.
438
  PRInt64 mTimeStart, mTimeEnd; // in ms.
439
};
440
441
// Encapsulates the decoding and reading of media data. Reading can be done
408
// Encapsulates the decoding and reading of media data. Reading can be done
442
// on either the state machine thread (when loading and seeking) or on
409
// on either the state machine thread (when loading and seeking) or on
443
// the reader thread (when it's reading and decoding). The reader encapsulates
410
// the reader thread (when it's reading and decoding). The reader encapsulates
444
// the reading state and maintains it's own monitor to ensure thread safety
411
// the reading state and maintains it's own monitor to ensure thread safety
445
// and correctness. Never hold the nsBuiltinDecoder's monitor when calling into
412
// and correctness. Never hold the nsBuiltinDecoder's monitor when calling into
446
// this class.
413
// this class.
447
class nsBuiltinDecoderReader : public nsRunnable {
414
class nsBuiltinDecoderReader : public nsRunnable {
448
public:
415
public:
 Lines 536-571   protected: Link Here 
536
503
537
  // Wrapper so that DecodeVideoFrame(PRBool&,PRInt64) can be called from
504
  // Wrapper so that DecodeVideoFrame(PRBool&,PRInt64) can be called from
538
  // DecodeToFirstData().
505
  // DecodeToFirstData().
539
  PRBool DecodeVideoFrame() {
506
  PRBool DecodeVideoFrame() {
540
    PRBool f = PR_FALSE;
507
    PRBool f = PR_FALSE;
541
    return DecodeVideoFrame(f, 0);
508
    return DecodeVideoFrame(f, 0);
542
  }
509
  }
543
510
544
  // Fills aRanges with ByteRanges denoting the sections of the media which
545
  // have been downloaded and are stored in the media cache. The reader
546
  // monitor must must be held with exactly one lock count. The nsMediaStream
547
  // must be pinned while calling this.
548
  nsresult GetBufferedBytes(nsTArray<ByteRange>& aRanges);
549
550
  // Returns the range in which you should perform a seek bisection if
551
  // you wish to seek to aTarget ms, given the known (buffered) byte ranges
552
  // in aRanges. If aExact is PR_TRUE, we only return an exact copy of a
553
  // range in which aTarget lies, or a null range if aTarget isn't contained
554
  // in any of the (buffered) ranges. Otherwise, when aExact is PR_FALSE,
555
  // we'll construct the smallest possible range we can, based on the times
556
  // and byte offsets known in aRanges. We can then use this to minimize our
557
  // bisection's search space when the target isn't in a known buffered range.
558
  ByteRange GetSeekRange(const nsTArray<ByteRange>& aRanges,
559
                         PRInt64 aTarget,
560
                         PRInt64 aStartTime,
561
                         PRInt64 aEndTime,
562
                         PRBool aExact);
563
564
  // The lock which we hold whenever we read or decode. This ensures the thread
511
  // The lock which we hold whenever we read or decode. This ensures the thread
565
  // safety of the reader and its data fields.
512
  // safety of the reader and its data fields.
566
  Monitor mMonitor;
513
  Monitor mMonitor;
567
514
568
  // Reference to the owning decoder object. Do not hold the
515
  // Reference to the owning decoder object. Do not hold the
569
  // reader's monitor when accessing this.
516
  // reader's monitor when accessing this.
570
  nsBuiltinDecoder* mDecoder;
517
  nsBuiltinDecoder* mDecoder;
571
518
(-)a/content/media/ogg/nsOggReader.cpp (-14 / +91 lines)
Line     Link Here 
 Lines 1018-1033   PRInt64 nsOggReader::FindEndTime(PRInt64 Link Here 
1018
    }
1018
    }
1019
  }
1019
  }
1020
1020
1021
  ogg_sync_reset(aState);
1021
  ogg_sync_reset(aState);
1022
1022
1023
  return endTime;
1023
  return endTime;
1024
}
1024
}
1025
1025
1026
nsresult nsOggReader::GetSeekRanges(nsTArray<SeekRange>& aRanges)
1027
{
1028
  NS_ASSERTION(mDecoder->OnStateMachineThread(),
1029
               "Should be on state machine thread.");
1030
  mMonitor.AssertCurrentThreadIn();
1031
  PRInt64 startOffset = mDataOffset;
1032
  nsMediaStream* stream = mDecoder->GetCurrentStream();
1033
  while (PR_TRUE) {
1034
    PRInt64 endOffset = stream->GetCachedDataEnd(startOffset);
1035
    if (endOffset == startOffset) {
1036
      // Uncached at startOffset.
1037
      endOffset = stream->GetNextCachedData(startOffset);
1038
      if (endOffset == -1) {
1039
        // Uncached at startOffset until endOffset of stream, or we're at
1040
        // the end of stream.
1041
        break;
1042
      }
1043
    } else {
1044
      // Bytes [startOffset..endOffset] are cached.
1045
      PRInt64 startTime = -1;
1046
      PRInt64 endTime = -1;
1047
      if (NS_FAILED(ResetDecode())) {
1048
        return NS_ERROR_FAILURE;
1049
      }
1050
      FindStartTime(startOffset, startTime);
1051
      if (startTime != -1 &&
1052
          ((endTime = FindEndTime(endOffset)) != -1))
1053
      {
1054
        NS_ASSERTION(startOffset < endOffset,
1055
                     "Start offset must be before end offset");
1056
        NS_ASSERTION(startTime < endTime,
1057
                     "Start time must be before end time");
1058
        aRanges.AppendElement(SeekRange(startOffset,
1059
                                        endOffset,
1060
                                        startTime,
1061
                                        endTime));
1062
      }
1063
    }
1064
    startOffset = endOffset;
1065
  }
1066
  if (NS_FAILED(ResetDecode())) {
1067
    return NS_ERROR_FAILURE;
1068
  }
1069
  return NS_OK;
1070
}
1071
1072
nsOggReader::SeekRange
1073
nsOggReader::SelectSeekRange(const nsTArray<SeekRange>& ranges,
1074
                             PRInt64 aTarget,
1075
                             PRInt64 aStartTime,
1076
                             PRInt64 aEndTime,
1077
                             PRBool aExact)
1078
{
1079
  NS_ASSERTION(mDecoder->OnStateMachineThread(),
1080
               "Should be on state machine thread.");
1081
  PRInt64 so = mDataOffset;
1082
  PRInt64 eo = mDecoder->GetCurrentStream()->GetLength();
1083
  PRInt64 st = aStartTime;
1084
  PRInt64 et = aEndTime;
1085
  for (PRUint32 i = 0; i < ranges.Length(); i++) {
1086
    const SeekRange &r = ranges[i];
1087
    if (r.mTimeStart < aTarget) {
1088
      so = r.mOffsetStart;
1089
      st = r.mTimeStart;
1090
    }
1091
    if (r.mTimeEnd >= aTarget && r.mTimeEnd < et) {
1092
      eo = r.mOffsetEnd;
1093
      et = r.mTimeEnd;
1094
    }
1095
1096
    if (r.mTimeStart < aTarget && aTarget <= r.mTimeEnd) {
1097
      // Target lies exactly in this range.
1098
      return ranges[i];
1099
    }
1100
  }
1101
  return aExact ? SeekRange() : SeekRange(so, eo, st, et);
1102
}
1103
1026
nsOggReader::IndexedSeekResult nsOggReader::RollbackIndexedSeek(PRInt64 aOffset)
1104
nsOggReader::IndexedSeekResult nsOggReader::RollbackIndexedSeek(PRInt64 aOffset)
1027
{
1105
{
1028
  mSkeletonState->Deactivate();
1106
  mSkeletonState->Deactivate();
1029
  nsMediaStream* stream = mDecoder->GetCurrentStream();
1107
  nsMediaStream* stream = mDecoder->GetCurrentStream();
1030
  NS_ENSURE_TRUE(stream != nsnull, SEEK_FATAL_ERROR);
1108
  NS_ENSURE_TRUE(stream != nsnull, SEEK_FATAL_ERROR);
1031
  nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
1109
  nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
1032
  NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
1110
  NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
1033
  return SEEK_INDEX_FAIL;
1111
  return SEEK_INDEX_FAIL;
 Lines 1113-1130   nsOggReader::IndexedSeekResult nsOggRead Link Here 
1113
  }      
1191
  }      
1114
  mPageOffset = keyframe.mKeyPoint.mOffset + page.header_len + page.body_len;
1192
  mPageOffset = keyframe.mKeyPoint.mOffset + page.header_len + page.body_len;
1115
  return SEEK_OK;
1193
  return SEEK_OK;
1116
}
1194
}
1117
1195
1118
nsresult nsOggReader::SeekInBufferedRange(PRInt64 aTarget,
1196
nsresult nsOggReader::SeekInBufferedRange(PRInt64 aTarget,
1119
                                          PRInt64 aStartTime,
1197
                                          PRInt64 aStartTime,
1120
                                          PRInt64 aEndTime,
1198
                                          PRInt64 aEndTime,
1121
                                          const nsTArray<ByteRange>& aRanges,
1199
                                          const nsTArray<SeekRange>& aRanges,
1122
                                          const ByteRange& aRange)
1200
                                          const SeekRange& aRange)
1123
{
1201
{
1124
  LOG(PR_LOG_DEBUG, ("%p Seeking in buffered data to %lldms using bisection search", mDecoder, aTarget));
1202
  LOG(PR_LOG_DEBUG, ("%p Seeking in buffered data to %lldms using bisection search", mDecoder, aTarget));
1125
1203
1126
  // We know the exact byte range in which the target must lie. It must
1204
  // We know the exact byte range in which the target must lie. It must
1127
  // be buffered in the media cache. Seek there.
1205
  // be buffered in the media cache. Seek there.
1128
  nsresult res = SeekBisection(aTarget, aRange, 0);
1206
  nsresult res = SeekBisection(aTarget, aRange, 0);
1129
  if (NS_FAILED(res) || !HasVideo()) {
1207
  if (NS_FAILED(res) || !HasVideo()) {
1130
    return res;
1208
    return res;
 Lines 1151-1171   nsresult nsOggReader::SeekInBufferedRang Link Here 
1151
    // First decoded frame isn't a keyframe, seek back to previous keyframe,
1229
    // First decoded frame isn't a keyframe, seek back to previous keyframe,
1152
    // otherwise we'll get visual artifacts.
1230
    // otherwise we'll get visual artifacts.
1153
    NS_ASSERTION(video->mTimecode != -1, "Must have a granulepos");
1231
    NS_ASSERTION(video->mTimecode != -1, "Must have a granulepos");
1154
    int shift = mTheoraState->mInfo.keyframe_granule_shift;
1232
    int shift = mTheoraState->mInfo.keyframe_granule_shift;
1155
    PRInt64 keyframeGranulepos = (video->mTimecode >> shift) << shift;
1233
    PRInt64 keyframeGranulepos = (video->mTimecode >> shift) << shift;
1156
    PRInt64 keyframeTime = mTheoraState->StartTime(keyframeGranulepos);
1234
    PRInt64 keyframeTime = mTheoraState->StartTime(keyframeGranulepos);
1157
    SEEK_LOG(PR_LOG_DEBUG, ("Keyframe for %lld is at %lld, seeking back to it",
1235
    SEEK_LOG(PR_LOG_DEBUG, ("Keyframe for %lld is at %lld, seeking back to it",
1158
                            video->mTime, keyframeTime));
1236
                            video->mTime, keyframeTime));
1159
    ByteRange k = GetSeekRange(aRanges,
1237
    SeekRange k = SelectSeekRange(aRanges,
1160
                               keyframeTime,
1238
                                  keyframeTime,
1161
                               aStartTime,
1239
                                  aStartTime,
1162
                               aEndTime,
1240
                                  aEndTime,
1163
                               PR_FALSE);
1241
                                  PR_FALSE);
1164
    res = SeekBisection(keyframeTime, k, SEEK_FUZZ_MS);
1242
    res = SeekBisection(keyframeTime, k, SEEK_FUZZ_MS);
1165
    NS_ASSERTION(mTheoraGranulepos == -1, "SeekBisection must reset Theora decode");
1243
    NS_ASSERTION(mTheoraGranulepos == -1, "SeekBisection must reset Theora decode");
1166
    NS_ASSERTION(mVorbisGranulepos == -1, "SeekBisection must reset Vorbis decode");
1244
    NS_ASSERTION(mVorbisGranulepos == -1, "SeekBisection must reset Vorbis decode");
1167
  }
1245
  }
1168
  return res;
1246
  return res;
1169
}
1247
}
1170
1248
1171
PRBool nsOggReader::CanDecodeToTarget(PRInt64 aTarget,
1249
PRBool nsOggReader::CanDecodeToTarget(PRInt64 aTarget,
 Lines 1177-1193   PRBool nsOggReader::CanDecodeToTarget(PR Link Here 
1177
  PRInt64 margin = HasVideo() ? mTheoraState->MaxKeyframeOffset() : SEEK_DECODE_MARGIN;
1255
  PRInt64 margin = HasVideo() ? mTheoraState->MaxKeyframeOffset() : SEEK_DECODE_MARGIN;
1178
  return aTarget >= aCurrentTime &&
1256
  return aTarget >= aCurrentTime &&
1179
         aTarget - aCurrentTime < margin;
1257
         aTarget - aCurrentTime < margin;
1180
}
1258
}
1181
1259
1182
nsresult nsOggReader::SeekInUnbuffered(PRInt64 aTarget,
1260
nsresult nsOggReader::SeekInUnbuffered(PRInt64 aTarget,
1183
                                       PRInt64 aStartTime,
1261
                                       PRInt64 aStartTime,
1184
                                       PRInt64 aEndTime,
1262
                                       PRInt64 aEndTime,
1185
                                       const nsTArray<ByteRange>& aRanges)
1263
                                       const nsTArray<SeekRange>& aRanges)
1186
{
1264
{
1187
  LOG(PR_LOG_DEBUG, ("%p Seeking in unbuffered data to %lldms using bisection search", mDecoder, aTarget));
1265
  LOG(PR_LOG_DEBUG, ("%p Seeking in unbuffered data to %lldms using bisection search", mDecoder, aTarget));
1188
  
1266
  
1189
  // If we've got an active Theora bitstream, determine the maximum possible
1267
  // If we've got an active Theora bitstream, determine the maximum possible
1190
  // time in ms which a keyframe could be before a given interframe. We
1268
  // time in ms which a keyframe could be before a given interframe. We
1191
  // subtract this from our seek target, seek to the new target, and then
1269
  // subtract this from our seek target, seek to the new target, and then
1192
  // will decode forward to the original seek target. We should encounter a
1270
  // will decode forward to the original seek target. We should encounter a
1193
  // keyframe in that interval. This prevents us from needing to run two
1271
  // keyframe in that interval. This prevents us from needing to run two
 Lines 1200-1216   nsresult nsOggReader::SeekInUnbuffered(P Link Here 
1200
  // keyframe).
1278
  // keyframe).
1201
  PRInt64 keyframeOffsetMs = 0;
1279
  PRInt64 keyframeOffsetMs = 0;
1202
  if (HasVideo() && mTheoraState) {
1280
  if (HasVideo() && mTheoraState) {
1203
    keyframeOffsetMs = mTheoraState->MaxKeyframeOffset();
1281
    keyframeOffsetMs = mTheoraState->MaxKeyframeOffset();
1204
  }
1282
  }
1205
  PRInt64 seekTarget = NS_MAX(aStartTime, aTarget - keyframeOffsetMs);
1283
  PRInt64 seekTarget = NS_MAX(aStartTime, aTarget - keyframeOffsetMs);
1206
  // Minimize the bisection search space using the known timestamps from the
1284
  // Minimize the bisection search space using the known timestamps from the
1207
  // buffered ranges.
1285
  // buffered ranges.
1208
  ByteRange k = GetSeekRange(aRanges, seekTarget, aStartTime, aEndTime, PR_FALSE);
1286
  SeekRange k = SelectSeekRange(aRanges, seekTarget, aStartTime, aEndTime, PR_FALSE);
1209
  nsresult res = SeekBisection(seekTarget, k, SEEK_FUZZ_MS);
1287
  nsresult res = SeekBisection(seekTarget, k, SEEK_FUZZ_MS);
1210
  NS_ASSERTION(mTheoraGranulepos == -1, "SeekBisection must reset Theora decode");
1288
  NS_ASSERTION(mTheoraGranulepos == -1, "SeekBisection must reset Theora decode");
1211
  NS_ASSERTION(mVorbisGranulepos == -1, "SeekBisection must reset Vorbis decode");
1289
  NS_ASSERTION(mVorbisGranulepos == -1, "SeekBisection must reset Vorbis decode");
1212
  return res;
1290
  return res;
1213
}
1291
}
1214
1292
1215
nsresult nsOggReader::Seek(PRInt64 aTarget,
1293
nsresult nsOggReader::Seek(PRInt64 aTarget,
1216
                           PRInt64 aStartTime,
1294
                           PRInt64 aStartTime,
 Lines 1246-1267   nsresult nsOggReader::Seek(PRInt64 aTarg Link Here 
1246
        "will just decode to it", mDecoder, aCurrentTime, aTarget));
1324
        "will just decode to it", mDecoder, aCurrentTime, aTarget));
1247
  } else {
1325
  } else {
1248
    IndexedSeekResult sres = SeekToKeyframeUsingIndex(aTarget);
1326
    IndexedSeekResult sres = SeekToKeyframeUsingIndex(aTarget);
1249
    NS_ENSURE_TRUE(sres != SEEK_FATAL_ERROR, NS_ERROR_FAILURE);
1327
    NS_ENSURE_TRUE(sres != SEEK_FATAL_ERROR, NS_ERROR_FAILURE);
1250
    if (sres == SEEK_INDEX_FAIL) {
1328
    if (sres == SEEK_INDEX_FAIL) {
1251
      // No index or other non-fatal index-related failure. Try to seek
1329
      // No index or other non-fatal index-related failure. Try to seek
1252
      // using a bisection search. Determine the already downloaded data
1330
      // using a bisection search. Determine the already downloaded data
1253
      // in the media cache, so we can try to seek in the cached data first.
1331
      // in the media cache, so we can try to seek in the cached data first.
1254
      nsAutoTArray<ByteRange, 16> ranges;
1332
      nsAutoTArray<SeekRange, 16> ranges;
1255
      res = GetBufferedBytes(ranges);
1333
      res = GetSeekRanges(ranges);
1256
      NS_ENSURE_SUCCESS(res,res);
1334
      NS_ENSURE_SUCCESS(res,res);
1257
1335
1258
      // Figure out if the seek target lies in a buffered range.
1336
      // Figure out if the seek target lies in a buffered range.
1259
      ByteRange r = GetSeekRange(ranges, aTarget, aStartTime, aEndTime, PR_TRUE);
1337
      SeekRange r = SelectSeekRange(ranges, aTarget, aStartTime, aEndTime, PR_TRUE);
1260
1338
1261
      if (!r.IsNull()) {
1339
      if (!r.IsNull()) {
1262
        // We know the buffered range in which the seek target lies, do a
1340
        // We know the buffered range in which the seek target lies, do a
1263
        // bisection search in that buffered range.
1341
        // bisection search in that buffered range.
1264
        res = SeekInBufferedRange(aTarget, aStartTime, aEndTime, ranges, r);
1342
        res = SeekInBufferedRange(aTarget, aStartTime, aEndTime, ranges, r);
1265
        NS_ENSURE_SUCCESS(res,res);
1343
        NS_ENSURE_SUCCESS(res,res);
1266
      } else {
1344
      } else {
1267
        // The target doesn't lie in a buffered range. Perform a bisection
1345
        // The target doesn't lie in a buffered range. Perform a bisection
 Lines 1340-1356   PageSync(nsMediaStream* aStream, Link Here 
1340
      continue;
1418
      continue;
1341
    }
1419
    }
1342
  }
1420
  }
1343
  
1421
  
1344
  return PAGE_SYNC_OK;
1422
  return PAGE_SYNC_OK;
1345
}
1423
}
1346
1424
1347
nsresult nsOggReader::SeekBisection(PRInt64 aTarget,
1425
nsresult nsOggReader::SeekBisection(PRInt64 aTarget,
1348
                                    const ByteRange& aRange,
1426
                                    const SeekRange& aRange,
1349
                                    PRUint32 aFuzz)
1427
                                    PRUint32 aFuzz)
1350
{
1428
{
1351
  NS_ASSERTION(mDecoder->OnStateMachineThread(),
1429
  NS_ASSERTION(mDecoder->OnStateMachineThread(),
1352
               "Should be on state machine thread.");
1430
               "Should be on state machine thread.");
1353
  nsresult res;
1431
  nsresult res;
1354
  nsMediaStream* stream = mDecoder->GetCurrentStream();
1432
  nsMediaStream* stream = mDecoder->GetCurrentStream();
1355
1433
1356
  if (aTarget == aRange.mTimeStart) {
1434
  if (aTarget == aRange.mTimeStart) {
 Lines 1711-1719   PRBool nsOggReader::IsKnownStream(PRUint Link Here 
1711
    PRUint32 serial = mKnownStreams[i];
1789
    PRUint32 serial = mKnownStreams[i];
1712
    if (serial == aSerial) {
1790
    if (serial == aSerial) {
1713
      return PR_TRUE;
1791
      return PR_TRUE;
1714
    }
1792
    }
1715
  }
1793
  }
1716
1794
1717
  return PR_FALSE;
1795
  return PR_FALSE;
1718
}
1796
}
1719
(-)a/content/media/ogg/nsOggReader.h (-6 / +59 lines)
Line     Link Here 
 Lines 116-152   private: Link Here 
116
    SEEK_INDEX_FAIL,  // Failure due to no index, or invalid index.
116
    SEEK_INDEX_FAIL,  // Failure due to no index, or invalid index.
117
    SEEK_FATAL_ERROR  // Error returned by a stream operation.
117
    SEEK_FATAL_ERROR  // Error returned by a stream operation.
118
  };
118
  };
119
  IndexedSeekResult SeekToKeyframeUsingIndex(PRInt64 aTarget);
119
  IndexedSeekResult SeekToKeyframeUsingIndex(PRInt64 aTarget);
120
120
121
  // Rolls back a seek-using-index attempt, returning a failure error code.
121
  // Rolls back a seek-using-index attempt, returning a failure error code.
122
  IndexedSeekResult RollbackIndexedSeek(PRInt64 aOffset);
122
  IndexedSeekResult RollbackIndexedSeek(PRInt64 aOffset);
123
123
124
  // Represents a section of contiguous media, with a start and end offset,
125
  // and the timestamps of the start and end of that range, that is cached.
126
  // Used to denote the extremities of a range in which we can seek quickly
127
  // (because it's cached).
128
  class SeekRange {
129
  public:
130
    SeekRange()
131
      : mOffsetStart(0),
132
        mOffsetEnd(0),
133
        mTimeStart(0),
134
        mTimeEnd(0)
135
    {}
136
137
    SeekRange(PRInt64 aOffsetStart,
138
              PRInt64 aOffsetEnd,
139
              PRInt64 aTimeStart,
140
              PRInt64 aTimeEnd)
141
      : mOffsetStart(aOffsetStart),
142
        mOffsetEnd(aOffsetEnd),
143
        mTimeStart(aTimeStart),
144
        mTimeEnd(aTimeEnd)
145
    {}
146
147
    PRBool IsNull() const {
148
      return mOffsetStart == 0 &&
149
             mOffsetEnd == 0 &&
150
             mTimeStart == 0 &&
151
             mTimeEnd == 0;
152
    }
153
154
    PRInt64 mOffsetStart, mOffsetEnd; // in bytes.
155
    PRInt64 mTimeStart, mTimeEnd; // in ms.
156
  };
157
124
  // Seeks to aTarget ms in the buffered range aRange using bisection search,
158
  // Seeks to aTarget ms in the buffered range aRange using bisection search,
125
  // or to the keyframe prior to aTarget if we have video. aStartTime must be
159
  // or to the keyframe prior to aTarget if we have video. aStartTime must be
126
  // the presentation time at the start of media, and aEndTime the time at
160
  // the presentation time at the start of media, and aEndTime the time at
127
  // end of media. aRanges must be the time/byte ranges buffered in the media
161
  // end of media. aRanges must be the time/byte ranges buffered in the media
128
  // cache as per GetBufferedBytes().
162
  // cache as per GetSeekRanges().
129
  nsresult SeekInBufferedRange(PRInt64 aTarget,
163
  nsresult SeekInBufferedRange(PRInt64 aTarget,
130
                               PRInt64 aStartTime,
164
                               PRInt64 aStartTime,
131
                               PRInt64 aEndTime,
165
                               PRInt64 aEndTime,
132
                               const nsTArray<ByteRange>& aRanges,
166
                               const nsTArray<SeekRange>& aRanges,
133
                               const ByteRange& aRange);
167
                               const SeekRange& aRange);
134
168
135
  // Seeks to before aTarget ms in media using bisection search. If the media
169
  // Seeks to before aTarget ms in media using bisection search. If the media
136
  // has video, this will seek to before the keyframe required to render the
170
  // has video, this will seek to before the keyframe required to render the
137
  // media at aTarget. Will use aRanges in order to narrow the bisection
171
  // media at aTarget. Will use aRanges in order to narrow the bisection
138
  // search space. aStartTime must be the presentation time at the start of
172
  // search space. aStartTime must be the presentation time at the start of
139
  // media, and aEndTime the time at end of media. aRanges must be the time/byte
173
  // media, and aEndTime the time at end of media. aRanges must be the time/byte
140
  // ranges buffered in the media cache as per GetBufferedBytes().
174
  // ranges buffered in the media cache as per GetSeekRanges().
141
  nsresult SeekInUnbuffered(PRInt64 aTarget,
175
  nsresult SeekInUnbuffered(PRInt64 aTarget,
142
                            PRInt64 aStartTime,
176
                            PRInt64 aStartTime,
143
                            PRInt64 aEndTime,
177
                            PRInt64 aEndTime,
144
                            const nsTArray<ByteRange>& aRanges);
178
                            const nsTArray<SeekRange>& aRanges);
145
179
146
  // Get the end time of aEndOffset, without reading before aStartOffset.
180
  // Get the end time of aEndOffset, without reading before aStartOffset.
147
  // This is the playback position we'd reach after playback finished at
181
  // This is the playback position we'd reach after playback finished at
148
  // aEndOffset. If PRBool aCachedDataOnly is PR_TRUE, then we'll only read
182
  // aEndOffset. If PRBool aCachedDataOnly is PR_TRUE, then we'll only read
149
  // from data which is cached in the media cached, otherwise we'll do
183
  // from data which is cached in the media cached, otherwise we'll do
150
  // regular blocking reads from the media stream. If PRBool aCachedDataOnly
184
  // regular blocking reads from the media stream. If PRBool aCachedDataOnly
151
  // is PR_TRUE, and aState is not mOggState, this can safely be called on
185
  // is PR_TRUE, and aState is not mOggState, this can safely be called on
152
  // the main thread, otherwise it must be called on the state machine thread.
186
  // the main thread, otherwise it must be called on the state machine thread.
 Lines 174-196   private: Link Here 
174
208
175
  // Performs a seek bisection to move the media stream's read cursor to the
209
  // Performs a seek bisection to move the media stream's read cursor to the
176
  // last ogg page boundary which has end time before aTarget ms on both the
210
  // last ogg page boundary which has end time before aTarget ms on both the
177
  // Theora and Vorbis bitstreams. Limits its search to data inside aRange;
211
  // Theora and Vorbis bitstreams. Limits its search to data inside aRange;
178
  // i.e. it will only read inside of the aRange's start and end offsets.
212
  // i.e. it will only read inside of the aRange's start and end offsets.
179
  // aFuzz is the number of ms of leniency we'll allow; we'll terminate the
213
  // aFuzz is the number of ms of leniency we'll allow; we'll terminate the
180
  // seek when we land in the range (aTime - aFuzz, aTime) ms.
214
  // seek when we land in the range (aTime - aFuzz, aTime) ms.
181
  nsresult SeekBisection(PRInt64 aTarget,
215
  nsresult SeekBisection(PRInt64 aTarget,
182
                         const ByteRange& aRange,
216
                         const SeekRange& aRange,
183
                         PRUint32 aFuzz);
217
                         PRUint32 aFuzz);
184
218
185
  // Returns true if the serial number is for a stream we encountered
219
  // Returns true if the serial number is for a stream we encountered
186
  // while reading metadata. Call on the main thread only.
220
  // while reading metadata. Call on the main thread only.
187
  PRBool IsKnownStream(PRUint32 aSerial);
221
  PRBool IsKnownStream(PRUint32 aSerial);
188
222
223
  // Fills aRanges with SeekRanges denoting the sections of the media which
224
  // have been downloaded and are stored in the media cache. The reader
225
  // monitor must must be held with exactly one lock count. The nsMediaStream
226
  // must be pinned while calling this.
227
  nsresult GetSeekRanges(nsTArray<SeekRange>& aRanges);
228
229
  // Returns the range in which you should perform a seek bisection if
230
  // you wish to seek to aTarget ms, given the known (buffered) byte ranges
231
  // in aRanges. If aExact is PR_TRUE, we only return an exact copy of a
232
  // range in which aTarget lies, or a null range if aTarget isn't contained
233
  // in any of the (buffered) ranges. Otherwise, when aExact is PR_FALSE,
234
  // we'll construct the smallest possible range we can, based on the times
235
  // and byte offsets known in aRanges. We can then use this to minimize our
236
  // bisection's search space when the target isn't in a known buffered range.
237
  SeekRange SelectSeekRange(const nsTArray<SeekRange>& aRanges,
238
                            PRInt64 aTarget,
239
                            PRInt64 aStartTime,
240
                            PRInt64 aEndTime,
241
                            PRBool aExact);
189
private:
242
private:
190
  // Maps Ogg serialnos to nsOggStreams.
243
  // Maps Ogg serialnos to nsOggStreams.
191
  nsClassHashtable<nsUint32HashKey, nsOggCodecState> mCodecStates;
244
  nsClassHashtable<nsUint32HashKey, nsOggCodecState> mCodecStates;
192
245
193
  // Array of serial numbers of streams that were encountered during
246
  // Array of serial numbers of streams that were encountered during
194
  // initial metadata load. Written on state machine thread during
247
  // initial metadata load. Written on state machine thread during
195
  // metadata loading and read on the main thread only after metadata
248
  // metadata loading and read on the main thread only after metadata
196
  // is loaded.
249
  // is loaded.

Return to bug 639391