Coverage Report

Created: 2018-09-25 14:53

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