Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/MediaDecoder.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 "MediaDecoder.h"
8
9
#include "ImageContainer.h"
10
#include "Layers.h"
11
#include "MediaDecoderStateMachine.h"
12
#include "MediaFormatReader.h"
13
#include "MediaResource.h"
14
#include "MediaShutdownManager.h"
15
#include "VideoFrameContainer.h"
16
#include "VideoUtils.h"
17
#include "mozilla/AbstractThread.h"
18
#include "mozilla/FloatingPoint.h"
19
#include "mozilla/MathAlgorithms.h"
20
#include "mozilla/Preferences.h"
21
#include "mozilla/StaticPrefs.h"
22
#include "mozilla/StaticPtr.h"
23
#include "mozilla/Telemetry.h"
24
#include "Visibility.h"
25
#include "mozilla/Unused.h"
26
#include "nsComponentManagerUtils.h"
27
#include "nsContentUtils.h"
28
#include "nsError.h"
29
#include "nsIMemoryReporter.h"
30
#include "nsIObserver.h"
31
#include "nsPrintfCString.h"
32
#include "nsTArray.h"
33
#include <algorithm>
34
#include <limits>
35
36
using namespace mozilla::dom;
37
using namespace mozilla::layers;
38
using namespace mozilla::media;
39
40
namespace mozilla {
41
42
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
43
// GetTickCount() and conflicts with MediaDecoder::GetCurrentTime implementation.
44
#ifdef GetCurrentTime
45
#undef GetCurrentTime
46
#endif
47
48
// avoid redefined macro in unified build
49
#undef LOG
50
#undef DUMP
51
52
LazyLogModule gMediaDecoderLog("MediaDecoder");
53
#define LOG(x, ...)                                                            \
54
0
  DDMOZ_LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
55
56
0
#define DUMP(x, ...) printf_stderr(x "\n", ##__VA_ARGS__)
57
58
#define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
59
60
static const char*
61
ToPlayStateStr(MediaDecoder::PlayState aState)
62
0
{
63
0
  switch (aState) {
64
0
    case MediaDecoder::PLAY_STATE_START:    return "START";
65
0
    case MediaDecoder::PLAY_STATE_LOADING:  return "LOADING";
66
0
    case MediaDecoder::PLAY_STATE_PAUSED:   return "PAUSED";
67
0
    case MediaDecoder::PLAY_STATE_PLAYING:  return "PLAYING";
68
0
    case MediaDecoder::PLAY_STATE_ENDED:    return "ENDED";
69
0
    case MediaDecoder::PLAY_STATE_SHUTDOWN: return "SHUTDOWN";
70
0
    default: MOZ_ASSERT_UNREACHABLE("Invalid playState.");
71
0
  }
72
0
  return "UNKNOWN";
73
0
}
74
75
class MediaMemoryTracker : public nsIMemoryReporter
76
{
77
  virtual ~MediaMemoryTracker();
78
79
  NS_DECL_THREADSAFE_ISUPPORTS
80
  NS_DECL_NSIMEMORYREPORTER
81
82
  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
83
84
  MediaMemoryTracker();
85
  void InitMemoryReporter();
86
87
  static StaticRefPtr<MediaMemoryTracker> sUniqueInstance;
88
89
  static MediaMemoryTracker* UniqueInstance()
90
0
  {
91
0
    if (!sUniqueInstance) {
92
0
      sUniqueInstance = new MediaMemoryTracker();
93
0
      sUniqueInstance->InitMemoryReporter();
94
0
    }
95
0
    return sUniqueInstance;
96
0
  }
97
98
  typedef nsTArray<MediaDecoder*> DecodersArray;
99
  static DecodersArray& Decoders()
100
0
  {
101
0
    return UniqueInstance()->mDecoders;
102
0
  }
103
104
  DecodersArray mDecoders;
105
106
public:
107
  static void AddMediaDecoder(MediaDecoder* aDecoder)
108
0
  {
109
0
    Decoders().AppendElement(aDecoder);
110
0
  }
111
112
  static void RemoveMediaDecoder(MediaDecoder* aDecoder)
113
0
  {
114
0
    DecodersArray& decoders = Decoders();
115
0
    decoders.RemoveElement(aDecoder);
116
0
    if (decoders.IsEmpty()) {
117
0
      sUniqueInstance = nullptr;
118
0
    }
119
0
  }
120
};
121
122
StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
123
124
LazyLogModule gMediaTimerLog("MediaTimer");
125
126
constexpr TimeUnit MediaDecoder::DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED;
127
128
void
129
MediaDecoder::InitStatics()
130
3
{
131
3
  MOZ_ASSERT(NS_IsMainThread());
132
3
  // Eagerly init gMediaDecoderLog to work around bug 1415441.
133
3
  MOZ_LOG(gMediaDecoderLog, LogLevel::Info, ("MediaDecoder::InitStatics"));
134
3
}
135
136
NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
137
138
void
139
MediaDecoder::NotifyOwnerActivityChanged(bool aIsDocumentVisible,
140
                                         Visibility aElementVisibility,
141
                                         bool aIsElementInTree)
142
0
{
143
0
  MOZ_ASSERT(NS_IsMainThread());
144
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
145
0
  AbstractThread::AutoEnter context(AbstractMainThread());
146
0
  SetElementVisibility(aIsDocumentVisible, aElementVisibility, aIsElementInTree);
147
0
148
0
  NotifyCompositor();
149
0
}
150
151
void
152
MediaDecoder::Pause()
153
0
{
154
0
  MOZ_ASSERT(NS_IsMainThread());
155
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
156
0
  AbstractThread::AutoEnter context(AbstractMainThread());
157
0
  if (mPlayState == PLAY_STATE_LOADING || IsEnded()) {
158
0
    mNextState = PLAY_STATE_PAUSED;
159
0
    return;
160
0
  }
161
0
  ChangeState(PLAY_STATE_PAUSED);
162
0
}
163
164
void
165
MediaDecoder::SetVolume(double aVolume)
166
0
{
167
0
  MOZ_ASSERT(NS_IsMainThread());
168
0
  AbstractThread::AutoEnter context(AbstractMainThread());
169
0
  mVolume = aVolume;
170
0
}
171
172
void
173
MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
174
                              TrackID aNextAvailableTrackID,
175
                              bool aFinishWhenEnded)
176
0
{
177
0
  MOZ_ASSERT(NS_IsMainThread());
178
0
  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
179
0
  AbstractThread::AutoEnter context(AbstractMainThread());
180
0
  mDecoderStateMachine->AddOutputStream(
181
0
    aStream, aNextAvailableTrackID, aFinishWhenEnded);
182
0
}
183
184
void
185
MediaDecoder::RemoveOutputStream(MediaStream* aStream)
186
0
{
187
0
  MOZ_ASSERT(NS_IsMainThread());
188
0
  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
189
0
  AbstractThread::AutoEnter context(AbstractMainThread());
190
0
  mDecoderStateMachine->RemoveOutputStream(aStream);
191
0
}
192
193
TrackID
194
MediaDecoder::NextAvailableTrackIDFor(MediaStream* aOutputStream) const
195
0
{
196
0
  MOZ_ASSERT(NS_IsMainThread());
197
0
  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
198
0
  AbstractThread::AutoEnter context(AbstractMainThread());
199
0
  return mDecoderStateMachine->NextAvailableTrackIDFor(aOutputStream);
200
0
}
201
202
double
203
MediaDecoder::GetDuration()
204
0
{
205
0
  MOZ_ASSERT(NS_IsMainThread());
206
0
  AbstractThread::AutoEnter context(AbstractMainThread());
207
0
  return mDuration;
208
0
}
209
210
bool
211
MediaDecoder::IsInfinite() const
212
0
{
213
0
  MOZ_ASSERT(NS_IsMainThread());
214
0
  AbstractThread::AutoEnter context(AbstractMainThread());
215
0
  return mozilla::IsInfinite<double>(mDuration);
216
0
}
217
218
#define INIT_MIRROR(name, val) \
219
  name(mOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Mirror)")
