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