content/media/mediasource/SourceBuffer.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "SourceBuffer.h"
michael@0 7
michael@0 8 #include "AsyncEventRunner.h"
michael@0 9 #include "DecoderTraits.h"
michael@0 10 #include "MediaDecoder.h"
michael@0 11 #include "MediaSourceDecoder.h"
michael@0 12 #include "SourceBufferResource.h"
michael@0 13 #include "mozilla/ErrorResult.h"
michael@0 14 #include "mozilla/FloatingPoint.h"
michael@0 15 #include "mozilla/dom/MediaSourceBinding.h"
michael@0 16 #include "mozilla/dom/TimeRanges.h"
michael@0 17 #include "nsError.h"
michael@0 18 #include "nsIEventTarget.h"
michael@0 19 #include "nsIRunnable.h"
michael@0 20 #include "nsThreadUtils.h"
michael@0 21 #include "prlog.h"
michael@0 22 #include "SubBufferDecoder.h"
michael@0 23
michael@0 24 struct JSContext;
michael@0 25 class JSObject;
michael@0 26
michael@0 27 #ifdef PR_LOGGING
michael@0 28 extern PRLogModuleInfo* gMediaSourceLog;
michael@0 29 #define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
michael@0 30 #else
michael@0 31 #define MSE_DEBUG(...)
michael@0 32 #endif
michael@0 33
michael@0 34 namespace mozilla {
michael@0 35
michael@0 36 class MediaResource;
michael@0 37 class ReentrantMonitor;
michael@0 38
michael@0 39 namespace layers {
michael@0 40
michael@0 41 class ImageContainer;
michael@0 42
michael@0 43 } // namespace layers
michael@0 44
michael@0 45 ReentrantMonitor&
michael@0 46 SubBufferDecoder::GetReentrantMonitor()
michael@0 47 {
michael@0 48 return mParentDecoder->GetReentrantMonitor();
michael@0 49 }
michael@0 50
michael@0 51 bool
michael@0 52 SubBufferDecoder::OnStateMachineThread() const
michael@0 53 {
michael@0 54 return mParentDecoder->OnStateMachineThread();
michael@0 55 }
michael@0 56
michael@0 57 bool
michael@0 58 SubBufferDecoder::OnDecodeThread() const
michael@0 59 {
michael@0 60 return mParentDecoder->OnDecodeThread();
michael@0 61 }
michael@0 62
michael@0 63 SourceBufferResource*
michael@0 64 SubBufferDecoder::GetResource() const
michael@0 65 {
michael@0 66 return static_cast<SourceBufferResource*>(mResource.get());
michael@0 67 }
michael@0 68
michael@0 69 void
michael@0 70 SubBufferDecoder::SetMediaDuration(int64_t aDuration)
michael@0 71 {
michael@0 72 mMediaDuration = aDuration;
michael@0 73 }
michael@0 74
michael@0 75 void
michael@0 76 SubBufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
michael@0 77 {
michael@0 78 //mParentDecoder->UpdateEstimatedMediaDuration(aDuration);
michael@0 79 }
michael@0 80
michael@0 81 void
michael@0 82 SubBufferDecoder::SetMediaSeekable(bool aMediaSeekable)
michael@0 83 {
michael@0 84 //mParentDecoder->SetMediaSeekable(aMediaSeekable);
michael@0 85 }
michael@0 86
michael@0 87 void
michael@0 88 SubBufferDecoder::SetTransportSeekable(bool aTransportSeekable)
michael@0 89 {
michael@0 90 //mParentDecoder->SetTransportSeekable(aTransportSeekable);
michael@0 91 }
michael@0 92
michael@0 93 layers::ImageContainer*
michael@0 94 SubBufferDecoder::GetImageContainer()
michael@0 95 {
michael@0 96 return mParentDecoder->GetImageContainer();
michael@0 97 }
michael@0 98
michael@0 99 MediaDecoderOwner*
michael@0 100 SubBufferDecoder::GetOwner()
michael@0 101 {
michael@0 102 return mParentDecoder->GetOwner();
michael@0 103 }
michael@0 104
michael@0 105 int64_t
michael@0 106 SubBufferDecoder::ConvertToByteOffset(double aTime)
michael@0 107 {
michael@0 108 // Uses a conversion based on (aTime/duration) * length. For the
michael@0 109 // purposes of eviction this should be adequate since we have the
michael@0 110 // byte threshold as well to ensure data actually gets evicted and
michael@0 111 // we ensure we don't evict before the current playable point.
michael@0 112 if (mMediaDuration == -1) {
michael@0 113 return -1;
michael@0 114 }
michael@0 115 int64_t length = GetResource()->GetLength();
michael@0 116 MOZ_ASSERT(length > 0);
michael@0 117 int64_t offset = (aTime / (double(mMediaDuration) / USECS_PER_S)) * length;
michael@0 118 return offset;
michael@0 119 }
michael@0 120
michael@0 121 class ContainerParser {
michael@0 122 public:
michael@0 123 virtual ~ContainerParser() {}
michael@0 124
michael@0 125 virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
michael@0 126 {
michael@0 127 return false;
michael@0 128 }
michael@0 129 };
michael@0 130
michael@0 131 class WebMContainerParser : public ContainerParser {
michael@0 132 public:
michael@0 133 bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
michael@0 134 {
michael@0 135 // XXX: This is overly primitive, needs to collect data as it's appended
michael@0 136 // to the SB and handle, rather than assuming everything is present in a
michael@0 137 // single aData segment.
michael@0 138 // 0x1a45dfa3 // EBML
michael@0 139 // ...
michael@0 140 // DocType == "webm"
michael@0 141 // ...
michael@0 142 // 0x18538067 // Segment (must be "unknown" size)
michael@0 143 // 0x1549a966 // -> Segment Info
michael@0 144 // 0x1654ae6b // -> One or more Tracks
michael@0 145 if (aLength >= 4 &&
michael@0 146 aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) {
michael@0 147 return true;
michael@0 148 }
michael@0 149 return false;
michael@0 150 }
michael@0 151 };
michael@0 152
michael@0 153 namespace dom {
michael@0 154
michael@0 155 void
michael@0 156 SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
michael@0 157 {
michael@0 158 if (!IsAttached() || mUpdating) {
michael@0 159 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 160 return;
michael@0 161 }
michael@0 162 MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
michael@0 163 if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
michael@0 164 mMediaSource->SetReadyState(MediaSourceReadyState::Open);
michael@0 165 }
michael@0 166 // TODO: Test append state.
michael@0 167 // TODO: If aMode is "sequence", set sequence start time.
michael@0 168 mAppendMode = aMode;
michael@0 169 }
michael@0 170
michael@0 171 void
michael@0 172 SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
michael@0 173 {
michael@0 174 if (!IsAttached() || mUpdating) {
michael@0 175 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 176 return;
michael@0 177 }
michael@0 178 MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
michael@0 179 if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
michael@0 180 mMediaSource->SetReadyState(MediaSourceReadyState::Open);
michael@0 181 }
michael@0 182 // TODO: Test append state.
michael@0 183 // TODO: If aMode is "sequence", set sequence start time.
michael@0 184 mTimestampOffset = aTimestampOffset;
michael@0 185 }
michael@0 186
michael@0 187 already_AddRefed<TimeRanges>
michael@0 188 SourceBuffer::GetBuffered(ErrorResult& aRv)
michael@0 189 {
michael@0 190 if (!IsAttached()) {
michael@0 191 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 192 return nullptr;
michael@0 193 }
michael@0 194 nsRefPtr<TimeRanges> ranges = new TimeRanges();
michael@0 195 for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
michael@0 196 nsRefPtr<TimeRanges> r = new TimeRanges();
michael@0 197 mDecoders[i]->GetBuffered(r);
michael@0 198 if (r->Length() > 0) {
michael@0 199 MSE_DEBUG("%p GetBuffered decoder=%u Length=%u Start=%f End=%f", this, i, r->Length(),
michael@0 200 r->GetStartTime(), r->GetEndTime());
michael@0 201 ranges->Add(r->GetStartTime(), r->GetEndTime());
michael@0 202 } else {
michael@0 203 MSE_DEBUG("%p GetBuffered decoder=%u Length=%u", this, i, r->Length());
michael@0 204 }
michael@0 205 }
michael@0 206 ranges->Normalize();
michael@0 207 MSE_DEBUG("%p GetBuffered Length=%u Start=%f End=%f", this, ranges->Length(),
michael@0 208 ranges->GetStartTime(), ranges->GetEndTime());
michael@0 209 return ranges.forget();
michael@0 210 }
michael@0 211
michael@0 212 void
michael@0 213 SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
michael@0 214 {
michael@0 215 if (!IsAttached() || mUpdating) {
michael@0 216 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 217 return;
michael@0 218 }
michael@0 219 if (aAppendWindowStart < 0 || aAppendWindowStart >= mAppendWindowEnd) {
michael@0 220 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 221 return;
michael@0 222 }
michael@0 223 mAppendWindowStart = aAppendWindowStart;
michael@0 224 }
michael@0 225
michael@0 226 void
michael@0 227 SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
michael@0 228 {
michael@0 229 if (!IsAttached() || mUpdating) {
michael@0 230 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 231 return;
michael@0 232 }
michael@0 233 if (IsNaN(aAppendWindowEnd) ||
michael@0 234 aAppendWindowEnd <= mAppendWindowStart) {
michael@0 235 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 236 return;
michael@0 237 }
michael@0 238 mAppendWindowEnd = aAppendWindowEnd;
michael@0 239 }
michael@0 240
michael@0 241 void
michael@0 242 SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
michael@0 243 {
michael@0 244 aData.ComputeLengthAndData();
michael@0 245
michael@0 246 AppendData(aData.Data(), aData.Length(), aRv);
michael@0 247 }
michael@0 248
michael@0 249 void
michael@0 250 SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
michael@0 251 {
michael@0 252 aData.ComputeLengthAndData();
michael@0 253
michael@0 254 AppendData(aData.Data(), aData.Length(), aRv);
michael@0 255 }
michael@0 256
michael@0 257 void
michael@0 258 SourceBuffer::Abort(ErrorResult& aRv)
michael@0 259 {
michael@0 260 MSE_DEBUG("%p Abort()", this);
michael@0 261 if (!IsAttached()) {
michael@0 262 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 263 return;
michael@0 264 }
michael@0 265 if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
michael@0 266 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 267 return;
michael@0 268 }
michael@0 269 if (mUpdating) {
michael@0 270 // TODO: Abort segment parser loop, buffer append, and stream append loop algorithms.
michael@0 271 AbortUpdating();
michael@0 272 }
michael@0 273 // TODO: Run reset parser algorithm.
michael@0 274 mAppendWindowStart = 0;
michael@0 275 mAppendWindowEnd = PositiveInfinity<double>();
michael@0 276
michael@0 277 MSE_DEBUG("%p Abort: Discarding decoders.", this);
michael@0 278 if (mCurrentDecoder) {
michael@0 279 mCurrentDecoder->GetResource()->Ended();
michael@0 280 mCurrentDecoder = nullptr;
michael@0 281 }
michael@0 282 }
michael@0 283
michael@0 284 void
michael@0 285 SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
michael@0 286 {
michael@0 287 MSE_DEBUG("%p Remove(Start=%f End=%f)", this, aStart, aEnd);
michael@0 288 if (!IsAttached() || mUpdating ||
michael@0 289 mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
michael@0 290 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 291 return;
michael@0 292 }
michael@0 293 if (aStart < 0 || aStart > mMediaSource->Duration() ||
michael@0 294 aEnd <= aStart) {
michael@0 295 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 296 return;
michael@0 297 }
michael@0 298 StartUpdating();
michael@0 299 /// TODO: Run coded frame removal algorithm asynchronously (would call StopUpdating()).
michael@0 300 StopUpdating();
michael@0 301 }
michael@0 302
michael@0 303 void
michael@0 304 SourceBuffer::Detach()
michael@0 305 {
michael@0 306 Ended();
michael@0 307 mDecoders.Clear();
michael@0 308 mCurrentDecoder = nullptr;
michael@0 309 mMediaSource = nullptr;
michael@0 310 }
michael@0 311
michael@0 312 void
michael@0 313 SourceBuffer::Ended()
michael@0 314 {
michael@0 315 for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
michael@0 316 mDecoders[i]->GetResource()->Ended();
michael@0 317 }
michael@0 318 }
michael@0 319
michael@0 320 SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
michael@0 321 : DOMEventTargetHelper(aMediaSource->GetParentObject())
michael@0 322 , mMediaSource(aMediaSource)
michael@0 323 , mType(aType)
michael@0 324 , mAppendWindowStart(0)
michael@0 325 , mAppendWindowEnd(PositiveInfinity<double>())
michael@0 326 , mTimestampOffset(0)
michael@0 327 , mAppendMode(SourceBufferAppendMode::Segments)
michael@0 328 , mUpdating(false)
michael@0 329 {
michael@0 330 MOZ_ASSERT(aMediaSource);
michael@0 331 if (mType.EqualsIgnoreCase("video/webm") || mType.EqualsIgnoreCase("audio/webm")) {
michael@0 332 mParser = new WebMContainerParser();
michael@0 333 } else {
michael@0 334 // XXX: Plug in parsers for MPEG4, etc. here.
michael@0 335 mParser = new ContainerParser();
michael@0 336 }
michael@0 337 }
michael@0 338
michael@0 339 already_AddRefed<SourceBuffer>
michael@0 340 SourceBuffer::Create(MediaSource* aMediaSource, const nsACString& aType)
michael@0 341 {
michael@0 342 nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(aMediaSource, aType);
michael@0 343 return sourceBuffer.forget();
michael@0 344 }
michael@0 345
michael@0 346 SourceBuffer::~SourceBuffer()
michael@0 347 {
michael@0 348 for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
michael@0 349 mDecoders[i]->GetResource()->Ended();
michael@0 350 }
michael@0 351 }
michael@0 352
michael@0 353 MediaSource*
michael@0 354 SourceBuffer::GetParentObject() const
michael@0 355 {
michael@0 356 return mMediaSource;
michael@0 357 }
michael@0 358
michael@0 359 JSObject*
michael@0 360 SourceBuffer::WrapObject(JSContext* aCx)
michael@0 361 {
michael@0 362 return SourceBufferBinding::Wrap(aCx, this);
michael@0 363 }
michael@0 364
michael@0 365 void
michael@0 366 SourceBuffer::DispatchSimpleEvent(const char* aName)
michael@0 367 {
michael@0 368 MSE_DEBUG("%p Dispatching event %s to SourceBuffer", this, aName);
michael@0 369 DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
michael@0 370 }
michael@0 371
michael@0 372 void
michael@0 373 SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
michael@0 374 {
michael@0 375 MSE_DEBUG("%p Queuing event %s to SourceBuffer", this, aName);
michael@0 376 nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
michael@0 377 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
michael@0 378 }
michael@0 379
michael@0 380 bool
michael@0 381 SourceBuffer::InitNewDecoder()
michael@0 382 {
michael@0 383 MediaSourceDecoder* parentDecoder = mMediaSource->GetDecoder();
michael@0 384 nsRefPtr<SubBufferDecoder> decoder = parentDecoder->CreateSubDecoder(mType);
michael@0 385 if (!decoder) {
michael@0 386 return false;
michael@0 387 }
michael@0 388 mDecoders.AppendElement(decoder);
michael@0 389 // XXX: At this point, we really want to push through any remaining
michael@0 390 // processing for the old decoder and discard it, rather than hanging on
michael@0 391 // to all of them in mDecoders.
michael@0 392 mCurrentDecoder = decoder;
michael@0 393 return true;
michael@0 394 }
michael@0 395
michael@0 396 void
michael@0 397 SourceBuffer::StartUpdating()
michael@0 398 {
michael@0 399 MOZ_ASSERT(!mUpdating);
michael@0 400 mUpdating = true;
michael@0 401 QueueAsyncSimpleEvent("updatestart");
michael@0 402 }
michael@0 403
michael@0 404 void
michael@0 405 SourceBuffer::StopUpdating()
michael@0 406 {
michael@0 407 MOZ_ASSERT(mUpdating);
michael@0 408 mUpdating = false;
michael@0 409 QueueAsyncSimpleEvent("update");
michael@0 410 QueueAsyncSimpleEvent("updateend");
michael@0 411 }
michael@0 412
michael@0 413 void
michael@0 414 SourceBuffer::AbortUpdating()
michael@0 415 {
michael@0 416 MOZ_ASSERT(mUpdating);
michael@0 417 mUpdating = false;
michael@0 418 QueueAsyncSimpleEvent("abort");
michael@0 419 QueueAsyncSimpleEvent("updateend");
michael@0 420 }
michael@0 421
michael@0 422 void
michael@0 423 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
michael@0 424 {
michael@0 425 MSE_DEBUG("%p AppendBuffer(Data=%u bytes)", this, aLength);
michael@0 426 if (!IsAttached() || mUpdating) {
michael@0 427 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 428 return;
michael@0 429 }
michael@0 430 if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
michael@0 431 mMediaSource->SetReadyState(MediaSourceReadyState::Open);
michael@0 432 }
michael@0 433 // TODO: Run coded frame eviction algorithm.
michael@0 434 // TODO: Test buffer full flag.
michael@0 435 StartUpdating();
michael@0 436 // TODO: Run buffer append algorithm asynchronously (would call StopUpdating()).
michael@0 437 if (mParser->IsInitSegmentPresent(aData, aLength) || !mCurrentDecoder) {
michael@0 438 MSE_DEBUG("%p AppendBuffer: New initialization segment, switching decoders.", this);
michael@0 439 if (mCurrentDecoder) {
michael@0 440 mCurrentDecoder->GetResource()->Ended();
michael@0 441 }
michael@0 442 if (!InitNewDecoder()) {
michael@0 443 aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
michael@0 444 return;
michael@0 445 }
michael@0 446 }
michael@0 447 // XXX: For future reference: NDA call must run on the main thread.
michael@0 448 mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),
michael@0 449 aLength,
michael@0 450 mCurrentDecoder->GetResource()->GetLength());
michael@0 451 mCurrentDecoder->GetResource()->AppendData(aData, aLength);
michael@0 452
michael@0 453 // Eviction uses a byte threshold. If the buffer is greater than the
michael@0 454 // number of bytes then data is evicted. The time range for this
michael@0 455 // eviction is reported back to the media source. It will then
michael@0 456 // evict data before that range across all SourceBuffer's it knows
michael@0 457 // about.
michael@0 458 const int evict_threshold = 1000000;
michael@0 459 bool evicted = mCurrentDecoder->GetResource()->EvictData(evict_threshold);
michael@0 460 if (evicted) {
michael@0 461 double start = 0.0;
michael@0 462 double end = 0.0;
michael@0 463 GetBufferedStartEndTime(&start, &end);
michael@0 464
michael@0 465 // We notify that we've evicted from the time range 0 through to
michael@0 466 // the current start point.
michael@0 467 mMediaSource->NotifyEvicted(0.0, start);
michael@0 468 }
michael@0 469 StopUpdating();
michael@0 470
michael@0 471 // Schedule the state machine thread to ensure playback starts
michael@0 472 // if required when data is appended.
michael@0 473 mMediaSource->GetDecoder()->ScheduleStateMachineThread();
michael@0 474 }
michael@0 475
michael@0 476 void
michael@0 477 SourceBuffer::GetBufferedStartEndTime(double* aStart, double* aEnd)
michael@0 478 {
michael@0 479 ErrorResult dummy;
michael@0 480 nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
michael@0 481 if (!ranges || ranges->Length() == 0) {
michael@0 482 *aStart = *aEnd = 0.0;
michael@0 483 return;
michael@0 484 }
michael@0 485 *aStart = ranges->Start(0, dummy);
michael@0 486 *aEnd = ranges->End(ranges->Length() - 1, dummy);
michael@0 487 }
michael@0 488
michael@0 489 void
michael@0 490 SourceBuffer::Evict(double aStart, double aEnd)
michael@0 491 {
michael@0 492 for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
michael@0 493 // Need to map time to byte offset then evict
michael@0 494 int64_t end = mDecoders[i]->ConvertToByteOffset(aEnd);
michael@0 495 if (end <= 0) {
michael@0 496 NS_WARNING("SourceBuffer::Evict failed");
michael@0 497 continue;
michael@0 498 }
michael@0 499 mDecoders[i]->GetResource()->EvictBefore(end);
michael@0 500 }
michael@0 501 }
michael@0 502
michael@0 503 bool
michael@0 504 SourceBuffer::ContainsTime(double aTime)
michael@0 505 {
michael@0 506 ErrorResult dummy;
michael@0 507 nsRefPtr<TimeRanges> ranges = GetBuffered(dummy);
michael@0 508 if (!ranges || ranges->Length() == 0) {
michael@0 509 return false;
michael@0 510 }
michael@0 511 for (uint32_t i = 0; i < ranges->Length(); ++i) {
michael@0 512 if (aTime >= ranges->Start(i, dummy) &&
michael@0 513 aTime <= ranges->End(i, dummy)) {
michael@0 514 return true;
michael@0 515 }
michael@0 516 }
michael@0 517 return false;
michael@0 518 }
michael@0 519
michael@0 520 NS_IMPL_CYCLE_COLLECTION_INHERITED(SourceBuffer, DOMEventTargetHelper,
michael@0 521 mMediaSource)
michael@0 522
michael@0 523 NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
michael@0 524 NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
michael@0 525
michael@0 526 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer)
michael@0 527 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
michael@0 528
michael@0 529 } // namespace dom
michael@0 530
michael@0 531 } // namespace mozilla

mercurial