220
#define INIT_CANONICAL(name, val) \
221
  name(mOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Canonical)")
222
223
MediaDecoder::MediaDecoder(MediaDecoderInit& aInit)
224
  : mWatchManager(this, aInit.mOwner->AbstractMainThread())
225
  , mLogicalPosition(0.0)
226
  , mDuration(std::numeric_limits<double>::quiet_NaN())
227
  , mOwner(aInit.mOwner)
228
  , mAbstractMainThread(aInit.mOwner->AbstractMainThread())
229
  , mFrameStats(new FrameStatistics())
230
  , mVideoFrameContainer(aInit.mOwner->GetVideoFrameContainer())
231
  , mMinimizePreroll(aInit.mMinimizePreroll)
232
  , mFiredMetadataLoaded(false)
233
  , mIsDocumentVisible(false)
234
  , mElementVisibility(Visibility::UNTRACKED)
235
  , mIsElementInTree(false)
236
  , mForcedHidden(false)
237
  , mHasSuspendTaint(aInit.mHasSuspendTaint)
238
  , mPlaybackRate(aInit.mPlaybackRate)
239
  , INIT_MIRROR(mBuffered, TimeIntervals())
240
  , INIT_MIRROR(mCurrentPosition, TimeUnit::Zero())
241
  , INIT_MIRROR(mStateMachineDuration, NullableTimeUnit())
242
  , INIT_MIRROR(mIsAudioDataAudible, false)
243
  , INIT_CANONICAL(mVolume, aInit.mVolume)
244
  , INIT_CANONICAL(mPreservesPitch, aInit.mPreservesPitch)
245
  , INIT_CANONICAL(mLooping, aInit.mLooping)
246
  , INIT_CANONICAL(mPlayState, PLAY_STATE_LOADING)
247
  , INIT_CANONICAL(mLogicallySeeking, false)
248
  , INIT_CANONICAL(mSameOriginMedia, false)
249
  , INIT_CANONICAL(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE)
250
  , mVideoDecodingOberver(new BackgroundVideoDecodingPermissionObserver(this))
251
  , mIsBackgroundVideoDecodingAllowed(false)
252
  , mTelemetryReported(false)
253
  , mContainerType(aInit.mContainerType)
254
0
{
255
0
  MOZ_ASSERT(NS_IsMainThread());
256
0
  MOZ_ASSERT(mAbstractMainThread);
257
0
  MediaMemoryTracker::AddMediaDecoder(this);
258
0
259
0
  //
260
0
  // Initialize watchers.
261
0
  //
262
0
263
0
  // mDuration
264
0
  mWatchManager.Watch(mStateMachineDuration, &MediaDecoder::DurationChanged);
265
0
266
0
  // readyState
267
0
  mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateReadyState);
268
0
  // ReadyState computation depends on MediaDecoder::CanPlayThrough, which
269
0
  // depends on the download rate.
270
0
  mWatchManager.Watch(mBuffered, &MediaDecoder::UpdateReadyState);
271
0
272
0
  // mLogicalPosition
273
0
  mWatchManager.Watch(mCurrentPosition, &MediaDecoder::UpdateLogicalPosition);
274
0
  mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateLogicalPosition);
275
0
  mWatchManager.Watch(mLogicallySeeking, &MediaDecoder::UpdateLogicalPosition);
276
0
277
0
  mWatchManager.Watch(mIsAudioDataAudible,
278
0
                      &MediaDecoder::NotifyAudibleStateChanged);
279
0
280
0
  MediaShutdownManager::InitStatics();
281
0
  mVideoDecodingOberver->RegisterEvent();
