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