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