282
0
}
283
284
#undef INIT_MIRROR
285
#undef INIT_CANONICAL
286
287
void
288
MediaDecoder::Shutdown()
289
0
{
290
0
  MOZ_ASSERT(NS_IsMainThread());
291
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
292
0
  AbstractThread::AutoEnter context(AbstractMainThread());
293
0
294
0
  // Unwatch all watch targets to prevent further notifications.
295
0
  mWatchManager.Shutdown();
296
0
297
0
  DiscardOngoingSeekIfExists();
298
0
299
0
  // This changes the decoder state to SHUTDOWN and does other things
300
0
  // necessary to unblock the state machine thread if it's blocked, so
301
0
  // the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
302
0
  if (mDecoderStateMachine) {
303
0
    mTimedMetadataListener.Disconnect();
304
0
    mMetadataLoadedListener.Disconnect();
305
0
    mFirstFrameLoadedListener.Disconnect();
306
0
    mOnPlaybackEvent.Disconnect();
307
0
    mOnPlaybackErrorEvent.Disconnect();
308
0
    mOnDecoderDoctorEvent.Disconnect();
309
0
    mOnMediaNotSeekable.Disconnect();
310
0
    mOnEncrypted.Disconnect();
311
0
    mOnWaitingForKey.Disconnect();
312
0
    mOnDecodeWarning.Disconnect();
313
0
    mOnNextFrameStatus.Disconnect();
314
0
315
0
    mDecoderStateMachine->BeginShutdown()
316
0
      ->Then(mAbstractMainThread, __func__, this,
317
0
             &MediaDecoder::FinishShutdown,
318
0
             &MediaDecoder::FinishShutdown);
319
0
  } else {
320
0
    // Ensure we always unregister asynchronously in order not to disrupt
321
0
    // the hashtable iterating in MediaShutdownManager::Shutdown().
322
0
    RefPtr<MediaDecoder> self = this;
323
0
    nsCOMPtr<nsIRunnable> r =
324
0
      NS_NewRunnableFunction("MediaDecoder::Shutdown", [self]() {
325
0
        self->mVideoFrameContainer = nullptr;
326
0
        MediaShutdownManager::Instance().Unregister(self);
327
0
      });
328
0
    mAbstractMainThread->Dispatch(r.forget());
329
0
  }
330
0
331
0
  // Ask the owner to remove its audio/video tracks.
332
0
  GetOwner()->RemoveMediaTracks();
333
0
334
0
  ChangeState(PLAY_STATE_SHUTDOWN);
335
0
  mVideoDecodingOberver->UnregisterEvent();
336
0
  mVideoDecodingOberver = nullptr;
337
0
  mOwner = nullptr;
338
0
}
339
340
void
341
MediaDecoder::NotifyXPCOMShutdown()
342
0
{
343
0
  MOZ_ASSERT(NS_IsMainThread());
344
0
  if (auto owner = GetOwner()) {
345
0
    owner->NotifyXPCOMShutdown();
346
0
  }
347
0
  MOZ_DIAGNOSTIC_ASSERT(IsShutdown());
348
0
349
0
  // Don't cause grief to release builds by ensuring Shutdown()
350
0
  // is always called during shutdown phase.
351
0
  if (!IsShutdown()) {
352
0
    Shutdown();
353
0
  }
354
0
}
355
356
MediaDecoder::~MediaDecoder()
357
0
{
358
0
  MOZ_ASSERT(NS_IsMainThread());
359
0
  MOZ_DIAGNOSTIC_ASSERT(IsShutdown());
360
0
  MediaMemoryTracker::RemoveMediaDecoder(this);
361
0
}
362
363
void
364
MediaDecoder::OnPlaybackEvent(MediaPlaybackEvent&& aEvent)
365
0
{
366
0
  switch (aEvent.mType) {
367
0
    case MediaPlaybackEvent::PlaybackEnded:
368
0
      PlaybackEnded();
369
0
      break;
370
0
    case MediaPlaybackEvent::SeekStarted:
371
0
      SeekingStarted();
372
0
      break;
373
0
    case MediaPlaybackEvent::Loop:
374
0
      GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
375
0
      GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
376
0
      break;
377
0
    case MediaPlaybackEvent::Invalidate:
378
0
      Invalidate();
379
0
      break;
380
0
    case MediaPlaybackEvent::EnterVideoSuspend:
381
0
      GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozentervideosuspend"));
382
0
      break;
383
0
    case MediaPlaybackEvent::ExitVideoSuspend:
384
0
      GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozexitvideosuspend"));
385
0
      break;
386
0
    case MediaPlaybackEvent::StartVideoSuspendTimer:
387
0
      GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozstartvideosuspendtimer"));
388
0
      break;
389
0
    case MediaPlaybackEvent::CancelVideoSuspendTimer:
390
0
      GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozcancelvideosuspendtimer"));
391
0
      break;
392
0
    case MediaPlaybackEvent::VideoOnlySeekBegin:
393
0
      GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozvideoonlyseekbegin"));
394
0
      break;
395
0
    case MediaPlaybackEvent::VideoOnlySeekCompleted:
396
0
      GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozvideoonlyseekcompleted"));
397
0
      break;
398
0
    default:
399
0
      break;
400
0
  }
401
0
}
402
403
void
404
MediaDecoder::OnPlaybackErrorEvent(const MediaResult& aError)
405
0
{
406
0
  DecodeError(aError);
407
0
}
408
409
void
410
MediaDecoder::OnDecoderDoctorEvent(DecoderDoctorEvent aEvent)
411
0
{
412
0
  MOZ_ASSERT(NS_IsMainThread());
413
0
  // OnDecoderDoctorEvent is disconnected at shutdown time.
414
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
415
0
  nsIDocument* doc = GetOwner()->GetDocument();
416
0
  if (!doc) {
417
0
    return;
418
0
  }
419
0
  DecoderDoctorDiagnostics diags;
420
0
  diags.StoreEvent(doc, aEvent, __func__);
421
0
}
422
423
static const char*
424
NextFrameStatusToStr(MediaDecoderOwner::NextFrameStatus aStatus)
425
0
{
426
0
  switch (aStatus) {
427
0
    case MediaDecoderOwner::NEXT_FRAME_AVAILABLE:
428
0
      return "NEXT_FRAME_AVAILABLE";
429
0
    case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE:
430
0
      return "NEXT_FRAME_UNAVAILABLE";
431
0
    case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING:
432
0
      return "NEXT_FRAME_UNAVAILABLE_BUFFERING";
433
0
    case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING:
434
0
      return "NEXT_FRAME_UNAVAILABLE_SEEKING";
435
0
    case MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED:
436
0
      return "NEXT_FRAME_UNINITIALIZED";
437
0
  }
438
0
  return "UNKNOWN";
439
0
}
440
441
void
442
MediaDecoder::OnNextFrameStatus(MediaDecoderOwner::NextFrameStatus aStatus)
443
0
{
444
0
  MOZ_ASSERT(NS_IsMainThread());
445
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
446
0
  if (mNextFrameStatus != aStatus) {
447
0
    LOG("Changed mNextFrameStatus to %s", NextFrameStatusToStr(aStatus));
448
0
    mNextFrameStatus = aStatus;
449
0
    UpdateReadyState();
450
0
  }
451
0
}
452
453
void
454
MediaDecoder::FinishShutdown()
455
0
{
456
0
  MOZ_ASSERT(NS_IsMainThread());
457
0
  SetStateMachine(nullptr);
458
0
  mVideoFrameContainer = nullptr;
459
0
  MediaShutdownManager::Instance().Unregister(this);
460
0
}
461
462
nsresult
463
MediaDecoder::InitializeStateMachine()
464
0
{
465
0
  MOZ_ASSERT(NS_IsMainThread());
466
0
  NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
467
0
  AbstractThread::AutoEnter context(AbstractMainThread());
468
0
469
0
  nsresult rv = mDecoderStateMachine->Init(this);
470
0
  NS_ENSURE_SUCCESS(rv, rv);
471
0
472
0
  // If some parameters got set before the state machine got created,
473
0
  // set them now
474
0
  SetStateMachineParameters();
475
0
476
0
  return NS_OK;
477
0
}
478
479
void
480
MediaDecoder::SetStateMachineParameters()
481
0
{
482
0
  MOZ_ASSERT(NS_IsMainThread());
483
0
  if (mPlaybackRate != 1 && mPlaybackRate != 0) {
484
0
    mDecoderStateMachine->DispatchSetPlaybackRate(mPlaybackRate);
485
0
  }
486
0
  mTimedMetadataListener = mDecoderStateMachine->TimedMetadataEvent().Connect(
487
0
    mAbstractMainThread, this, &MediaDecoder::OnMetadataUpdate);
488
0
  mMetadataLoadedListener = mDecoderStateMachine->MetadataLoadedEvent().Connect(
489
0
    mAbstractMainThread, this, &MediaDecoder::MetadataLoaded);
490
0
  mFirstFrameLoadedListener =
491
0
    mDecoderStateMachine->FirstFrameLoadedEvent().Connect(
492
0
      mAbstractMainThread, this, &MediaDecoder::FirstFrameLoaded);
493
0
494
0
  mOnPlaybackEvent = mDecoderStateMachine->OnPlaybackEvent().Connect(
495
0
    mAbstractMainThread, this, &MediaDecoder::OnPlaybackEvent);
496
0
  mOnPlaybackErrorEvent = mDecoderStateMachine->OnPlaybackErrorEvent().Connect(
497
0
    mAbstractMainThread, this, &MediaDecoder::OnPlaybackErrorEvent);
498
0
  mOnDecoderDoctorEvent = mDecoderStateMachine->OnDecoderDoctorEvent().Connect(
499
0
    mAbstractMainThread, this, &MediaDecoder::OnDecoderDoctorEvent);
500
0
  mOnMediaNotSeekable = mDecoderStateMachine->OnMediaNotSeekable().Connect(
501
0
    mAbstractMainThread, this, &MediaDecoder::OnMediaNotSeekable);
502
0
  mOnNextFrameStatus = mDecoderStateMachine->OnNextFrameStatus().Connect(
503
0
    mAbstractMainThread, this, &MediaDecoder::OnNextFrameStatus);
504
0
505
0
  mOnEncrypted = mReader->OnEncrypted().Connect(
506
0
    mAbstractMainThread, GetOwner(), &MediaDecoderOwner::DispatchEncrypted);
507
0
  mOnWaitingForKey = mReader->OnWaitingForKey().Connect(
508
0
    mAbstractMainThread, GetOwner(), &MediaDecoderOwner::NotifyWaitingForKey);
509
0
  mOnDecodeWarning = mReader->OnDecodeWarning().Connect(
510
0
    mAbstractMainThread, GetOwner(), &MediaDecoderOwner::DecodeWarning);
511
0
}
512
513
void
514
MediaDecoder::Play()
515
0
{
516
0
  MOZ_ASSERT(NS_IsMainThread());
517
0
518
0
  AbstractThread::AutoEnter context(AbstractMainThread());
519
0
  NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
520
0
  if (mPlaybackRate == 0) {
521
0
    return;
522
0
  }
523
0
524
0
  if (IsEnded()) {
525
0
    Seek(0, SeekTarget::PrevSyncPoint);
526
0
    return;
527
0
  } else if (mPlayState == PLAY_STATE_LOADING) {
528
0
    mNextState = PLAY_STATE_PLAYING;
529
0
    return;
530
0
  }
531
0
532
0
  ChangeState(PLAY_STATE_PLAYING);
533
0
}
534
535
void
536
MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
537
0
{
538
0
  MOZ_ASSERT(NS_IsMainThread());
539
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
540
0
541
0
  AbstractThread::AutoEnter context(AbstractMainThread());
542
0
  MOZ_ASSERT(aTime >= 0.0, "Cannot seek to a negative value.");
543
0
544
0
  int64_t timeUsecs = TimeUnit::FromSeconds(aTime).ToMicroseconds();
545
0
546
0
  mLogicalPosition = aTime;
547
0
548
0
  mLogicallySeeking = true;
549
0
  SeekTarget target = SeekTarget(timeUsecs, aSeekType);
550
0
  CallSeek(target);
551
0
552
0
  if (mPlayState == PLAY_STATE_ENDED) {
553
0
    ChangeState(GetOwner()->GetPaused() ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING);
554
0
  }
555
0
}
556
557
void
558
MediaDecoder::DiscardOngoingSeekIfExists()
559
0
{
560
0
  MOZ_ASSERT(NS_IsMainThread());
561
0
  AbstractThread::AutoEnter context(AbstractMainThread());
562
0
  mSeekRequest.DisconnectIfExists();
563
0
  GetOwner()->AsyncRejectSeekDOMPromiseIfExists();
564
0
}
565
566
void
567
MediaDecoder::CallSeek(const SeekTarget& aTarget)
568
0
{
569
0
  MOZ_ASSERT(NS_IsMainThread());
570
0
  AbstractThread::AutoEnter context(AbstractMainThread());
571
0
  DiscardOngoingSeekIfExists();
572
0
573
0
  mDecoderStateMachine->InvokeSeek(aTarget)
574
0
  ->Then(mAbstractMainThread, __func__, this,
575
0
         &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected)
576
0
  ->Track(mSeekRequest);
577
0
}
578
579
double
580
MediaDecoder::GetCurrentTime()
581
0
{
582
0
  MOZ_ASSERT(NS_IsMainThread());
583
0
  AbstractThread::AutoEnter context(AbstractMainThread());
584
0
  return mLogicalPosition;
585
0
}
586
587
void
588
MediaDecoder::OnMetadataUpdate(TimedMetadata&& aMetadata)
589
0
{
590
0
  MOZ_ASSERT(NS_IsMainThread());
591
0
  AbstractThread::AutoEnter context(AbstractMainThread());
592
0
  GetOwner()->RemoveMediaTracks();
593
0
  MetadataLoaded(MakeUnique<MediaInfo>(*aMetadata.mInfo),
594
0
                 UniquePtr<MetadataTags>(aMetadata.mTags.forget()),
595
0
                 MediaDecoderEventVisibility::Observable);
596
0
  FirstFrameLoaded(std::move(aMetadata.mInfo),
597
0
                   MediaDecoderEventVisibility::Observable);
598
0
}
599
600
void
601
MediaDecoder::MetadataLoaded(UniquePtr<MediaInfo> aInfo,
602
                             UniquePtr<MetadataTags> aTags,
603
                             MediaDecoderEventVisibility aEventVisibility)
