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 "MediaSource.h"
8 
9 #if MOZ_AV1
10 #include "AOMDecoder.h"
11 #endif
12 #include "AsyncEventRunner.h"
13 #include "DecoderTraits.h"
14 #include "Benchmark.h"
15 #include "DecoderDoctorDiagnostics.h"
16 #include "MediaContainerType.h"
17 #include "MediaResult.h"
18 #include "MediaSourceDemuxer.h"
19 #include "MediaSourceUtils.h"
20 #include "SourceBuffer.h"
21 #include "SourceBufferList.h"
22 #include "mozilla/ErrorResult.h"
23 #include "mozilla/FloatingPoint.h"
24 #include "mozilla/Preferences.h"
25 #include "mozilla/dom/BindingDeclarations.h"
26 #include "mozilla/dom/HTMLMediaElement.h"
27 #include "mozilla/mozalloc.h"
28 #include "nsDebug.h"
29 #include "nsError.h"
30 #include "nsIRunnable.h"
31 #include "nsIScriptObjectPrincipal.h"
32 #include "nsPIDOMWindow.h"
33 #include "nsMimeTypes.h"
34 #include "nsString.h"
35 #include "nsThreadUtils.h"
36 #include "mozilla/Logging.h"
37 #include "nsServiceManagerUtils.h"
38 #include "mozilla/gfx/gfxVars.h"
39 #include "mozilla/Sprintf.h"
40 
41 #ifdef MOZ_WIDGET_ANDROID
42 #include "AndroidBridge.h"
43 #endif
44 
45 struct JSContext;
46 class JSObject;
47 
GetMediaSourceLog()48 mozilla::LogModule* GetMediaSourceLog() {
49   static mozilla::LazyLogModule sLogModule("MediaSource");
50   return sLogModule;
51 }
52 
GetMediaSourceAPILog()53 mozilla::LogModule* GetMediaSourceAPILog() {
54   static mozilla::LazyLogModule sLogModule("MediaSource");
55   return sLogModule;
56 }
57 
58 #define MSE_DEBUG(arg, ...)                                              \
59   DDMOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, "::%s: " arg, \
60             __func__, ##__VA_ARGS__)
61 #define MSE_API(arg, ...)                                                   \
62   DDMOZ_LOG(GetMediaSourceAPILog(), mozilla::LogLevel::Debug, "::%s: " arg, \
63             __func__, ##__VA_ARGS__)
64 
65 // Arbitrary limit.
66 static const unsigned int MAX_SOURCE_BUFFERS = 16;
67 
68 namespace mozilla {
69 
70 // Returns true if we should enable MSE webm regardless of preferences.
71 // 1. If MP4/H264 isn't supported:
72 //   * Windows XP
73 //   * Windows Vista and Server 2008 without the optional "Platform Update
74 //   Supplement"
75 //   * N/KN editions (Europe and Korea) of Windows 7/8/8.1/10 without the
76 //     optional "Windows Media Feature Pack"
77 // 2. If H264 hardware acceleration is not available.
78 // 3. The CPU is considered to be fast enough
IsWebMForced(DecoderDoctorDiagnostics * aDiagnostics)79 static bool IsWebMForced(DecoderDoctorDiagnostics* aDiagnostics) {
80   bool mp4supported = DecoderTraits::IsMP4SupportedType(
81       MediaContainerType(MEDIAMIMETYPE(VIDEO_MP4)), aDiagnostics);
82   bool hwsupported = gfx::gfxVars::CanUseHardwareVideoDecoding();
83 #ifdef MOZ_WIDGET_ANDROID
84   return !mp4supported || !hwsupported || VP9Benchmark::IsVP9DecodeFast() ||
85          java::HardwareCodecCapabilityUtils::HasHWVP9();
86 #else
87   return !mp4supported || !hwsupported || VP9Benchmark::IsVP9DecodeFast();
88 #endif
89 }
90 
91 namespace dom {
92 
93 /* static */
IsTypeSupported(const nsAString & aType,DecoderDoctorDiagnostics * aDiagnostics)94 nsresult MediaSource::IsTypeSupported(const nsAString& aType,
95                                       DecoderDoctorDiagnostics* aDiagnostics) {
96   if (aType.IsEmpty()) {
97     return NS_ERROR_DOM_TYPE_ERR;
98   }
99 
100   Maybe<MediaContainerType> containerType = MakeMediaContainerType(aType);
101   if (!containerType) {
102     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
103   }
104 
105   if (DecoderTraits::CanHandleContainerType(*containerType, aDiagnostics) ==
106       CANPLAY_NO) {
107     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
108   }
109 
110   // Now we know that this media type could be played.
111   // MediaSource imposes extra restrictions, and some prefs.
112   const MediaMIMEType& mimeType = containerType->Type();
113   if (mimeType == MEDIAMIMETYPE("video/mp4") ||
114       mimeType == MEDIAMIMETYPE("audio/mp4")) {
115     if (!Preferences::GetBool("media.mediasource.mp4.enabled", false)) {
116       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
117     }
118     return NS_OK;
119   }
120   if (mimeType == MEDIAMIMETYPE("video/webm")) {
121     if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) ||
122           containerType->ExtendedType().Codecs().Contains(
123               NS_LITERAL_STRING("vp8")) ||
124 #ifdef MOZ_AV1
125           // FIXME: Temporary comparison with the full codecs attribute.
126           // See bug 1377015.
127           AOMDecoder::IsSupportedCodec(
128               containerType->ExtendedType().Codecs().AsString()) ||
129 #endif
130           IsWebMForced(aDiagnostics))) {
131       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
132     }
133     return NS_OK;
134   }
135   if (mimeType == MEDIAMIMETYPE("audio/webm")) {
136     if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) ||
137           Preferences::GetBool("media.mediasource.webm.audio.enabled", true))) {
138       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
139     }
140     return NS_OK;
141   }
142 
143   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
144 }
145 
Constructor(const GlobalObject & aGlobal,ErrorResult & aRv)146 /* static */ already_AddRefed<MediaSource> MediaSource::Constructor(
147     const GlobalObject& aGlobal, ErrorResult& aRv) {
148   nsCOMPtr<nsPIDOMWindowInner> window =
149       do_QueryInterface(aGlobal.GetAsSupports());
150   if (!window) {
151     aRv.Throw(NS_ERROR_UNEXPECTED);
152     return nullptr;
153   }
154 
155   RefPtr<MediaSource> mediaSource = new MediaSource(window);
156   return mediaSource.forget();
157 }
158 
~MediaSource()159 MediaSource::~MediaSource() {
160   MOZ_ASSERT(NS_IsMainThread());
161   MSE_API("");
162   if (mDecoder) {
163     mDecoder->DetachMediaSource();
164   }
165 }
166 
SourceBuffers()167 SourceBufferList* MediaSource::SourceBuffers() {
168   MOZ_ASSERT(NS_IsMainThread());
169   MOZ_ASSERT_IF(mReadyState == MediaSourceReadyState::Closed,
170                 mSourceBuffers->IsEmpty());
171   return mSourceBuffers;
172 }
173 
ActiveSourceBuffers()174 SourceBufferList* MediaSource::ActiveSourceBuffers() {
175   MOZ_ASSERT(NS_IsMainThread());
176   MOZ_ASSERT_IF(mReadyState == MediaSourceReadyState::Closed,
177                 mActiveSourceBuffers->IsEmpty());
178   return mActiveSourceBuffers;
179 }
180 
ReadyState()181 MediaSourceReadyState MediaSource::ReadyState() {
182   MOZ_ASSERT(NS_IsMainThread());
183   return mReadyState;
184 }
185 
Duration()186 double MediaSource::Duration() {
187   MOZ_ASSERT(NS_IsMainThread());
188   if (mReadyState == MediaSourceReadyState::Closed) {
189     return UnspecifiedNaN<double>();
190   }
191   MOZ_ASSERT(mDecoder);
192   return mDecoder->GetDuration();
193 }
194 
SetDuration(double aDuration,ErrorResult & aRv)195 void MediaSource::SetDuration(double aDuration, ErrorResult& aRv) {
196   MOZ_ASSERT(NS_IsMainThread());
197   MSE_API("SetDuration(aDuration=%f, ErrorResult)", aDuration);
198   if (aDuration < 0 || IsNaN(aDuration)) {
199     aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
200     return;
201   }
202   if (mReadyState != MediaSourceReadyState::Open ||
203       mSourceBuffers->AnyUpdating()) {
204     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
205     return;
206   }
207   DurationChange(aDuration, aRv);
208 }
209 
SetDuration(double aDuration)210 void MediaSource::SetDuration(double aDuration) {
211   MOZ_ASSERT(NS_IsMainThread());
212   MSE_API("SetDuration(aDuration=%f)", aDuration);
213   mDecoder->SetMediaSourceDuration(aDuration);
214 }
215 
AddSourceBuffer(const nsAString & aType,ErrorResult & aRv)216 already_AddRefed<SourceBuffer> MediaSource::AddSourceBuffer(
217     const nsAString& aType, ErrorResult& aRv) {
218   MOZ_ASSERT(NS_IsMainThread());
219   DecoderDoctorDiagnostics diagnostics;
220   nsresult rv = IsTypeSupported(aType, &diagnostics);
221   diagnostics.StoreFormatDiagnostics(
222       GetOwner() ? GetOwner()->GetExtantDoc() : nullptr, aType,
223       NS_SUCCEEDED(rv), __func__);
224   MSE_API("AddSourceBuffer(aType=%s)%s", NS_ConvertUTF16toUTF8(aType).get(),
225           rv == NS_OK ? "" : " [not supported]");
226   if (NS_FAILED(rv)) {
227     aRv.Throw(rv);
228     return nullptr;
229   }
230   if (mSourceBuffers->Length() >= MAX_SOURCE_BUFFERS) {
231     aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
232     return nullptr;
233   }
234   if (mReadyState != MediaSourceReadyState::Open) {
235     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
236     return nullptr;
237   }
238   Maybe<MediaContainerType> containerType = MakeMediaContainerType(aType);
239   if (!containerType) {
240     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
241     return nullptr;
242   }
243   RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this, *containerType);
244   if (!sourceBuffer) {
245     aRv.Throw(NS_ERROR_FAILURE);  // XXX need a better error here
246     return nullptr;
247   }
248   mSourceBuffers->Append(sourceBuffer);
249   DDLINKCHILD("sourcebuffer[]", sourceBuffer.get());
250   MSE_DEBUG("sourceBuffer=%p", sourceBuffer.get());
251   return sourceBuffer.forget();
252 }
253 
SourceBufferIsActive(SourceBuffer * aSourceBuffer)254 RefPtr<MediaSource::ActiveCompletionPromise> MediaSource::SourceBufferIsActive(
255     SourceBuffer* aSourceBuffer) {
256   MOZ_ASSERT(NS_IsMainThread());
257   mActiveSourceBuffers->ClearSimple();
258   bool initMissing = false;
259   bool found = false;
260   for (uint32_t i = 0; i < mSourceBuffers->Length(); i++) {
261     SourceBuffer* sourceBuffer = mSourceBuffers->IndexedGetter(i, found);
262     MOZ_ALWAYS_TRUE(found);
263     if (sourceBuffer == aSourceBuffer) {
264       mActiveSourceBuffers->Append(aSourceBuffer);
265     } else if (sourceBuffer->IsActive()) {
266       mActiveSourceBuffers->AppendSimple(sourceBuffer);
267     } else {
268       // Some source buffers haven't yet received an init segment.
269       // There's nothing more we can do at this stage.
270       initMissing = true;
271     }
272   }
273   if (initMissing || !mDecoder) {
274     return ActiveCompletionPromise::CreateAndResolve(true, __func__);
275   }
276 
277   mDecoder->NotifyInitDataArrived();
278 
279   // Add our promise to the queue.
280   // It will be resolved once the HTMLMediaElement modifies its readyState.
281   MozPromiseHolder<ActiveCompletionPromise> holder;
282   RefPtr<ActiveCompletionPromise> promise = holder.Ensure(__func__);
283   mCompletionPromises.AppendElement(Move(holder));
284   return promise;
285 }
286 
CompletePendingTransactions()287 void MediaSource::CompletePendingTransactions() {
288   MOZ_ASSERT(NS_IsMainThread());
289   MSE_DEBUG("Resolving %u promises", unsigned(mCompletionPromises.Length()));
290   for (auto& promise : mCompletionPromises) {
291     promise.Resolve(true, __func__);
292   }
293   mCompletionPromises.Clear();
294 }
295 
RemoveSourceBuffer(SourceBuffer & aSourceBuffer,ErrorResult & aRv)296 void MediaSource::RemoveSourceBuffer(SourceBuffer& aSourceBuffer,
297                                      ErrorResult& aRv) {
298   MOZ_ASSERT(NS_IsMainThread());
299   SourceBuffer* sourceBuffer = &aSourceBuffer;
300   MSE_API("RemoveSourceBuffer(aSourceBuffer=%p)", sourceBuffer);
301   if (!mSourceBuffers->Contains(sourceBuffer)) {
302     aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
303     return;
304   }
305 
306   sourceBuffer->AbortBufferAppend();
307   // TODO:
308   // abort stream append loop (if running)
309 
310   // TODO:
311   // For all sourceBuffer audioTracks, videoTracks, textTracks:
312   //     set sourceBuffer to null
313   //     remove sourceBuffer video, audio, text Tracks from MediaElement tracks
314   //     remove sourceBuffer video, audio, text Tracks and fire "removetrack" at
315   //     affected lists fire "removetrack" at modified MediaElement track lists
316   // If removed enabled/selected, fire "change" at affected MediaElement list.
317   if (mActiveSourceBuffers->Contains(sourceBuffer)) {
318     mActiveSourceBuffers->Remove(sourceBuffer);
319   }
320   mSourceBuffers->Remove(sourceBuffer);
321   DDUNLINKCHILD(sourceBuffer);
322   // TODO: Free all resources associated with sourceBuffer
323 }
324 
EndOfStream(const Optional<MediaSourceEndOfStreamError> & aError,ErrorResult & aRv)325 void MediaSource::EndOfStream(
326     const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv) {
327   MOZ_ASSERT(NS_IsMainThread());
328   MSE_API("EndOfStream(aError=%d)",
329           aError.WasPassed() ? uint32_t(aError.Value()) : 0);
330   if (mReadyState != MediaSourceReadyState::Open ||
331       mSourceBuffers->AnyUpdating()) {
332     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
333     return;
334   }
335 
336   SetReadyState(MediaSourceReadyState::Ended);
337   mSourceBuffers->Ended();
338   if (!aError.WasPassed()) {
339     DurationChange(mSourceBuffers->GetHighestBufferedEndTime(), aRv);
340     // Notify reader that all data is now available.
341     mDecoder->Ended(true);
342     return;
343   }
344   switch (aError.Value()) {
345     case MediaSourceEndOfStreamError::Network:
346       mDecoder->NetworkError(MediaResult(NS_ERROR_FAILURE, "MSE network"));
347       break;
348     case MediaSourceEndOfStreamError::Decode:
349       mDecoder->DecodeError(NS_ERROR_DOM_MEDIA_FATAL_ERR);
350       break;
351     default:
352       aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
353   }
354 }
355 
EndOfStream(const MediaResult & aError)356 void MediaSource::EndOfStream(const MediaResult& aError) {
357   MOZ_ASSERT(NS_IsMainThread());
358   MSE_API("EndOfStream(aError=%s)", aError.ErrorName().get());
359 
360   SetReadyState(MediaSourceReadyState::Ended);
361   mSourceBuffers->Ended();
362   mDecoder->DecodeError(aError);
363 }
364 
IsTypeSupported(const GlobalObject & aOwner,const nsAString & aType)365 /* static */ bool MediaSource::IsTypeSupported(const GlobalObject& aOwner,
366                                                const nsAString& aType) {
367   MOZ_ASSERT(NS_IsMainThread());
368   DecoderDoctorDiagnostics diagnostics;
369   nsresult rv = IsTypeSupported(aType, &diagnostics);
370   nsCOMPtr<nsPIDOMWindowInner> window =
371       do_QueryInterface(aOwner.GetAsSupports());
372   diagnostics.StoreFormatDiagnostics(window ? window->GetExtantDoc() : nullptr,
373                                      aType, NS_SUCCEEDED(rv), __func__);
374   MOZ_LOG(GetMediaSourceAPILog(), mozilla::LogLevel::Debug,
375           ("MediaSource::%s: IsTypeSupported(aType=%s) %s", __func__,
376            NS_ConvertUTF16toUTF8(aType).get(),
377            rv == NS_OK ? "OK" : "[not supported]"));
378   return NS_SUCCEEDED(rv);
379 }
380 
Enabled(JSContext * cx,JSObject * aGlobal)381 /* static */ bool MediaSource::Enabled(JSContext* cx, JSObject* aGlobal) {
382   return Preferences::GetBool("media.mediasource.enabled");
383 }
384 
SetLiveSeekableRange(double aStart,double aEnd,ErrorResult & aRv)385 void MediaSource::SetLiveSeekableRange(double aStart, double aEnd,
386                                        ErrorResult& aRv) {
387   MOZ_ASSERT(NS_IsMainThread());
388 
389   // 1. If the readyState attribute is not "open" then throw an
390   // InvalidStateError exception and abort these steps.
391   if (mReadyState != MediaSourceReadyState::Open) {
392     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
393     return;
394   }
395 
396   // 2. If start is negative or greater than end, then throw a TypeError
397   // exception and abort these steps.
398   if (aStart < 0 || aStart > aEnd) {
399     aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
400     return;
401   }
402 
403   // 3. Set live seekable range to be a new normalized TimeRanges object
404   // containing a single range whose start position is start and end position is
405   // end.
406   mLiveSeekableRange =
407       Some(media::TimeInterval(media::TimeUnit::FromSeconds(aStart),
408                                media::TimeUnit::FromSeconds(aEnd)));
409 }
410 
ClearLiveSeekableRange(ErrorResult & aRv)411 void MediaSource::ClearLiveSeekableRange(ErrorResult& aRv) {
412   MOZ_ASSERT(NS_IsMainThread());
413 
414   // 1. If the readyState attribute is not "open" then throw an
415   // InvalidStateError exception and abort these steps.
416   if (mReadyState != MediaSourceReadyState::Open) {
417     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
418     return;
419   }
420 
421   // 2. If live seekable range contains a range, then set live seekable range to
422   // be a new empty TimeRanges object.
423   mLiveSeekableRange.reset();
424 }
425 
Attach(MediaSourceDecoder * aDecoder)426 bool MediaSource::Attach(MediaSourceDecoder* aDecoder) {
427   MOZ_ASSERT(NS_IsMainThread());
428   MSE_DEBUG("Attach(aDecoder=%p) owner=%p", aDecoder, aDecoder->GetOwner());
429   MOZ_ASSERT(aDecoder);
430   MOZ_ASSERT(aDecoder->GetOwner());
431   if (mReadyState != MediaSourceReadyState::Closed) {
432     return false;
433   }
434   MOZ_ASSERT(!mMediaElement);
435   mMediaElement = aDecoder->GetOwner()->GetMediaElement();
436   MOZ_ASSERT(!mDecoder);
437   mDecoder = aDecoder;
438   mDecoder->AttachMediaSource(this);
439   SetReadyState(MediaSourceReadyState::Open);
440   return true;
441 }
442 
Detach()443 void MediaSource::Detach() {
444   MOZ_ASSERT(NS_IsMainThread());
445   MOZ_RELEASE_ASSERT(mCompletionPromises.IsEmpty());
446   MSE_DEBUG("mDecoder=%p owner=%p", mDecoder.get(),
447             mDecoder ? mDecoder->GetOwner() : nullptr);
448   if (!mDecoder) {
449     MOZ_ASSERT(mReadyState == MediaSourceReadyState::Closed);
450     MOZ_ASSERT(mActiveSourceBuffers->IsEmpty() && mSourceBuffers->IsEmpty());
451     return;
452   }
453   mMediaElement = nullptr;
454   SetReadyState(MediaSourceReadyState::Closed);
455   if (mActiveSourceBuffers) {
456     mActiveSourceBuffers->Clear();
457   }
458   if (mSourceBuffers) {
459     mSourceBuffers->Clear();
460   }
461   mDecoder->DetachMediaSource();
462   mDecoder = nullptr;
463 }
464 
MediaSource(nsPIDOMWindowInner * aWindow)465 MediaSource::MediaSource(nsPIDOMWindowInner* aWindow)
466     : DOMEventTargetHelper(aWindow),
467       mDecoder(nullptr),
468       mPrincipal(nullptr),
469       mAbstractMainThread(
470           GetOwnerGlobal()->AbstractMainThreadFor(TaskCategory::Other)),
471       mReadyState(MediaSourceReadyState::Closed) {
472   MOZ_ASSERT(NS_IsMainThread());
473   mSourceBuffers = new SourceBufferList(this);
474   mActiveSourceBuffers = new SourceBufferList(this);
475 
476   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
477   if (sop) {
478     mPrincipal = sop->GetPrincipal();
479   }
480 
481   MSE_API("MediaSource(aWindow=%p) mSourceBuffers=%p mActiveSourceBuffers=%p",
482           aWindow, mSourceBuffers.get(), mActiveSourceBuffers.get());
483 }
484 
SetReadyState(MediaSourceReadyState aState)485 void MediaSource::SetReadyState(MediaSourceReadyState aState) {
486   MOZ_ASSERT(NS_IsMainThread());
487   MOZ_ASSERT(aState != mReadyState);
488   MSE_DEBUG("SetReadyState(aState=%" PRIu32 ") mReadyState=%" PRIu32,
489             static_cast<uint32_t>(aState), static_cast<uint32_t>(mReadyState));
490 
491   MediaSourceReadyState oldState = mReadyState;
492   mReadyState = aState;
493 
494   if (mReadyState == MediaSourceReadyState::Open &&
495       (oldState == MediaSourceReadyState::Closed ||
496        oldState == MediaSourceReadyState::Ended)) {
497     QueueAsyncSimpleEvent("sourceopen");
498     if (oldState == MediaSourceReadyState::Ended) {
499       // Notify reader that more data may come.
500       mDecoder->Ended(false);
501     }
502     return;
503   }
504 
505   if (mReadyState == MediaSourceReadyState::Ended &&
506       oldState == MediaSourceReadyState::Open) {
507     QueueAsyncSimpleEvent("sourceended");
508     return;
509   }
510 
511   if (mReadyState == MediaSourceReadyState::Closed &&
512       (oldState == MediaSourceReadyState::Open ||
513        oldState == MediaSourceReadyState::Ended)) {
514     QueueAsyncSimpleEvent("sourceclose");
515     return;
516   }
517 
518   NS_WARNING("Invalid MediaSource readyState transition");
519 }
520 
DispatchSimpleEvent(const char * aName)521 void MediaSource::DispatchSimpleEvent(const char* aName) {
522   MOZ_ASSERT(NS_IsMainThread());
523   MSE_API("Dispatch event '%s'", aName);
524   DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
525 }
526 
QueueAsyncSimpleEvent(const char * aName)527 void MediaSource::QueueAsyncSimpleEvent(const char* aName) {
528   MSE_DEBUG("Queuing event '%s'", aName);
529   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
530   mAbstractMainThread->Dispatch(event.forget());
531 }
532 
DurationChange(double aNewDuration,ErrorResult & aRv)533 void MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv) {
534   MOZ_ASSERT(NS_IsMainThread());
535   MSE_DEBUG("DurationChange(aNewDuration=%f)", aNewDuration);
536 
537   // 1. If the current value of duration is equal to new duration, then return.
538   if (mDecoder->GetDuration() == aNewDuration) {
539     return;
540   }
541 
542   // 2. If new duration is less than the highest starting presentation timestamp
543   // of any buffered coded frames for all SourceBuffer objects in sourceBuffers,
544   // then throw an InvalidStateError exception and abort these steps.
545   if (aNewDuration < mSourceBuffers->HighestStartTime()) {
546     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
547     return;
548   }
549 
550   // 3. Let highest end time be the largest track buffer ranges end time across
551   // all the track buffers across all SourceBuffer objects in sourceBuffers.
552   double highestEndTime = mSourceBuffers->HighestEndTime();
553   // 4. If new duration is less than highest end time, then
554   //    4.1 Update new duration to equal highest end time.
555   aNewDuration = std::max(aNewDuration, highestEndTime);
556 
557   // 5. Update the media duration to new duration and run the HTMLMediaElement
558   // duration change algorithm.
559   mDecoder->SetMediaSourceDuration(aNewDuration);
560 }
561 
GetMozDebugReaderData(nsAString & aString)562 void MediaSource::GetMozDebugReaderData(nsAString& aString) {
563   nsAutoCString result;
564   mDecoder->GetMozDebugReaderData(result);
565   aString = NS_ConvertUTF8toUTF16(result);
566 }
567 
GetParentObject() const568 nsPIDOMWindowInner* MediaSource::GetParentObject() const { return GetOwner(); }
569 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)570 JSObject* MediaSource::WrapObject(JSContext* aCx,
571                                   JS::Handle<JSObject*> aGivenProto) {
572   return MediaSourceBinding::Wrap(aCx, this, aGivenProto);
573 }
574 
575 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaSource, DOMEventTargetHelper,
576                                    mMediaElement, mSourceBuffers,
577                                    mActiveSourceBuffers)
578 
579 NS_IMPL_ADDREF_INHERITED(MediaSource, DOMEventTargetHelper)
580 NS_IMPL_RELEASE_INHERITED(MediaSource, DOMEventTargetHelper)
581 
582 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaSource)
583   NS_INTERFACE_MAP_ENTRY(mozilla::dom::MediaSource)
584 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
585 
586 #undef MSE_DEBUG
587 #undef MSE_API
588 
589 }  // namespace dom
590 
591 }  // namespace mozilla
592