Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "SourceBuffer.h"
8 :
9 : #include "AsyncEventRunner.h"
10 : #include "MediaData.h"
11 : #include "MediaSourceDemuxer.h"
12 : #include "MediaSourceUtils.h"
13 : #include "mozilla/ErrorResult.h"
14 : #include "mozilla/FloatingPoint.h"
15 : #include "mozilla/Preferences.h"
16 : #include "mozilla/dom/MediaSourceBinding.h"
17 : #include "mozilla/dom/TimeRanges.h"
18 : #include "nsError.h"
19 : #include "nsIEventTarget.h"
20 : #include "nsIRunnable.h"
21 : #include "nsThreadUtils.h"
22 : #include "mozilla/Logging.h"
23 : #include <time.h>
24 : #include "TimeUnits.h"
25 :
26 : // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
27 : // GetTickCount() and conflicts with MediaDecoder::GetCurrentTime implementation.
28 : #ifdef GetCurrentTime
29 : #undef GetCurrentTime
30 : #endif
31 :
32 : struct JSContext;
33 : class JSObject;
34 :
35 : extern mozilla::LogModule* GetMediaSourceLog();
36 : extern mozilla::LogModule* GetMediaSourceAPILog();
37 :
38 : #define MSE_DEBUG(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, ("SourceBuffer(%p:%s)::%s: " arg, this, mType.OriginalString().Data(), __func__, ##__VA_ARGS__))
39 : #define MSE_DEBUGV(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Verbose, ("SourceBuffer(%p:%s)::%s: " arg, this, mType.OriginalString().Data(), __func__, ##__VA_ARGS__))
40 : #define MSE_API(arg, ...) MOZ_LOG(GetMediaSourceAPILog(), mozilla::LogLevel::Debug, ("SourceBuffer(%p:%s)::%s: " arg, this, mType.OriginalString().Data(), __func__, ##__VA_ARGS__))
41 :
42 : namespace mozilla {
43 :
44 : using media::TimeUnit;
45 : typedef SourceBufferAttributes::AppendState AppendState;
46 :
47 : namespace dom {
48 :
49 : void
50 0 : SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv)
51 : {
52 0 : MOZ_ASSERT(NS_IsMainThread());
53 0 : MSE_API("SetMode(aMode=%" PRIu32 ")", static_cast<uint32_t>(aMode));
54 0 : if (!IsAttached() || mUpdating) {
55 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
56 0 : return;
57 : }
58 0 : if (mCurrentAttributes.mGenerateTimestamps &&
59 : aMode == SourceBufferAppendMode::Segments) {
60 0 : aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
61 0 : return;
62 : }
63 0 : MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
64 0 : if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
65 0 : mMediaSource->SetReadyState(MediaSourceReadyState::Open);
66 : }
67 0 : if (mCurrentAttributes.GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
68 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
69 0 : return;
70 : }
71 :
72 0 : if (aMode == SourceBufferAppendMode::Sequence) {
73 : // Will set GroupStartTimestamp to GroupEndTimestamp.
74 0 : mCurrentAttributes.RestartGroupStartTimestamp();
75 : }
76 :
77 0 : mCurrentAttributes.SetAppendMode(aMode);
78 : }
79 :
80 : void
81 0 : SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv)
82 : {
83 0 : MOZ_ASSERT(NS_IsMainThread());
84 0 : MSE_API("SetTimestampOffset(aTimestampOffset=%f)", aTimestampOffset);
85 0 : if (!IsAttached() || mUpdating) {
86 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
87 0 : return;
88 : }
89 0 : MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed);
90 0 : if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
91 0 : mMediaSource->SetReadyState(MediaSourceReadyState::Open);
92 : }
93 0 : if (mCurrentAttributes.GetAppendState() == AppendState::PARSING_MEDIA_SEGMENT){
94 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
95 0 : return;
96 : }
97 0 : mCurrentAttributes.SetApparentTimestampOffset(aTimestampOffset);
98 0 : if (mCurrentAttributes.GetAppendMode() == SourceBufferAppendMode::Sequence) {
99 0 : mCurrentAttributes.SetGroupStartTimestamp(mCurrentAttributes.GetTimestampOffset());
100 : }
101 : }
102 :
103 : TimeRanges*
104 0 : SourceBuffer::GetBuffered(ErrorResult& aRv)
105 : {
106 0 : MOZ_ASSERT(NS_IsMainThread());
107 : // http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
108 : // 1. If this object has been removed from the sourceBuffers attribute of the parent media source then throw an InvalidStateError exception and abort these steps.
109 0 : if (!IsAttached()) {
110 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
111 0 : return nullptr;
112 : }
113 0 : bool rangeChanged = true;
114 0 : media::TimeIntervals intersection = mTrackBuffersManager->Buffered();
115 0 : MSE_DEBUGV("intersection=%s", DumpTimeRanges(intersection).get());
116 0 : if (mBuffered) {
117 0 : media::TimeIntervals currentValue(mBuffered);
118 0 : rangeChanged = (intersection != currentValue);
119 0 : MSE_DEBUGV("currentValue=%s", DumpTimeRanges(currentValue).get());
120 : }
121 : // 5. If intersection ranges does not contain the exact same range information as the current value of this attribute, then update the current value of this attribute to intersection ranges.
122 0 : if (rangeChanged) {
123 0 : mBuffered = new TimeRanges(ToSupports(this));
124 0 : intersection.ToTimeRanges(mBuffered);
125 : }
126 : // 6. Return the current value of this attribute.
127 0 : return mBuffered;
128 : }
129 :
130 : media::TimeIntervals
131 0 : SourceBuffer::GetTimeIntervals()
132 : {
133 0 : return mTrackBuffersManager->Buffered();
134 : }
135 :
136 : void
137 0 : SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv)
138 : {
139 0 : MOZ_ASSERT(NS_IsMainThread());
140 0 : MSE_API("SetAppendWindowStart(aAppendWindowStart=%f)", aAppendWindowStart);
141 0 : if (!IsAttached() || mUpdating) {
142 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
143 0 : return;
144 : }
145 0 : if (aAppendWindowStart < 0 ||
146 0 : aAppendWindowStart >= mCurrentAttributes.GetAppendWindowEnd()) {
147 0 : aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
148 0 : return;
149 : }
150 0 : mCurrentAttributes.SetAppendWindowStart(aAppendWindowStart);
151 : }
152 :
153 : void
154 0 : SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv)
155 : {
156 0 : MOZ_ASSERT(NS_IsMainThread());
157 0 : MSE_API("SetAppendWindowEnd(aAppendWindowEnd=%f)", aAppendWindowEnd);
158 0 : if (!IsAttached() || mUpdating) {
159 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
160 0 : return;
161 : }
162 0 : if (IsNaN(aAppendWindowEnd) ||
163 0 : aAppendWindowEnd <= mCurrentAttributes.GetAppendWindowStart()) {
164 0 : aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
165 0 : return;
166 : }
167 0 : mCurrentAttributes.SetAppendWindowEnd(aAppendWindowEnd);
168 : }
169 :
170 : void
171 0 : SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv)
172 : {
173 0 : MOZ_ASSERT(NS_IsMainThread());
174 0 : MSE_API("AppendBuffer(ArrayBuffer)");
175 0 : aData.ComputeLengthAndData();
176 0 : AppendData(aData.Data(), aData.Length(), aRv);
177 0 : }
178 :
179 : void
180 0 : SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv)
181 : {
182 0 : MOZ_ASSERT(NS_IsMainThread());
183 0 : MSE_API("AppendBuffer(ArrayBufferView)");
184 0 : aData.ComputeLengthAndData();
185 0 : AppendData(aData.Data(), aData.Length(), aRv);
186 0 : }
187 :
188 : void
189 0 : SourceBuffer::Abort(ErrorResult& aRv)
190 : {
191 0 : MOZ_ASSERT(NS_IsMainThread());
192 0 : MSE_API("Abort()");
193 0 : if (!IsAttached()) {
194 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
195 0 : return;
196 : }
197 0 : if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
198 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
199 0 : return;
200 : }
201 0 : if (mPendingRemoval.Exists()) {
202 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
203 0 : return;
204 : }
205 0 : AbortBufferAppend();
206 0 : ResetParserState();
207 0 : mCurrentAttributes.SetAppendWindowStart(0);
208 0 : mCurrentAttributes.SetAppendWindowEnd(PositiveInfinity<double>());
209 : }
210 :
211 : void
212 0 : SourceBuffer::AbortBufferAppend()
213 : {
214 0 : if (mUpdating) {
215 0 : mCompletionPromise.DisconnectIfExists();
216 0 : if (mPendingAppend.Exists()) {
217 0 : mPendingAppend.Disconnect();
218 0 : mTrackBuffersManager->AbortAppendData();
219 : }
220 0 : AbortUpdating();
221 : }
222 0 : }
223 :
224 : void
225 0 : SourceBuffer::ResetParserState()
226 : {
227 0 : mTrackBuffersManager->ResetParserState(mCurrentAttributes);
228 0 : }
229 :
230 : void
231 0 : SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
232 : {
233 0 : MOZ_ASSERT(NS_IsMainThread());
234 0 : MSE_API("Remove(aStart=%f, aEnd=%f)", aStart, aEnd);
235 0 : if (!IsAttached()) {
236 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
237 0 : return;
238 : }
239 0 : if (mUpdating) {
240 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
241 0 : return;
242 : }
243 0 : if (IsNaN(mMediaSource->Duration()) ||
244 0 : aStart < 0 || aStart > mMediaSource->Duration() ||
245 0 : aEnd <= aStart || IsNaN(aEnd)) {
246 0 : aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
247 0 : return;
248 : }
249 0 : if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
250 0 : mMediaSource->SetReadyState(MediaSourceReadyState::Open);
251 : }
252 :
253 0 : RangeRemoval(aStart, aEnd);
254 : }
255 :
256 : void
257 0 : SourceBuffer::RangeRemoval(double aStart, double aEnd)
258 : {
259 0 : StartUpdating();
260 :
261 0 : RefPtr<SourceBuffer> self = this;
262 0 : mTrackBuffersManager->RangeRemoval(TimeUnit::FromSeconds(aStart),
263 : TimeUnit::FromSeconds(aEnd))
264 0 : ->Then(mAbstractMainThread, __func__,
265 0 : [self] (bool) {
266 0 : self->mPendingRemoval.Complete();
267 0 : self->StopUpdating();
268 0 : },
269 0 : []() { MOZ_ASSERT(false); })
270 0 : ->Track(mPendingRemoval);
271 0 : }
272 :
273 : void
274 0 : SourceBuffer::Detach()
275 : {
276 0 : MOZ_ASSERT(NS_IsMainThread());
277 0 : MSE_DEBUG("Detach");
278 0 : if (!mMediaSource) {
279 0 : MSE_DEBUG("Already detached");
280 0 : return;
281 : }
282 0 : AbortBufferAppend();
283 0 : if (mTrackBuffersManager) {
284 0 : mTrackBuffersManager->Detach();
285 0 : mMediaSource->GetDecoder()->GetDemuxer()->DetachSourceBuffer(
286 0 : mTrackBuffersManager.get());
287 : }
288 0 : mTrackBuffersManager = nullptr;
289 0 : mMediaSource = nullptr;
290 : }
291 :
292 : void
293 0 : SourceBuffer::Ended()
294 : {
295 0 : MOZ_ASSERT(NS_IsMainThread());
296 0 : MOZ_ASSERT(IsAttached());
297 0 : MSE_DEBUG("Ended");
298 0 : mTrackBuffersManager->Ended();
299 0 : }
300 :
301 0 : SourceBuffer::SourceBuffer(MediaSource* aMediaSource,
302 0 : const MediaContainerType& aType)
303 : : DOMEventTargetHelper(aMediaSource->GetParentObject())
304 : , mMediaSource(aMediaSource)
305 : , mAbstractMainThread(aMediaSource->AbstractMainThread())
306 0 : , mCurrentAttributes(aType.Type() == MEDIAMIMETYPE("audio/mpeg") ||
307 0 : aType.Type() == MEDIAMIMETYPE("audio/aac"))
308 : , mUpdating(false)
309 : , mActive(false)
310 0 : , mType(aType)
311 : {
312 0 : MOZ_ASSERT(NS_IsMainThread());
313 0 : MOZ_ASSERT(aMediaSource);
314 :
315 : mTrackBuffersManager =
316 0 : new TrackBuffersManager(aMediaSource->GetDecoder(), aType);
317 :
318 0 : MSE_DEBUG("Create mTrackBuffersManager=%p",
319 : mTrackBuffersManager.get());
320 :
321 0 : ErrorResult dummy;
322 0 : if (mCurrentAttributes.mGenerateTimestamps) {
323 0 : SetMode(SourceBufferAppendMode::Sequence, dummy);
324 : } else {
325 0 : SetMode(SourceBufferAppendMode::Segments, dummy);
326 : }
327 0 : mMediaSource->GetDecoder()->GetDemuxer()->AttachSourceBuffer(
328 0 : mTrackBuffersManager.get());
329 0 : }
330 :
331 0 : SourceBuffer::~SourceBuffer()
332 : {
333 0 : MOZ_ASSERT(NS_IsMainThread());
334 0 : MOZ_ASSERT(!mMediaSource);
335 0 : MSE_DEBUG("");
336 0 : }
337 :
338 : MediaSource*
339 0 : SourceBuffer::GetParentObject() const
340 : {
341 0 : return mMediaSource;
342 : }
343 :
344 : JSObject*
345 0 : SourceBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
346 : {
347 0 : return SourceBufferBinding::Wrap(aCx, this, aGivenProto);
348 : }
349 :
350 : void
351 0 : SourceBuffer::DispatchSimpleEvent(const char* aName)
352 : {
353 0 : MOZ_ASSERT(NS_IsMainThread());
354 0 : MSE_API("Dispatch event '%s'", aName);
355 0 : DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
356 0 : }
357 :
358 : void
359 0 : SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
360 : {
361 0 : MSE_DEBUG("Queuing event '%s'", aName);
362 0 : nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
363 0 : mAbstractMainThread->Dispatch(event.forget());
364 0 : }
365 :
366 : void
367 0 : SourceBuffer::StartUpdating()
368 : {
369 0 : MOZ_ASSERT(NS_IsMainThread());
370 0 : MOZ_ASSERT(!mUpdating);
371 0 : mUpdating = true;
372 0 : QueueAsyncSimpleEvent("updatestart");
373 0 : }
374 :
375 : void
376 0 : SourceBuffer::StopUpdating()
377 : {
378 0 : MOZ_ASSERT(NS_IsMainThread());
379 0 : if (!mUpdating) {
380 : // The buffer append or range removal algorithm has been interrupted by
381 : // abort().
382 0 : return;
383 : }
384 0 : mUpdating = false;
385 0 : QueueAsyncSimpleEvent("update");
386 0 : QueueAsyncSimpleEvent("updateend");
387 : }
388 :
389 : void
390 0 : SourceBuffer::AbortUpdating()
391 : {
392 0 : MOZ_ASSERT(NS_IsMainThread());
393 0 : mUpdating = false;
394 0 : QueueAsyncSimpleEvent("abort");
395 0 : QueueAsyncSimpleEvent("updateend");
396 0 : }
397 :
398 : void
399 0 : SourceBuffer::CheckEndTime()
400 : {
401 0 : MOZ_ASSERT(NS_IsMainThread());
402 : // Check if we need to update mMediaSource duration
403 0 : double endTime = mCurrentAttributes.GetGroupEndTimestamp().ToSeconds();
404 0 : double duration = mMediaSource->Duration();
405 0 : if (endTime > duration) {
406 0 : mMediaSource->SetDuration(endTime);
407 : }
408 0 : }
409 :
410 : void
411 0 : SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
412 : {
413 0 : MSE_DEBUG("AppendData(aLength=%u)", aLength);
414 :
415 0 : RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aLength, aRv);
416 0 : if (!data) {
417 0 : return;
418 : }
419 0 : StartUpdating();
420 :
421 0 : mTrackBuffersManager->AppendData(data.forget(), mCurrentAttributes)
422 0 : ->Then(mAbstractMainThread, __func__, this,
423 : &SourceBuffer::AppendDataCompletedWithSuccess,
424 0 : &SourceBuffer::AppendDataErrored)
425 0 : ->Track(mPendingAppend);
426 : }
427 :
428 : void
429 0 : SourceBuffer::AppendDataCompletedWithSuccess(const SourceBufferTask::AppendBufferResult& aResult)
430 : {
431 0 : MOZ_ASSERT(mUpdating);
432 0 : mPendingAppend.Complete();
433 :
434 0 : if (aResult.first()) {
435 0 : if (!mActive) {
436 0 : mActive = true;
437 0 : MSE_DEBUG("Init segment received");
438 0 : RefPtr<SourceBuffer> self = this;
439 0 : mMediaSource->SourceBufferIsActive(this)
440 0 : ->Then(mAbstractMainThread, __func__,
441 0 : [self, this]() {
442 0 : MSE_DEBUG("Complete AppendBuffer operation");
443 0 : mCompletionPromise.Complete();
444 0 : StopUpdating();
445 0 : })
446 0 : ->Track(mCompletionPromise);
447 : }
448 : }
449 0 : if (mActive) {
450 : // Tell our parent decoder that we have received new data
451 : // and send progress event.
452 0 : mMediaSource->GetDecoder()->NotifyDataArrived();
453 : }
454 :
455 0 : mCurrentAttributes = aResult.second();
456 :
457 0 : CheckEndTime();
458 :
459 0 : if (!mCompletionPromise.Exists()) {
460 0 : StopUpdating();
461 : }
462 0 : }
463 :
464 : void
465 0 : SourceBuffer::AppendDataErrored(const MediaResult& aError)
466 : {
467 0 : MOZ_ASSERT(mUpdating);
468 0 : mPendingAppend.Complete();
469 :
470 0 : switch (aError.Code()) {
471 : case NS_ERROR_DOM_MEDIA_CANCELED:
472 : // Nothing further to do as the trackbuffer has been shutdown.
473 : // or append was aborted and abort() has handled all the events.
474 0 : break;
475 : default:
476 0 : AppendError(aError);
477 0 : break;
478 : }
479 0 : }
480 :
481 : void
482 0 : SourceBuffer::AppendError(const MediaResult& aDecodeError)
483 : {
484 0 : MOZ_ASSERT(NS_IsMainThread());
485 :
486 0 : ResetParserState();
487 :
488 0 : mUpdating = false;
489 :
490 0 : QueueAsyncSimpleEvent("error");
491 0 : QueueAsyncSimpleEvent("updateend");
492 :
493 0 : MOZ_ASSERT(NS_FAILED(aDecodeError));
494 :
495 0 : mMediaSource->EndOfStream(aDecodeError);
496 0 : }
497 :
498 : already_AddRefed<MediaByteBuffer>
499 0 : SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
500 : {
501 : typedef TrackBuffersManager::EvictDataResult Result;
502 :
503 0 : if (!IsAttached() || mUpdating) {
504 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
505 0 : return nullptr;
506 : }
507 :
508 : // If the HTMLMediaElement.error attribute is not null, then throw an
509 : // InvalidStateError exception and abort these steps.
510 0 : if (!mMediaSource->GetDecoder() ||
511 0 : mMediaSource->GetDecoder()->OwnerHasError()) {
512 0 : MSE_DEBUG("HTMLMediaElement.error is not null");
513 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
514 0 : return nullptr;
515 : }
516 :
517 0 : if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
518 0 : mMediaSource->SetReadyState(MediaSourceReadyState::Open);
519 : }
520 :
521 : // Eviction uses a byte threshold. If the buffer is greater than the
522 : // number of bytes then data is evicted.
523 : // TODO: Drive evictions off memory pressure notifications.
524 : // TODO: Consider a global eviction threshold rather than per TrackBuffer.
525 : // Give a chance to the TrackBuffersManager to evict some data if needed.
526 : Result evicted =
527 0 : mTrackBuffersManager->EvictData(TimeUnit::FromSeconds(mMediaSource->GetDecoder()->GetCurrentTime()),
528 0 : aLength);
529 :
530 : // See if we have enough free space to append our new data.
531 0 : if (evicted == Result::BUFFER_FULL) {
532 0 : aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
533 0 : return nullptr;
534 : }
535 :
536 0 : RefPtr<MediaByteBuffer> data = new MediaByteBuffer();
537 0 : if (!data->AppendElements(aData, aLength, fallible)) {
538 0 : aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
539 0 : return nullptr;
540 : }
541 0 : return data.forget();
542 : }
543 :
544 : double
545 0 : SourceBuffer::GetBufferedStart()
546 : {
547 0 : MOZ_ASSERT(NS_IsMainThread());
548 0 : ErrorResult dummy;
549 0 : RefPtr<TimeRanges> ranges = GetBuffered(dummy);
550 0 : return ranges->Length() > 0 ? ranges->GetStartTime() : 0;
551 : }
552 :
553 : double
554 0 : SourceBuffer::GetBufferedEnd()
555 : {
556 0 : MOZ_ASSERT(NS_IsMainThread());
557 0 : ErrorResult dummy;
558 0 : RefPtr<TimeRanges> ranges = GetBuffered(dummy);
559 0 : return ranges->Length() > 0 ? ranges->GetEndTime() : 0;
560 : }
561 :
562 : double
563 0 : SourceBuffer::HighestStartTime()
564 : {
565 0 : MOZ_ASSERT(NS_IsMainThread());
566 : return mTrackBuffersManager
567 0 : ? mTrackBuffersManager->HighestStartTime().ToSeconds()
568 0 : : 0.0;
569 : }
570 :
571 : double
572 0 : SourceBuffer::HighestEndTime()
573 : {
574 0 : MOZ_ASSERT(NS_IsMainThread());
575 : return mTrackBuffersManager
576 0 : ? mTrackBuffersManager->HighestEndTime().ToSeconds()
577 0 : : 0.0;
578 : }
579 :
580 : NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
581 :
582 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)
583 : // Tell the TrackBuffer to end its current SourceBufferResource.
584 0 : TrackBuffersManager* manager = tmp->mTrackBuffersManager;
585 0 : if (manager) {
586 0 : manager->Detach();
587 : }
588 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource)
589 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mBuffered)
590 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DOMEventTargetHelper)
591 :
592 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SourceBuffer,
593 : DOMEventTargetHelper)
594 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
595 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBuffered)
596 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
597 :
598 0 : NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper)
599 0 : NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper)
600 :
601 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer)
602 0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
603 :
604 : #undef MSE_DEBUG
605 : #undef MSE_DEBUGV
606 : #undef MSE_API
607 :
608 : } // namespace dom
609 :
610 : } // namespace mozilla
|