604
0
{
605
0
  MOZ_ASSERT(NS_IsMainThread());
606
0
  AbstractThread::AutoEnter context(AbstractMainThread());
607
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
608
0
609
0
  LOG("MetadataLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d",
610
0
      aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
611
0
      aInfo->HasAudio(), aInfo->HasVideo());
612
0
613
0
  mMediaSeekable = aInfo->mMediaSeekable;
614
0
  mMediaSeekableOnlyInBufferedRanges = aInfo->mMediaSeekableOnlyInBufferedRanges;
615
0
  mInfo = aInfo.release();
616
0
  GetOwner()->ConstructMediaTracks(mInfo);
617
0
618
0
  // Make sure the element and the frame (if any) are told about
619
0
  // our new size.
620
0
  if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
621
0
    mFiredMetadataLoaded = true;
622
0
    GetOwner()->MetadataLoaded(mInfo, std::move(aTags));
623
0
  }
624
0
  // Invalidate() will end up calling GetOwner()->UpdateMediaSize with the last
625
0
  // dimensions retrieved from the video frame container. The video frame
626
0
  // container contains more up to date dimensions than aInfo.
627
0
  // So we call Invalidate() after calling GetOwner()->MetadataLoaded to ensure
628
0
  // the media element has the latest dimensions.
629
0
  Invalidate();
630
0
631
0
  EnsureTelemetryReported();
632
0
}
633
634
void
635
MediaDecoder::EnsureTelemetryReported()
636
0
{
637
0
  MOZ_ASSERT(NS_IsMainThread());
638
0
  AbstractThread::AutoEnter context(AbstractMainThread());
639
0
640
0
  if (mTelemetryReported || !mInfo) {
641
0
    // Note: sometimes we get multiple MetadataLoaded calls (for example
642
0
    // for chained ogg). So we ensure we don't report duplicate results for
643
0
    // these resources.
644
0
    return;
645
0
  }
646
0
647
0
  nsTArray<nsCString> codecs;
648
0
  if (mInfo->HasAudio() &&
649
0
      !mInfo->mAudio.GetAsAudioInfo()->mMimeType.IsEmpty()) {
650
0
    codecs.AppendElement(mInfo->mAudio.GetAsAudioInfo()->mMimeType);
651
0
  }
652
0
  if (mInfo->HasVideo() &&
653
0
      !mInfo->mVideo.GetAsVideoInfo()->mMimeType.IsEmpty()) {
654
0
    codecs.AppendElement(mInfo->mVideo.GetAsVideoInfo()->mMimeType);
655
0
  }
656
0
  if (codecs.IsEmpty()) {
657
0
    codecs.AppendElement(
658
0
      nsPrintfCString("resource; %s", ContainerType().OriginalString().Data()));
659
0
  }
660
0
  for (const nsCString& codec : codecs) {
661
0
    LOG("Telemetry MEDIA_CODEC_USED= '%s'", codec.get());
662
0
    Telemetry::Accumulate(Telemetry::HistogramID::MEDIA_CODEC_USED, codec);
663
0
  }
664
0
665
0
  mTelemetryReported = true;
666
0
}
667
668
const char*
669
MediaDecoder::PlayStateStr()
670
0
{
671
0
  MOZ_ASSERT(NS_IsMainThread());
672
0
  AbstractThread::AutoEnter context(AbstractMainThread());
673
0
  return ToPlayStateStr(mPlayState);
674
0
}
675
676
void
677
MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
678
                               MediaDecoderEventVisibility aEventVisibility)
679
0
{
680
0
  MOZ_ASSERT(NS_IsMainThread());
681
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
682
0
  AbstractThread::AutoEnter context(AbstractMainThread());
683
0
684
0
  LOG("FirstFrameLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d "
685
0
      "mPlayState=%s transportSeekable=%d",
686
0
      aInfo->mAudio.mChannels, aInfo->mAudio.mRate, aInfo->HasAudio(),
687
0
      aInfo->HasVideo(), PlayStateStr(), IsTransportSeekable());
688
0
689
0
  mInfo = aInfo.forget();
690
0
691
0
  Invalidate();
692
0
693
0
  // The element can run javascript via events
694
0
  // before reaching here, so only change the
695
0
  // state if we're still set to the original
696
0
  // loading state.
697
0
  if (mPlayState == PLAY_STATE_LOADING) {
698
0
    ChangeState(mNextState);
699
0
  }
700
0
701
0
  // GetOwner()->FirstFrameLoaded() might call us back. Put it at the bottom of
702
0
  // this function to avoid unexpected shutdown from reentrant calls.
703
0
  if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
704
0
    GetOwner()->FirstFrameLoaded();
705
0
  }
706
0
}
707
708
void
709
MediaDecoder::NetworkError(const MediaResult& aError)
710
0
{
711
0
  MOZ_ASSERT(NS_IsMainThread());
712
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
713
0
  GetOwner()->NetworkError(aError);
714
0
}
715
716
void
717
MediaDecoder::DecodeError(const MediaResult& aError)
718
0
{
719
0
  MOZ_ASSERT(NS_IsMainThread());
720
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
721
0
  GetOwner()->DecodeError(aError);
722
0
}
723
724
void
725
MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin)
726
0
{
727
0
  MOZ_ASSERT(NS_IsMainThread());
728
0
  AbstractThread::AutoEnter context(AbstractMainThread());
729
0
  mSameOriginMedia = aSameOrigin;
730
0
}
731
732
bool
733
MediaDecoder::IsSeeking() const
734
0
{
735
0
  MOZ_ASSERT(NS_IsMainThread());
736
0
  return mLogicallySeeking;
737
0
}
738
739
bool
740
MediaDecoder::OwnerHasError() const
741
0
{
742
0
  MOZ_ASSERT(NS_IsMainThread());
743
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
744
0
  return GetOwner()->HasError();
745
0
}
746
747
bool
748
MediaDecoder::IsEnded() const
749
0
{
750
0
  MOZ_ASSERT(NS_IsMainThread());
751
0
  return mPlayState == PLAY_STATE_ENDED;
752
0
}
753
754
bool
755
MediaDecoder::IsShutdown() const
756
0
{
757
0
  MOZ_ASSERT(NS_IsMainThread());
758
0
  return mPlayState == PLAY_STATE_SHUTDOWN;
759
0
}
760
761
void
762
MediaDecoder::PlaybackEnded()
763
0
{
764
0
  MOZ_ASSERT(NS_IsMainThread());
765
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
766
0
767
0
  if (mLogicallySeeking || mPlayState == PLAY_STATE_LOADING ||
768
0
      mPlayState == PLAY_STATE_ENDED) {
769
0
    LOG("MediaDecoder::PlaybackEnded bailed out, "
770
0
        "mLogicallySeeking=%d mPlayState=%s",
771
0
        mLogicallySeeking.Ref(), ToPlayStateStr(mPlayState));
772
0
    return;
773
0
  }
774
0
775
0
  LOG("MediaDecoder::PlaybackEnded");
776
0
777
0
  ChangeState(PLAY_STATE_ENDED);
778
0
  InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);
779
0
  GetOwner()->PlaybackEnded();
780
0
}
781
782
void
783
MediaDecoder::NotifyPrincipalChanged()
784
0
{
785
0
  MOZ_ASSERT(NS_IsMainThread());
786
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
787
0
  AbstractThread::AutoEnter context(AbstractMainThread());
788
0
  nsCOMPtr<nsIPrincipal> newPrincipal = GetCurrentPrincipal();
789
0
  mMediaPrincipalHandle = MakePrincipalHandle(newPrincipal);
790
0
  GetOwner()->NotifyDecoderPrincipalChanged();
791
0
}
792
793
void
794
MediaDecoder::OnSeekResolved()
795
0
{
796
0
  MOZ_ASSERT(NS_IsMainThread());
797
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
798
0
  AbstractThread::AutoEnter context(AbstractMainThread());
799
0
  mSeekRequest.Complete();
800
0
801
0
  mLogicallySeeking = false;
802
0
803
0
  // Ensure logical position is updated after seek.
804
0
  UpdateLogicalPositionInternal();
805
0
806
0
  GetOwner()->SeekCompleted();
807
0
  GetOwner()->AsyncResolveSeekDOMPromiseIfExists();
808
0
}
809
810
void
811
MediaDecoder::OnSeekRejected()
812
0
{
813
0
  MOZ_ASSERT(NS_IsMainThread());
814
0
  mSeekRequest.Complete();
815
0
  mLogicallySeeking = false;
816
0
  GetOwner()->AsyncRejectSeekDOMPromiseIfExists();
817
0
}
818
819
void
820
MediaDecoder::SeekingStarted()
821
0
{
822
0
  MOZ_ASSERT(NS_IsMainThread());
823
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
824
0
  GetOwner()->SeekStarted();
825
0
}
826
827
void
828
MediaDecoder::ChangeState(PlayState aState)
829
0
{
830
0
  MOZ_ASSERT(NS_IsMainThread());
831
0
  MOZ_ASSERT(!IsShutdown(), "SHUTDOWN is the final state.");
832
0
  AbstractThread::AutoEnter context(AbstractMainThread());
833
0
834
0
  if (mNextState == aState) {
835
0
    mNextState = PLAY_STATE_PAUSED;
836
0
  }
837
0
838
0
  if (mPlayState != aState) {
839
0
    DDLOG(DDLogCategory::Property, "play_state", ToPlayStateStr(aState));
840
0
  }
841
0
  mPlayState = aState;
842
0
843
0
  if (mPlayState == PLAY_STATE_PLAYING) {
844
0
    GetOwner()->ConstructMediaTracks(mInfo);
845
0
  } else if (IsEnded()) {
846
0
    GetOwner()->RemoveMediaTracks();
847
0
  }
848
0
}
849
850
void
851
MediaDecoder::UpdateLogicalPositionInternal()
852
0
{
853
0
  MOZ_ASSERT(NS_IsMainThread());
854
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
855
0
856
0
  double currentPosition = CurrentPosition().ToSeconds();
857
0
  if (mPlayState == PLAY_STATE_ENDED) {
858
0
    currentPosition = std::max(currentPosition, mDuration);
859
0
  }
860
0
  bool logicalPositionChanged = mLogicalPosition != currentPosition;
861
0
  mLogicalPosition = currentPosition;
862
0
  DDLOG(DDLogCategory::Property, "currentTime", mLogicalPosition);
863
0
864
0
  // Invalidate the frame so any video data is displayed.
865
0
  // Do this before the timeupdate event so that if that
866
0
  // event runs JavaScript that queries the media size, the
867
0
  // frame has reflowed and the size updated beforehand.
868
0
  Invalidate();
869
0
870
0
  if (logicalPositionChanged) {
871
0
    FireTimeUpdate();
872
0
  }
873
0
}
874
875
void
876
MediaDecoder::DurationChanged()
877
0
{
878
0
  MOZ_ASSERT(NS_IsMainThread());
879
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
880
0
  AbstractThread::AutoEnter context(AbstractMainThread());
881
0
882
0
  double oldDuration = mDuration;
883
0
884
0
  // Use the explicit duration if we have one.
885
0
  // Otherwise use the duration mirrored from MDSM.
886
0
  if (mExplicitDuration.isSome()) {
887
0
    mDuration = mExplicitDuration.ref();
888
0
  } else if (mStateMachineDuration.Ref().isSome()) {
889
0
    mDuration = mStateMachineDuration.Ref().ref().ToSeconds();
890
0
  }
891
0
892
0
  if (mDuration == oldDuration || IsNaN(mDuration)) {
893
0
    return;
894
0
  }
895
0
896
0
  LOG("Duration changed to %f", mDuration);
897
0
898
0
  // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=28822 for a discussion
899
0
  // of whether we should fire durationchange on explicit infinity.
900
0
  if (mFiredMetadataLoaded &&
901
0
      (!mozilla::IsInfinite<double>(mDuration) || mExplicitDuration.isSome())) {
902
0
    GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
903
0
  }
904
0
905
0
  if (CurrentPosition() > TimeUnit::FromSeconds(mDuration)) {
906
0
    Seek(mDuration, SeekTarget::Accurate);
907
0
  }
908
0
}
909
910
already_AddRefed<KnowsCompositor>
911
MediaDecoder::GetCompositor()
912
0
{
913
0
  MediaDecoderOwner* owner = GetOwner();
914
0
  nsIDocument* ownerDoc = owner ? owner->GetDocument() : nullptr;
915
0
  RefPtr<LayerManager> layerManager =
916
0
    ownerDoc ? nsContentUtils::LayerManagerForDocument(ownerDoc) : nullptr;
917
0
  RefPtr<KnowsCompositor> knows =
918
0
    layerManager ? layerManager->AsKnowsCompositor() : nullptr;
919
0
  return knows ? knows->GetForMedia().forget() : nullptr;
920
0
}
921
922
void
923
MediaDecoder::NotifyCompositor()
924
0
{
925
0
  RefPtr<KnowsCompositor> knowsCompositor = GetCompositor();
926
0
  if (knowsCompositor) {
927
0
    nsCOMPtr<nsIRunnable> r =
928
0
      NewRunnableMethod<already_AddRefed<KnowsCompositor>&&>(
929
0
        "MediaFormatReader::UpdateCompositor",
930
0
        mReader,
931
0
        &MediaFormatReader::UpdateCompositor,
932
0
        knowsCompositor.forget());
933
0
    Unused << mReader->OwnerThread()->Dispatch(r.forget());
934
0
  }
935
0
}
936
937
void
938
MediaDecoder::SetElementVisibility(bool aIsDocumentVisible,
939
                                   Visibility aElementVisibility,
940
                                   bool aIsElementInTree)
941
0
{
942
0
  MOZ_ASSERT(NS_IsMainThread());
943
0
  mIsDocumentVisible = aIsDocumentVisible;
944
0
  mElementVisibility = aElementVisibility;
945
0
  mIsElementInTree = aIsElementInTree;
946
0
  UpdateVideoDecodeMode();
947
0
}
948
949
void
950
MediaDecoder::SetForcedHidden(bool aForcedHidden)
951
0
{
952
0
  MOZ_ASSERT(NS_IsMainThread());
953
0
  mForcedHidden = aForcedHidden;
954
0
  UpdateVideoDecodeMode();
955
0
}
956
957
void
958
MediaDecoder::SetSuspendTaint(bool aTainted)
959
0
{
960
0
  MOZ_ASSERT(NS_IsMainThread());
961
0
  mHasSuspendTaint = aTainted;
962
0
  UpdateVideoDecodeMode();
963
0
}
964
965
void
966
MediaDecoder::UpdateVideoDecodeMode()
967
0
{
968
0
  MOZ_ASSERT(NS_IsMainThread());
969
0
  AbstractThread::AutoEnter context(mAbstractMainThread);
970
0
971
0
  // The MDSM may yet be set.
972
0
  if (!mDecoderStateMachine) {
973
0
    LOG("UpdateVideoDecodeMode(), early return because we don't have MDSM.");
974
0
    return;
975
0
  }
976
0
977
0
  // If an element is in-tree with UNTRACKED visibility, the visibility is
978
0
  // incomplete and don't update the video decode mode.
979
0
  if (mIsElementInTree && mElementVisibility == Visibility::UNTRACKED) {
980
0
    LOG("UpdateVideoDecodeMode(), early return because we have incomplete visibility states.");
981
0
    return;
982
0
  }
983
0
984
0
  // If mHasSuspendTaint is set, never suspend the video decoder.
985
0
  if (mHasSuspendTaint) {
986
0
    LOG("UpdateVideoDecodeMode(), set Normal because the element has been tainted.");
987
0
    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
988
0
    return;
989
0
  }
990
0
991
0
  // Don't suspend elements that is not in tree.
992
0
  if (!mIsElementInTree) {
993
0
    LOG("UpdateVideoDecodeMode(), set Normal because the element is not in tree.");
994
0
    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
995
0
    return;
996
0
  }
997
0
998
0
  // If mForcedHidden is set, suspend the video decoder anyway.
999
0
  if (mForcedHidden) {
1000
0
    LOG("UpdateVideoDecodeMode(), set Suspend because the element is forced to be suspended.");
1001
0
    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Suspend);
1002
0
    return;
1003
0
  }
1004
0
1005
0
  // Resume decoding in the advance, even the element is in the background.
1006
0
  if (mIsBackgroundVideoDecodingAllowed) {
1007
0
    LOG("UpdateVideoDecodeMode(), set Normal because the tab is in background and hovered.");
1008
0
    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
1009
0
    return;
1010
0
  }
1011
0
1012
0
  // Otherwise, depends on the owner's visibility state.
1013
0
  // A element is visible only if its document is visible and the element
1014
0
  // itself is visible.
1015
0
  if (mIsDocumentVisible &&
1016
0
      mElementVisibility == Visibility::APPROXIMATELY_VISIBLE) {
1017
0
    LOG("UpdateVideoDecodeMode(), set Normal because the element visible.");
1018
0
    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
1019
0
  } else {
1020
0
    LOG("UpdateVideoDecodeMode(), set Suspend because the element is not visible.");
1021
0
    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Suspend);
1022
0
  }
1023
0
}
1024
1025
void
1026
MediaDecoder::SetIsBackgroundVideoDecodingAllowed(bool aAllowed)
1027
0
{
1028
0
  mIsBackgroundVideoDecodingAllowed = aAllowed;
1029
0
  UpdateVideoDecodeMode();
1030
0
}
1031
1032
bool
1033
MediaDecoder::HasSuspendTaint() const
1034
0
{
1035
0
  MOZ_ASSERT(NS_IsMainThread());
1036
0
  return mHasSuspendTaint;
1037
0
}
1038
1039
bool
1040
MediaDecoder::IsMediaSeekable()
1041
0
{
1042
0
  MOZ_ASSERT(NS_IsMainThread());
1043
0
  NS_ENSURE_TRUE(GetStateMachine(), false);
1044
0
  return mMediaSeekable;
1045
0
}
1046
1047
media::TimeIntervals
1048
MediaDecoder::GetSeekable()
1049
0
{
1050
0
  MOZ_ASSERT(NS_IsMainThread());
1051
0
1052
0
  if (IsNaN(GetDuration())) {
1053
0
    // We do not have a duration yet, we can't determine the seekable range.
1054
0
    return TimeIntervals();
1055
0
  }
1056
0
1057
0
  // We can seek in buffered range if the media is seekable. Also, we can seek
1058
0
  // in unbuffered ranges if the transport level is seekable (local file or the
1059
0
  // server supports range requests, etc.) or in cue-less WebMs
1060
0
  if (mMediaSeekableOnlyInBufferedRanges) {
1061
0
    return GetBuffered();
1062
0
  } else if (!IsMediaSeekable()) {
1063
0
    return media::TimeIntervals();
1064
0
  } else if (!IsTransportSeekable()) {
1065
0
    return GetBuffered();
1066
0
  } else {
1067
0
    return media::TimeIntervals(
1068
0
      media::TimeInterval(TimeUnit::Zero(),
1069
0
                          IsInfinite()
1070
0
                          ? TimeUnit::FromInfinity()
1071
0
                          : TimeUnit::FromSeconds(GetDuration())));
1072
0
  }
1073
0
}
1074
1075
void
1076
MediaDecoder::SetFragmentEndTime(double aTime)
1077
0
{
1078
0
  MOZ_ASSERT(NS_IsMainThread());
1079
0
  if (mDecoderStateMachine) {
1080
0
    mDecoderStateMachine->DispatchSetFragmentEndTime(
1081
0
      TimeUnit::FromSeconds(aTime));
1082
0
  }
1083
0
}
1084
1085
void
1086
MediaDecoder::SetPlaybackRate(double aPlaybackRate)
1087
0
{
1088
0
  MOZ_ASSERT(NS_IsMainThread());
1089
0
  AbstractThread::AutoEnter context(AbstractMainThread());
1090
0
1091
0
  double oldRate = mPlaybackRate;
1092
0
  mPlaybackRate = aPlaybackRate;
1093
0
  if (aPlaybackRate == 0) {
1094
0
    Pause();
1095
0
    return;
1096
0
  }
1097
0
1098
0
1099
0
  if (oldRate == 0 && !GetOwner()->GetPaused()) {
1100
0
    // PlaybackRate is no longer null.
1101
0
    // Restart the playback if the media was playing.
1102
0
    Play();
1103
0
  }
1104
0
1105
0
  if (mDecoderStateMachine) {
1106
0
    mDecoderStateMachine->DispatchSetPlaybackRate(aPlaybackRate);
1107
0
  }
1108
0
}
1109
1110
void
1111
MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
1112
0
{
1113
0
  MOZ_ASSERT(NS_IsMainThread());
1114
0
  AbstractThread::AutoEnter context(AbstractMainThread());
1115
0
  mPreservesPitch = aPreservesPitch;
1116
0
}
1117
1118
void
1119
MediaDecoder::SetLooping(bool aLooping)
1120
0
{
1121
0
  MOZ_ASSERT(NS_IsMainThread());
1122
0
  AbstractThread::AutoEnter context(AbstractMainThread());
1123
0
  mLooping = aLooping;
1124
0
}
1125
1126
void
1127
MediaDecoder::ConnectMirrors(MediaDecoderStateMachine* aObject)
1128
0
{
1129
0
  MOZ_ASSERT(NS_IsMainThread());
1130
0
  MOZ_ASSERT(aObject);
1131
0
  mStateMachineDuration.Connect(aObject->CanonicalDuration());
1132
0
  mBuffered.Connect(aObject->CanonicalBuffered());
1133
0
  mCurrentPosition.Connect(aObject->CanonicalCurrentPosition());
1134
0
  mIsAudioDataAudible.Connect(aObject->CanonicalIsAudioDataAudible());
1135
0
}
1136
1137
void
1138
MediaDecoder::DisconnectMirrors()
1139
0
{
1140
0
  MOZ_ASSERT(NS_IsMainThread());
1141
0
  mStateMachineDuration.DisconnectIfConnected();
1142
0
  mBuffered.DisconnectIfConnected();
1143
0
  mCurrentPosition.DisconnectIfConnected();
1144
0
  mIsAudioDataAudible.DisconnectIfConnected();
1145
0
}
1146
1147
void
1148
MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
1149
0
{
1150
0
  MOZ_ASSERT(NS_IsMainThread());
1151
0
  MOZ_ASSERT_IF(aStateMachine, !mDecoderStateMachine);
1152
0
  if (aStateMachine) {
1153
0
    mDecoderStateMachine = aStateMachine;
1154
0
    DDLINKCHILD("decoder state machine", mDecoderStateMachine.get());
1155
0
    ConnectMirrors(aStateMachine);
1156
0
    UpdateVideoDecodeMode();
1157
0
  } else if (mDecoderStateMachine) {
1158
0
    DDUNLINKCHILD(mDecoderStateMachine.get());
1159
0
    mDecoderStateMachine = nullptr;
1160
0
    DisconnectMirrors();
1161
0
  }
1162
0
}
1163
1164
ImageContainer*
1165
MediaDecoder::GetImageContainer()
1166
0
{
1167
0
  return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer()
1168
0
                              : nullptr;
1169
0
}
1170
1171
void
1172
MediaDecoder::InvalidateWithFlags(uint32_t aFlags)
1173
0
{
1174
0
  if (mVideoFrameContainer) {
1175
0
    mVideoFrameContainer->InvalidateWithFlags(aFlags);
1176
0
  }
1177
0
}
1178
1179
void
1180
MediaDecoder::Invalidate()
1181
0
{
1182
0
  if (mVideoFrameContainer) {
1183
0
    mVideoFrameContainer->Invalidate();
1184
0
  }
1185
0
}
1186
1187
// Constructs the time ranges representing what segments of the media
1188
// are buffered and playable.
1189
media::TimeIntervals
1190
MediaDecoder::GetBuffered()
1191
0
{
1192
0
  MOZ_ASSERT(NS_IsMainThread());
1193
0
  return mBuffered.Ref();
1194
0
}
1195
1196
size_t
1197
MediaDecoder::SizeOfVideoQueue()
1198
0
{
1199
0
  MOZ_ASSERT(NS_IsMainThread());
1200
0
  if (mDecoderStateMachine) {
1201
0
    return mDecoderStateMachine->SizeOfVideoQueue();
1202
0
  }
1203
0
  return 0;
1204
0
}
1205
1206
size_t
1207
MediaDecoder::SizeOfAudioQueue()
1208
0
{
1209
0
  MOZ_ASSERT(NS_IsMainThread());
1210
0
  if (mDecoderStateMachine) {
1211
0
    return mDecoderStateMachine->SizeOfAudioQueue();
1212
0
  }
1213
0
  return 0;
1214
0
}
1215
1216
void
1217
MediaDecoder::NotifyReaderDataArrived()
1218
0
{
1219
0
  MOZ_ASSERT(NS_IsMainThread());
1220
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1221
0
1222
0
  nsresult rv = mReader->OwnerThread()->Dispatch(
1223
0
    NewRunnableMethod("MediaFormatReader::NotifyDataArrived",
1224
0
                      mReader.get(),
1225
0
                      &MediaFormatReader::NotifyDataArrived));
1226
0
  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
1227
0
  Unused << rv;
1228
0
}
1229
1230
// Provide access to the state machine object
1231
MediaDecoderStateMachine*
1232
MediaDecoder::GetStateMachine() const
1233
0
{
1234
0
  MOZ_ASSERT(NS_IsMainThread());
1235
0
  return mDecoderStateMachine;
1236
0
}
1237
1238
void
1239
MediaDecoder::FireTimeUpdate()
1240
0
{
1241
0
  MOZ_ASSERT(NS_IsMainThread());
1242
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1243
0
  GetOwner()->FireTimeUpdate(true);
1244
0
}
1245
1246
bool
1247
MediaDecoder::CanPlayThrough()
1248
0
{
1249
0
  MOZ_ASSERT(NS_IsMainThread());
1250
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1251
0
  AbstractThread::AutoEnter context(AbstractMainThread());
1252
0
  return CanPlayThroughImpl();
1253
0
}
1254
1255
RefPtr<SetCDMPromise>
1256
MediaDecoder::SetCDMProxy(CDMProxy* aProxy)
1257
0
{
1258
0
  MOZ_ASSERT(NS_IsMainThread());
1259
0
  return InvokeAsync<RefPtr<CDMProxy>>(mReader->OwnerThread(),
1260
0
                                       mReader.get(),
1261
0
                                       __func__,
1262
0
                                       &MediaFormatReader::SetCDMProxy,
1263
0
                                       aProxy);
1264
0
}
1265
1266
bool
1267
MediaDecoder::IsOpusEnabled()
1268
0
{
1269
0
  return StaticPrefs::MediaOpusEnabled();
1270
0
}
1271
1272
bool
1273
MediaDecoder::IsOggEnabled()
1274
0
{
1275
0
  return StaticPrefs::MediaOggEnabled();
1276
0
}
1277
1278
bool
1279
MediaDecoder::IsWaveEnabled()
1280
0
{
1281
0
  return StaticPrefs::MediaWaveEnabled();
1282
0
}
1283
1284
bool
1285
MediaDecoder::IsWebMEnabled()
1286
0
{
1287
0
  return StaticPrefs::MediaWebMEnabled();
1288
0
}
1289
1290
NS_IMETHODIMP
1291
MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
1292
                                   nsISupports* aData, bool aAnonymize)
1293
0
{
1294
0
  // NB: When resourceSizes' ref count goes to 0 the promise will report the
1295
0
  //     resources memory and finish the asynchronous memory report.
1296
0
  RefPtr<MediaDecoder::ResourceSizes> resourceSizes =
1297
0
      new MediaDecoder::ResourceSizes(MediaMemoryTracker::MallocSizeOf);
1298
0
1299
0
  nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
1300
0
  nsCOMPtr<nsISupports> data = aData;
1301
0
1302
0
  resourceSizes->Promise()->Then(
1303
0
      // Don't use SystemGroup::AbstractMainThreadFor() for
1304
0
      // handleReport->Callback() will run scripts.
1305
0
      AbstractThread::MainThread(),
1306
0
      __func__,
1307
0
      [handleReport, data] (size_t size) {
1308
0
        handleReport->Callback(
1309
0
            EmptyCString(), NS_LITERAL_CSTRING("explicit/media/resources"),
1310
0
            KIND_HEAP, UNITS_BYTES, size,
1311
0
            NS_LITERAL_CSTRING("Memory used by media resources including "
1312
0
                               "streaming buffers, caches, etc."),
1313
0
            data);
1314
0
1315
0
        nsCOMPtr<nsIMemoryReporterManager> imgr =
1316
0
          do_GetService("@mozilla.org/memory-reporter-manager;1");
1317
0
1318
0
        if (imgr) {
1319
0
          imgr->EndReport();
1320
0
        }
1321
0
      },
1322
0
      [] (size_t) { /* unused reject function */ });
1323
0
1324
0
  int64_t video = 0;
1325
0
  int64_t audio = 0;
1326
0
  DecodersArray& decoders = Decoders();
1327
0
  for (size_t i = 0; i < decoders.Length(); ++i) {
1328
0
    MediaDecoder* decoder = decoders[i];
1329
0
    video += decoder->SizeOfVideoQueue();
1330
0
    audio += decoder->SizeOfAudioQueue();
1331
0
    decoder->AddSizeOfResources(resourceSizes);
1332
0
  }
1333
0
1334
0
  MOZ_COLLECT_REPORT(
1335
0
    "explicit/media/decoded/video", KIND_HEAP, UNITS_BYTES, video,
1336
0
    "Memory used by decoded video frames.");
1337
0
1338
0
  MOZ_COLLECT_REPORT(
1339
0
    "explicit/media/decoded/audio", KIND_HEAP, UNITS_BYTES, audio,
1340
0
    "Memory used by decoded audio chunks.");
1341
0
1342
0
  return NS_OK;
1343
0
}
1344
1345
MediaDecoderOwner*
1346
MediaDecoder::GetOwner() const
1347
0
{
1348
0
  MOZ_ASSERT(NS_IsMainThread());
1349
0
  // mOwner is valid until shutdown.
1350
0
  return mOwner;
1351
0
}
1352
1353
MediaDecoderOwner::NextFrameStatus
1354
MediaDecoder::NextFrameBufferedStatus()
1355
0
{
1356
0
  MOZ_ASSERT(NS_IsMainThread());
1357
0
  // Next frame hasn't been decoded yet.
1358
0
  // Use the buffered range to consider if we have the next frame available.
1359
0
  auto currentPosition = CurrentPosition();
1360
0
  media::TimeInterval interval(
1361
0
    currentPosition,
1362
0
    currentPosition + DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED);
1363
0
  return GetBuffered().Contains(interval)
1364
0
         ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
1365
0
         : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
1366
0
}
1367
1368
nsCString
1369
MediaDecoder::GetDebugInfo()
1370
0
{
1371
0
  return nsPrintfCString(
1372
0
    "MediaDecoder=%p: channels=%u rate=%u hasAudio=%d hasVideo=%d "
1373
0
    "mPlayState=%s",
1374
0
    this,
1375
0
    mInfo ? mInfo->mAudio.mChannels : 0,
1376
0
    mInfo ? mInfo->mAudio.mRate : 0,
1377
0
    mInfo ? mInfo->HasAudio() : 0,
1378
0
    mInfo ? mInfo->HasVideo() : 0,
1379
0
    PlayStateStr());
1380
0
}
1381
1382
RefPtr<GenericPromise>
1383
MediaDecoder::DumpDebugInfo()
1384
0
{
1385
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1386
0
  nsCString str = GetDebugInfo();
1387
0
1388
0
  nsAutoCString readerStr;
1389
0
  GetMozDebugReaderData(readerStr);
1390
0
  if (!readerStr.IsEmpty()) {
1391
0
    str += "\nreader data:\n";
1392
0
    str += readerStr;
1393
0
  }
1394
0
1395
0
  if (!GetStateMachine()) {
1396
0
    DUMP("%s", str.get());
1397
0
    return GenericPromise::CreateAndResolve(true, __func__);
1398
0
  }
1399
0
1400
0
  return GetStateMachine()->RequestDebugInfo()->Then(
1401
0
    SystemGroup::AbstractMainThreadFor(TaskCategory::Other),
1402
0
    __func__,
1403
0
    [str](const nsACString& aString) {
1404
0
      DUMP("%s", str.get());
1405
0
      DUMP("%s", aString.Data());
1406
0
      return GenericPromise::CreateAndResolve(true, __func__);
1407
0
    },
1408
0
    [str]() {
1409
0
      DUMP("%s", str.get());
1410
0
      return GenericPromise::CreateAndResolve(true, __func__);
1411
0
    });
1412
0
}
1413
1414
RefPtr<MediaDecoder::DebugInfoPromise>
1415
MediaDecoder::RequestDebugInfo()
1416
0
{
1417
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1418
0
1419
0
  auto str = GetDebugInfo();
1420
0
  if (!GetStateMachine()) {
1421
0
    return DebugInfoPromise::CreateAndResolve(str, __func__);
1422
0
  }
1423
0
1424
0
  return GetStateMachine()->RequestDebugInfo()->Then(
1425
0
    SystemGroup::AbstractMainThreadFor(TaskCategory::Other), __func__,
1426
0
    [str] (const nsACString& aString) {
1427
0
      nsCString result = str + nsCString("\n") + aString;
1428
0
      return DebugInfoPromise::CreateAndResolve(result, __func__);
1429
0
    },
1430
0
    [str] () {
1431
0
      return DebugInfoPromise::CreateAndResolve(str, __func__);
1432
0
    });
1433
0
}
1434
1435
void
1436
MediaDecoder::GetMozDebugReaderData(nsACString& aString)
1437
0
{
1438
0
  aString += nsPrintfCString("Container Type: %s\n",
1439
0
                             ContainerType().Type().AsString().get());
1440
0
  if (mReader) {
1441
0
    mReader->GetMozDebugReaderData(aString);
1442
0
  }
1443
0
}
1444
1445
void
1446
MediaDecoder::NotifyAudibleStateChanged()
1447
0
{
1448
0
  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1449
0
  GetOwner()->SetAudibleState(mIsAudioDataAudible);
1450
0
}
1451
1452
MediaMemoryTracker::MediaMemoryTracker()
1453
0
{
1454
0
}
1455
1456
void
1457
MediaMemoryTracker::InitMemoryReporter()
1458
0
{
1459
0
  RegisterWeakAsyncMemoryReporter(this);
1460
0
}
1461
1462
MediaMemoryTracker::~MediaMemoryTracker()
1463
0
{
1464
0
  UnregisterWeakMemoryReporter(this);
1465
0
}
1466
1467
} // namespace mozilla
1468
1469
// avoid redefined macro in unified build
1470
#undef DUMP
1471
#undef LOG
1472
#undef NS_DispatchToMainThread