Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "MediaDecoder.h"
8 #include "mozilla/FloatingPoint.h"
9 #include "mozilla/MathAlgorithms.h"
10 #include <limits>
11 #include "nsIObserver.h"
12 #include "nsTArray.h"
13 #include "VideoUtils.h"
14 #include "MediaDecoderStateMachine.h"
15 #include "mozilla/dom/TimeRanges.h"
16 #include "ImageContainer.h"
17 #include "MediaResource.h"
18 #include "nsError.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/StaticPtr.h"
21 #include "nsIMemoryReporter.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsITimer.h"
24 #include <algorithm>
25 #include "MediaShutdownManager.h"
26 #include "AudioChannelService.h"
28 #ifdef MOZ_WMF
29 #include "WMFDecoder.h"
30 #endif
32 using namespace mozilla::layers;
33 using namespace mozilla::dom;
35 namespace mozilla {
37 // Number of milliseconds between progress events as defined by spec
38 static const uint32_t PROGRESS_MS = 350;
40 // Number of milliseconds of no data before a stall event is fired as defined by spec
41 static const uint32_t STALL_MS = 3000;
43 // Number of estimated seconds worth of data we need to have buffered
44 // ahead of the current playback position before we allow the media decoder
45 // to report that it can play through the entire media without the decode
46 // catching up with the download. Having this margin make the
47 // MediaDecoder::CanPlayThrough() calculation more stable in the case of
48 // fluctuating bitrates.
49 static const int64_t CAN_PLAY_THROUGH_MARGIN = 1;
51 // avoid redefined macro in unified build
52 #undef DECODER_LOG
54 #ifdef PR_LOGGING
55 PRLogModuleInfo* gMediaDecoderLog;
56 #define DECODER_LOG(type, msg, ...) \
57 PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, this, ##__VA_ARGS__))
58 #else
59 #define DECODER_LOG(type, msg, ...)
60 #endif
62 class MediaMemoryTracker : public nsIMemoryReporter
63 {
64 NS_DECL_THREADSAFE_ISUPPORTS
65 NS_DECL_NSIMEMORYREPORTER
67 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
69 MediaMemoryTracker();
70 virtual ~MediaMemoryTracker();
71 void InitMemoryReporter();
73 static StaticRefPtr<MediaMemoryTracker> sUniqueInstance;
75 static MediaMemoryTracker* UniqueInstance() {
76 if (!sUniqueInstance) {
77 sUniqueInstance = new MediaMemoryTracker();
78 sUniqueInstance->InitMemoryReporter();
79 }
80 return sUniqueInstance;
81 }
83 typedef nsTArray<MediaDecoder*> DecodersArray;
84 static DecodersArray& Decoders() {
85 return UniqueInstance()->mDecoders;
86 }
88 DecodersArray mDecoders;
90 public:
91 static void AddMediaDecoder(MediaDecoder* aDecoder)
92 {
93 Decoders().AppendElement(aDecoder);
94 }
96 static void RemoveMediaDecoder(MediaDecoder* aDecoder)
97 {
98 DecodersArray& decoders = Decoders();
99 decoders.RemoveElement(aDecoder);
100 if (decoders.IsEmpty()) {
101 sUniqueInstance = nullptr;
102 }
103 }
104 };
106 StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
108 NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
110 NS_IMPL_ISUPPORTS(MediaDecoder, nsIObserver)
112 void MediaDecoder::SetDormantIfNecessary(bool aDormant)
113 {
114 MOZ_ASSERT(NS_IsMainThread());
115 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
117 if (!mDecoderStateMachine || !mDecoderStateMachine->IsDormantNeeded() || (mPlayState == PLAY_STATE_SHUTDOWN)) {
118 return;
119 }
121 if (mIsDormant == aDormant) {
122 // no change to dormant state
123 return;
124 }
126 if(aDormant) {
127 // enter dormant state
128 StopProgress();
129 DestroyDecodedStream();
130 mDecoderStateMachine->SetDormant(true);
132 mRequestedSeekTarget = SeekTarget(mCurrentTime, SeekTarget::Accurate);
133 if (mPlayState == PLAY_STATE_PLAYING){
134 mNextState = PLAY_STATE_PLAYING;
135 } else {
136 mNextState = PLAY_STATE_PAUSED;
137 }
138 mNextState = mPlayState;
139 mIsDormant = true;
140 mIsExitingDormant = false;
141 ChangeState(PLAY_STATE_LOADING);
142 } else if ((aDormant != true) && (mPlayState == PLAY_STATE_LOADING)) {
143 // exit dormant state
144 // trigger to state machine.
145 mDecoderStateMachine->SetDormant(false);
146 mIsExitingDormant = true;
147 }
148 }
150 void MediaDecoder::Pause()
151 {
152 MOZ_ASSERT(NS_IsMainThread());
153 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
154 if ((mPlayState == PLAY_STATE_LOADING && mIsDormant) || mPlayState == PLAY_STATE_SEEKING || mPlayState == PLAY_STATE_ENDED) {
155 mNextState = PLAY_STATE_PAUSED;
156 return;
157 }
159 ChangeState(PLAY_STATE_PAUSED);
160 }
162 void MediaDecoder::SetVolume(double aVolume)
163 {
164 MOZ_ASSERT(NS_IsMainThread());
165 mInitialVolume = aVolume;
166 if (mDecoderStateMachine) {
167 mDecoderStateMachine->SetVolume(aVolume);
168 }
169 }
171 void MediaDecoder::SetAudioCaptured(bool aCaptured)
172 {
173 MOZ_ASSERT(NS_IsMainThread());
174 mInitialAudioCaptured = aCaptured;
175 if (mDecoderStateMachine) {
176 mDecoderStateMachine->SetAudioCaptured(aCaptured);
177 }
178 }
180 void MediaDecoder::ConnectDecodedStreamToOutputStream(OutputStreamData* aStream)
181 {
182 NS_ASSERTION(!aStream->mPort, "Already connected?");
184 // The output stream must stay in sync with the decoded stream, so if
185 // either stream is blocked, we block the other.
186 aStream->mPort = aStream->mStream->AllocateInputPort(mDecodedStream->mStream,
187 MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT);
188 // Unblock the output stream now. While it's connected to mDecodedStream,
189 // mDecodedStream is responsible for controlling blocking.
190 aStream->mStream->ChangeExplicitBlockerCount(-1);
191 }
193 MediaDecoder::DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder,
194 int64_t aInitialTime,
195 SourceMediaStream* aStream)
196 : mLastAudioPacketTime(-1),
197 mLastAudioPacketEndTime(-1),
198 mAudioFramesWritten(0),
199 mInitialTime(aInitialTime),
200 mNextVideoTime(aInitialTime),
201 mDecoder(aDecoder),
202 mStreamInitialized(false),
203 mHaveSentFinish(false),
204 mHaveSentFinishAudio(false),
205 mHaveSentFinishVideo(false),
206 mStream(aStream),
207 mHaveBlockedForPlayState(false),
208 mHaveBlockedForStateMachineNotPlaying(false)
209 {
210 mListener = new DecodedStreamGraphListener(mStream, this);
211 mStream->AddListener(mListener);
212 }
214 MediaDecoder::DecodedStreamData::~DecodedStreamData()
215 {
216 mListener->Forget();
217 mStream->Destroy();
218 }
220 MediaDecoder::DecodedStreamGraphListener::DecodedStreamGraphListener(MediaStream* aStream,
221 DecodedStreamData* aData)
222 : mData(aData),
223 mMutex("MediaDecoder::DecodedStreamData::mMutex"),
224 mStream(aStream),
225 mLastOutputTime(aStream->GetCurrentTime()),
226 mStreamFinishedOnMainThread(false)
227 {
228 }
230 void
231 MediaDecoder::DecodedStreamGraphListener::NotifyOutput(MediaStreamGraph* aGraph,
232 GraphTime aCurrentTime)
233 {
234 MutexAutoLock lock(mMutex);
235 if (mStream) {
236 mLastOutputTime = mStream->GraphTimeToStreamTime(aCurrentTime);
237 }
238 }
240 void
241 MediaDecoder::DecodedStreamGraphListener::DoNotifyFinished()
242 {
243 if (mData && mData->mDecoder) {
244 if (mData->mDecoder->GetState() == PLAY_STATE_PLAYING) {
245 nsCOMPtr<nsIRunnable> event =
246 NS_NewRunnableMethod(mData->mDecoder, &MediaDecoder::PlaybackEnded);
247 NS_DispatchToCurrentThread(event);
248 }
249 }
251 MutexAutoLock lock(mMutex);
252 mStreamFinishedOnMainThread = true;
253 }
255 void
256 MediaDecoder::DecodedStreamGraphListener::NotifyFinished(MediaStreamGraph* aGraph)
257 {
258 nsCOMPtr<nsIRunnable> event =
259 NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
260 aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
261 }
263 void MediaDecoder::DestroyDecodedStream()
264 {
265 MOZ_ASSERT(NS_IsMainThread());
266 GetReentrantMonitor().AssertCurrentThreadIn();
268 // All streams are having their SourceMediaStream disconnected, so they
269 // need to be explicitly blocked again.
270 for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
271 OutputStreamData& os = mOutputStreams[i];
272 // During cycle collection, nsDOMMediaStream can be destroyed and send
273 // its Destroy message before this decoder is destroyed. So we have to
274 // be careful not to send any messages after the Destroy().
275 if (os.mStream->IsDestroyed()) {
276 // Probably the DOM MediaStream was GCed. Clean up.
277 os.mPort->Destroy();
278 mOutputStreams.RemoveElementAt(i);
279 continue;
280 }
281 os.mStream->ChangeExplicitBlockerCount(1);
282 // Explicitly remove all existing ports. This is not strictly necessary but it's
283 // good form.
284 os.mPort->Destroy();
285 os.mPort = nullptr;
286 }
288 mDecodedStream = nullptr;
289 }
291 void MediaDecoder::UpdateStreamBlockingForStateMachinePlaying()
292 {
293 GetReentrantMonitor().AssertCurrentThreadIn();
294 if (!mDecodedStream) {
295 return;
296 }
297 if (mDecoderStateMachine) {
298 mDecoderStateMachine->SetSyncPointForMediaStream();
299 }
300 bool blockForStateMachineNotPlaying =
301 mDecoderStateMachine && !mDecoderStateMachine->IsPlaying() &&
302 mDecoderStateMachine->GetState() != MediaDecoderStateMachine::DECODER_STATE_COMPLETED;
303 if (blockForStateMachineNotPlaying != mDecodedStream->mHaveBlockedForStateMachineNotPlaying) {
304 mDecodedStream->mHaveBlockedForStateMachineNotPlaying = blockForStateMachineNotPlaying;
305 int32_t delta = blockForStateMachineNotPlaying ? 1 : -1;
306 if (NS_IsMainThread()) {
307 mDecodedStream->mStream->ChangeExplicitBlockerCount(delta);
308 } else {
309 nsCOMPtr<nsIRunnable> runnable =
310 NS_NewRunnableMethodWithArg<int32_t>(mDecodedStream->mStream.get(),
311 &MediaStream::ChangeExplicitBlockerCount, delta);
312 NS_DispatchToMainThread(runnable);
313 }
314 }
315 }
317 void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
318 {
319 MOZ_ASSERT(NS_IsMainThread());
320 GetReentrantMonitor().AssertCurrentThreadIn();
321 DECODER_LOG(PR_LOG_DEBUG, "RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
323 DestroyDecodedStream();
325 mDecodedStream = new DecodedStreamData(this, aStartTimeUSecs,
326 MediaStreamGraph::GetInstance()->CreateSourceStream(nullptr));
328 // Note that the delay between removing ports in DestroyDecodedStream
329 // and adding new ones won't cause a glitch since all graph operations
330 // between main-thread stable states take effect atomically.
331 for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
332 OutputStreamData& os = mOutputStreams[i];
333 if (os.mStream->IsDestroyed()) {
334 // Probably the DOM MediaStream was GCed. Clean up.
335 // No need to destroy the port; all ports have been destroyed here.
336 mOutputStreams.RemoveElementAt(i);
337 continue;
338 }
339 ConnectDecodedStreamToOutputStream(&os);
340 }
341 UpdateStreamBlockingForStateMachinePlaying();
343 mDecodedStream->mHaveBlockedForPlayState = mPlayState != PLAY_STATE_PLAYING;
344 if (mDecodedStream->mHaveBlockedForPlayState) {
345 mDecodedStream->mStream->ChangeExplicitBlockerCount(1);
346 }
347 }
349 void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
350 bool aFinishWhenEnded)
351 {
352 MOZ_ASSERT(NS_IsMainThread());
353 DECODER_LOG(PR_LOG_DEBUG, "AddOutputStream aStream=%p!", aStream);
355 {
356 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
357 if (!mDecodedStream) {
358 RecreateDecodedStream(mDecoderStateMachine ?
359 int64_t(mDecoderStateMachine->GetCurrentTime()*USECS_PER_S) : 0);
360 }
361 OutputStreamData* os = mOutputStreams.AppendElement();
362 os->Init(aStream, aFinishWhenEnded);
363 ConnectDecodedStreamToOutputStream(os);
364 if (aFinishWhenEnded) {
365 // Ensure that aStream finishes the moment mDecodedStream does.
366 aStream->SetAutofinish(true);
367 }
368 }
370 // This can be called before Load(), in which case our mDecoderStateMachine
371 // won't have been created yet and we can rely on Load() to schedule it
372 // once it is created.
373 if (mDecoderStateMachine) {
374 // Make sure the state machine thread runs so that any buffered data
375 // is fed into our stream.
376 ScheduleStateMachineThread();
377 }
378 }
380 double MediaDecoder::GetDuration()
381 {
382 MOZ_ASSERT(NS_IsMainThread());
383 if (mInfiniteStream) {
384 return std::numeric_limits<double>::infinity();
385 }
386 if (mDuration >= 0) {
387 return static_cast<double>(mDuration) / static_cast<double>(USECS_PER_S);
388 }
389 return std::numeric_limits<double>::quiet_NaN();
390 }
392 int64_t MediaDecoder::GetMediaDuration()
393 {
394 NS_ENSURE_TRUE(GetStateMachine(), -1);
395 return GetStateMachine()->GetDuration();
396 }
398 void MediaDecoder::SetInfinite(bool aInfinite)
399 {
400 MOZ_ASSERT(NS_IsMainThread());
401 mInfiniteStream = aInfinite;
402 }
404 bool MediaDecoder::IsInfinite()
405 {
406 MOZ_ASSERT(NS_IsMainThread());
407 return mInfiniteStream;
408 }
410 MediaDecoder::MediaDecoder() :
411 mDecoderPosition(0),
412 mPlaybackPosition(0),
413 mCurrentTime(0.0),
414 mInitialVolume(0.0),
415 mInitialPlaybackRate(1.0),
416 mInitialPreservesPitch(true),
417 mDuration(-1),
418 mTransportSeekable(true),
419 mMediaSeekable(true),
420 mSameOriginMedia(false),
421 mReentrantMonitor("media.decoder"),
422 mIsDormant(false),
423 mIsExitingDormant(false),
424 mPlayState(PLAY_STATE_PAUSED),
425 mNextState(PLAY_STATE_PAUSED),
426 mCalledResourceLoaded(false),
427 mIgnoreProgressData(false),
428 mInfiniteStream(false),
429 mOwner(nullptr),
430 mPinnedForSeek(false),
431 mShuttingDown(false),
432 mPausedForPlaybackRateNull(false),
433 mMinimizePreroll(false)
434 {
435 MOZ_COUNT_CTOR(MediaDecoder);
436 MOZ_ASSERT(NS_IsMainThread());
437 MediaMemoryTracker::AddMediaDecoder(this);
438 #ifdef PR_LOGGING
439 if (!gMediaDecoderLog) {
440 gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
441 }
442 #endif
444 mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
445 }
447 bool MediaDecoder::Init(MediaDecoderOwner* aOwner)
448 {
449 MOZ_ASSERT(NS_IsMainThread());
450 mOwner = aOwner;
451 mVideoFrameContainer = aOwner->GetVideoFrameContainer();
452 MediaShutdownManager::Instance().Register(this);
453 return true;
454 }
456 void MediaDecoder::Shutdown()
457 {
458 MOZ_ASSERT(NS_IsMainThread());
460 if (mShuttingDown)
461 return;
463 mShuttingDown = true;
465 {
466 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
467 DestroyDecodedStream();
468 }
470 // This changes the decoder state to SHUTDOWN and does other things
471 // necessary to unblock the state machine thread if it's blocked, so
472 // the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
473 if (mDecoderStateMachine) {
474 mDecoderStateMachine->Shutdown();
475 }
477 // Force any outstanding seek and byterange requests to complete
478 // to prevent shutdown from deadlocking.
479 if (mResource) {
480 mResource->Close();
481 }
483 ChangeState(PLAY_STATE_SHUTDOWN);
485 StopProgress();
486 mOwner = nullptr;
488 MediaShutdownManager::Instance().Unregister(this);
489 }
491 MediaDecoder::~MediaDecoder()
492 {
493 MOZ_ASSERT(NS_IsMainThread());
494 MediaMemoryTracker::RemoveMediaDecoder(this);
495 UnpinForSeek();
496 MOZ_COUNT_DTOR(MediaDecoder);
497 }
499 nsresult MediaDecoder::OpenResource(nsIStreamListener** aStreamListener)
500 {
501 MOZ_ASSERT(NS_IsMainThread());
502 if (aStreamListener) {
503 *aStreamListener = nullptr;
504 }
506 {
507 // Hold the lock while we do this to set proper lock ordering
508 // expectations for dynamic deadlock detectors: decoder lock(s)
509 // should be grabbed before the cache lock
510 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
512 nsresult rv = mResource->Open(aStreamListener);
513 if (NS_FAILED(rv)) {
514 DECODER_LOG(PR_LOG_WARNING, "Failed to open stream!");
515 return rv;
516 }
517 }
518 return NS_OK;
519 }
521 nsresult MediaDecoder::Load(nsIStreamListener** aStreamListener,
522 MediaDecoder* aCloneDonor)
523 {
524 MOZ_ASSERT(NS_IsMainThread());
526 nsresult rv = OpenResource(aStreamListener);
527 NS_ENSURE_SUCCESS(rv, rv);
529 mDecoderStateMachine = CreateStateMachine();
530 if (!mDecoderStateMachine) {
531 DECODER_LOG(PR_LOG_WARNING, "Failed to create state machine!");
532 return NS_ERROR_FAILURE;
533 }
535 return InitializeStateMachine(aCloneDonor);
536 }
538 nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
539 {
540 MOZ_ASSERT(NS_IsMainThread());
541 NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
543 MediaDecoder* cloneDonor = static_cast<MediaDecoder*>(aCloneDonor);
544 if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ?
545 cloneDonor->mDecoderStateMachine : nullptr))) {
546 DECODER_LOG(PR_LOG_WARNING, "Failed to init state machine!");
547 return NS_ERROR_FAILURE;
548 }
549 {
550 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
551 mDecoderStateMachine->SetTransportSeekable(mTransportSeekable);
552 mDecoderStateMachine->SetMediaSeekable(mMediaSeekable);
553 mDecoderStateMachine->SetDuration(mDuration);
554 mDecoderStateMachine->SetVolume(mInitialVolume);
555 mDecoderStateMachine->SetAudioCaptured(mInitialAudioCaptured);
556 SetPlaybackRate(mInitialPlaybackRate);
557 mDecoderStateMachine->SetPreservesPitch(mInitialPreservesPitch);
558 if (mMinimizePreroll) {
559 mDecoderStateMachine->SetMinimizePrerollUntilPlaybackStarts();
560 }
561 }
563 ChangeState(PLAY_STATE_LOADING);
565 return ScheduleStateMachineThread();
566 }
568 void MediaDecoder::SetMinimizePrerollUntilPlaybackStarts()
569 {
570 MOZ_ASSERT(NS_IsMainThread());
571 mMinimizePreroll = true;
572 }
574 nsresult MediaDecoder::ScheduleStateMachineThread()
575 {
576 MOZ_ASSERT(NS_IsMainThread());
577 NS_ASSERTION(mDecoderStateMachine,
578 "Must have state machine to start state machine thread");
579 NS_ENSURE_STATE(mDecoderStateMachine);
581 if (mShuttingDown)
582 return NS_OK;
583 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
584 return mDecoderStateMachine->ScheduleStateMachine();
585 }
587 nsresult MediaDecoder::Play()
588 {
589 MOZ_ASSERT(NS_IsMainThread());
590 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
591 NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
592 nsresult res = ScheduleStateMachineThread();
593 NS_ENSURE_SUCCESS(res,res);
594 if ((mPlayState == PLAY_STATE_LOADING && mIsDormant) || mPlayState == PLAY_STATE_SEEKING) {
595 mNextState = PLAY_STATE_PLAYING;
596 return NS_OK;
597 }
598 if (mPlayState == PLAY_STATE_ENDED)
599 return Seek(0, SeekTarget::PrevSyncPoint);
601 ChangeState(PLAY_STATE_PLAYING);
602 return NS_OK;
603 }
605 nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
606 {
607 MOZ_ASSERT(NS_IsMainThread());
608 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
610 NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value.");
612 int64_t timeUsecs = 0;
613 nsresult rv = SecondsToUsecs(aTime, timeUsecs);
614 NS_ENSURE_SUCCESS(rv, rv);
616 mRequestedSeekTarget = SeekTarget(timeUsecs, aSeekType);
617 mCurrentTime = aTime;
619 // If we are already in the seeking state, then setting mRequestedSeekTarget
620 // above will result in the new seek occurring when the current seek
621 // completes.
622 if ((mPlayState != PLAY_STATE_LOADING || !mIsDormant) && mPlayState != PLAY_STATE_SEEKING) {
623 bool paused = false;
624 if (mOwner) {
625 paused = mOwner->GetPaused();
626 }
627 mNextState = paused ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING;
628 PinForSeek();
629 ChangeState(PLAY_STATE_SEEKING);
630 }
632 return ScheduleStateMachineThread();
633 }
635 bool MediaDecoder::IsLogicallyPlaying()
636 {
637 GetReentrantMonitor().AssertCurrentThreadIn();
638 return mPlayState == PLAY_STATE_PLAYING ||
639 mNextState == PLAY_STATE_PLAYING;
640 }
642 double MediaDecoder::GetCurrentTime()
643 {
644 MOZ_ASSERT(NS_IsMainThread());
645 return mCurrentTime;
646 }
648 already_AddRefed<nsIPrincipal> MediaDecoder::GetCurrentPrincipal()
649 {
650 MOZ_ASSERT(NS_IsMainThread());
651 return mResource ? mResource->GetCurrentPrincipal() : nullptr;
652 }
654 void MediaDecoder::QueueMetadata(int64_t aPublishTime,
655 int aChannels,
656 int aRate,
657 bool aHasAudio,
658 bool aHasVideo,
659 MetadataTags* aTags)
660 {
661 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
662 GetReentrantMonitor().AssertCurrentThreadIn();
663 mDecoderStateMachine->QueueMetadata(aPublishTime, aChannels, aRate, aHasAudio, aHasVideo, aTags);
664 }
666 bool
667 MediaDecoder::IsDataCachedToEndOfResource()
668 {
669 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
670 return (mResource &&
671 mResource->IsDataCachedToEndOfResource(mDecoderPosition));
672 }
674 void MediaDecoder::MetadataLoaded(int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags)
675 {
676 MOZ_ASSERT(NS_IsMainThread());
677 if (mShuttingDown) {
678 return;
679 }
681 {
682 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
683 if (mPlayState == PLAY_STATE_LOADING && mIsDormant && !mIsExitingDormant) {
684 return;
685 } else if (mPlayState == PLAY_STATE_LOADING && mIsDormant && mIsExitingDormant) {
686 mIsDormant = false;
687 mIsExitingDormant = false;
688 }
689 mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
690 // Duration has changed so we should recompute playback rate
691 UpdatePlaybackRate();
692 }
694 if (mDuration == -1) {
695 SetInfinite(true);
696 }
698 if (mOwner) {
699 // Make sure the element and the frame (if any) are told about
700 // our new size.
701 Invalidate();
702 mOwner->MetadataLoaded(aChannels, aRate, aHasAudio, aHasVideo, aTags);
703 }
705 if (!mCalledResourceLoaded) {
706 StartProgress();
707 } else if (mOwner) {
708 // Resource was loaded during metadata loading, when progress
709 // events are being ignored. Fire the final progress event.
710 mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
711 }
713 // Only inform the element of FirstFrameLoaded if not doing a load() in order
714 // to fulfill a seek, otherwise we'll get multiple loadedfirstframe events.
715 bool notifyResourceIsLoaded = !mCalledResourceLoaded &&
716 IsDataCachedToEndOfResource();
717 if (mOwner) {
718 mOwner->FirstFrameLoaded(notifyResourceIsLoaded);
719 }
721 // This can run cache callbacks.
722 mResource->EnsureCacheUpToDate();
724 // The element can run javascript via events
725 // before reaching here, so only change the
726 // state if we're still set to the original
727 // loading state.
728 if (mPlayState == PLAY_STATE_LOADING) {
729 if (mRequestedSeekTarget.IsValid()) {
730 ChangeState(PLAY_STATE_SEEKING);
731 }
732 else {
733 ChangeState(mNextState);
734 }
735 }
737 if (notifyResourceIsLoaded) {
738 ResourceLoaded();
739 }
741 // Run NotifySuspendedStatusChanged now to give us a chance to notice
742 // that autoplay should run.
743 NotifySuspendedStatusChanged();
744 }
746 void MediaDecoder::ResourceLoaded()
747 {
748 MOZ_ASSERT(NS_IsMainThread());
750 // Don't handle ResourceLoaded if we are shutting down, or if
751 // we need to ignore progress data due to seeking (in the case
752 // that the seek results in reaching end of file, we get a bogus call
753 // to ResourceLoaded).
754 if (mShuttingDown)
755 return;
757 {
758 // If we are seeking or loading then the resource loaded notification we get
759 // should be ignored, since it represents the end of the seek request.
760 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
761 if (mIgnoreProgressData || mCalledResourceLoaded || mPlayState == PLAY_STATE_LOADING)
762 return;
764 Progress(false);
766 mCalledResourceLoaded = true;
767 StopProgress();
768 }
770 // Ensure the final progress event gets fired
771 if (mOwner) {
772 mOwner->ResourceLoaded();
773 }
774 }
776 void MediaDecoder::ResetConnectionState()
777 {
778 MOZ_ASSERT(NS_IsMainThread());
779 if (mShuttingDown)
780 return;
782 if (mOwner) {
783 // Notify the media element that connection gets lost.
784 mOwner->ResetConnectionState();
785 }
787 // Since we have notified the media element the connection
788 // lost event, the decoder will be reloaded when user tries
789 // to play the Rtsp streaming next time.
790 Shutdown();
791 }
793 void MediaDecoder::NetworkError()
794 {
795 MOZ_ASSERT(NS_IsMainThread());
796 if (mShuttingDown)
797 return;
799 if (mOwner)
800 mOwner->NetworkError();
802 Shutdown();
803 }
805 void MediaDecoder::DecodeError()
806 {
807 MOZ_ASSERT(NS_IsMainThread());
808 if (mShuttingDown)
809 return;
811 if (mOwner)
812 mOwner->DecodeError();
814 Shutdown();
815 }
817 void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin)
818 {
819 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
820 mSameOriginMedia = aSameOrigin;
821 }
823 bool MediaDecoder::IsSameOriginMedia()
824 {
825 GetReentrantMonitor().AssertCurrentThreadIn();
826 return mSameOriginMedia;
827 }
829 bool MediaDecoder::IsSeeking() const
830 {
831 MOZ_ASSERT(NS_IsMainThread());
832 return mPlayState == PLAY_STATE_SEEKING;
833 }
835 bool MediaDecoder::IsEnded() const
836 {
837 MOZ_ASSERT(NS_IsMainThread());
838 return mPlayState == PLAY_STATE_ENDED || mPlayState == PLAY_STATE_SHUTDOWN;
839 }
841 void MediaDecoder::PlaybackEnded()
842 {
843 MOZ_ASSERT(NS_IsMainThread());
845 if (mShuttingDown || mPlayState == MediaDecoder::PLAY_STATE_SEEKING)
846 return;
848 {
849 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
851 for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
852 OutputStreamData& os = mOutputStreams[i];
853 if (os.mStream->IsDestroyed()) {
854 // Probably the DOM MediaStream was GCed. Clean up.
855 os.mPort->Destroy();
856 mOutputStreams.RemoveElementAt(i);
857 continue;
858 }
859 if (os.mFinishWhenEnded) {
860 // Shouldn't really be needed since mDecodedStream should already have
861 // finished, but doesn't hurt.
862 os.mStream->Finish();
863 os.mPort->Destroy();
864 // Not really needed but it keeps the invariant that a stream not
865 // connected to mDecodedStream is explicity blocked.
866 os.mStream->ChangeExplicitBlockerCount(1);
867 mOutputStreams.RemoveElementAt(i);
868 }
869 }
870 }
872 PlaybackPositionChanged();
873 ChangeState(PLAY_STATE_ENDED);
874 InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);
876 UpdateReadyStateForData();
877 if (mOwner) {
878 mOwner->PlaybackEnded();
879 }
881 // This must be called after |mOwner->PlaybackEnded()| call above, in order
882 // to fire the required durationchange.
883 if (IsInfinite()) {
884 SetInfinite(false);
885 }
886 }
888 NS_IMETHODIMP MediaDecoder::Observe(nsISupports *aSubjet,
889 const char *aTopic,
890 const char16_t *someData)
891 {
892 MOZ_ASSERT(NS_IsMainThread());
893 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
894 Shutdown();
895 }
897 return NS_OK;
898 }
900 MediaDecoder::Statistics
901 MediaDecoder::GetStatistics()
902 {
903 MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread());
904 Statistics result;
906 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
907 if (mResource) {
908 result.mDownloadRate =
909 mResource->GetDownloadRate(&result.mDownloadRateReliable);
910 result.mDownloadPosition =
911 mResource->GetCachedDataEnd(mDecoderPosition);
912 result.mTotalBytes = mResource->GetLength();
913 result.mPlaybackRate = ComputePlaybackRate(&result.mPlaybackRateReliable);
914 result.mDecoderPosition = mDecoderPosition;
915 result.mPlaybackPosition = mPlaybackPosition;
916 }
917 else {
918 result.mDownloadRate = 0;
919 result.mDownloadRateReliable = true;
920 result.mPlaybackRate = 0;
921 result.mPlaybackRateReliable = true;
922 result.mDecoderPosition = 0;
923 result.mPlaybackPosition = 0;
924 result.mDownloadPosition = 0;
925 result.mTotalBytes = 0;
926 }
928 return result;
929 }
931 double MediaDecoder::ComputePlaybackRate(bool* aReliable)
932 {
933 GetReentrantMonitor().AssertCurrentThreadIn();
934 MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread() || OnDecodeThread());
936 int64_t length = mResource ? mResource->GetLength() : -1;
937 if (mDuration >= 0 && length >= 0) {
938 *aReliable = true;
939 return length * static_cast<double>(USECS_PER_S) / mDuration;
940 }
941 return mPlaybackStatistics.GetRateAtLastStop(aReliable);
942 }
944 void MediaDecoder::UpdatePlaybackRate()
945 {
946 MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread());
947 GetReentrantMonitor().AssertCurrentThreadIn();
948 if (!mResource)
949 return;
950 bool reliable;
951 uint32_t rate = uint32_t(ComputePlaybackRate(&reliable));
952 if (reliable) {
953 // Avoid passing a zero rate
954 rate = std::max(rate, 1u);
955 }
956 else {
957 // Set a minimum rate of 10,000 bytes per second ... sometimes we just
958 // don't have good data
959 rate = std::max(rate, 10000u);
960 }
961 mResource->SetPlaybackRate(rate);
962 }
964 void MediaDecoder::NotifySuspendedStatusChanged()
965 {
966 MOZ_ASSERT(NS_IsMainThread());
967 if (!mResource)
968 return;
969 bool suspended = mResource->IsSuspendedByCache();
970 if (mOwner) {
971 mOwner->NotifySuspendedByCache(suspended);
972 UpdateReadyStateForData();
973 }
974 }
976 void MediaDecoder::NotifyBytesDownloaded()
977 {
978 MOZ_ASSERT(NS_IsMainThread());
979 {
980 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
981 UpdatePlaybackRate();
982 }
983 UpdateReadyStateForData();
984 Progress(false);
985 }
987 void MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
988 {
989 MOZ_ASSERT(NS_IsMainThread());
991 if (aStatus == NS_BINDING_ABORTED) {
992 // Download has been cancelled by user.
993 if (mOwner) {
994 mOwner->LoadAborted();
995 }
996 return;
997 }
999 {
1000 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1001 UpdatePlaybackRate();
1002 }
1004 if (NS_SUCCEEDED(aStatus)) {
1005 ResourceLoaded();
1006 }
1007 else if (aStatus != NS_BASE_STREAM_CLOSED) {
1008 NetworkError();
1009 }
1010 UpdateReadyStateForData();
1011 }
1013 void MediaDecoder::NotifyPrincipalChanged()
1014 {
1015 if (mOwner) {
1016 mOwner->NotifyDecoderPrincipalChanged();
1017 }
1018 }
1020 void MediaDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset)
1021 {
1022 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1023 NS_ENSURE_TRUE_VOID(mDecoderStateMachine);
1024 if (mIgnoreProgressData) {
1025 return;
1026 }
1027 if (aOffset >= mDecoderPosition) {
1028 mPlaybackStatistics.AddBytes(aBytes);
1029 }
1030 mDecoderPosition = aOffset + aBytes;
1031 }
1033 void MediaDecoder::UpdateReadyStateForData()
1034 {
1035 MOZ_ASSERT(NS_IsMainThread());
1036 if (!mOwner || mShuttingDown || !mDecoderStateMachine)
1037 return;
1038 MediaDecoderOwner::NextFrameStatus frameStatus =
1039 mDecoderStateMachine->GetNextFrameStatus();
1040 mOwner->UpdateReadyStateForData(frameStatus);
1041 }
1043 void MediaDecoder::SeekingStopped()
1044 {
1045 MOZ_ASSERT(NS_IsMainThread());
1047 if (mShuttingDown)
1048 return;
1050 bool seekWasAborted = false;
1051 {
1052 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1054 // An additional seek was requested while the current seek was
1055 // in operation.
1056 if (mRequestedSeekTarget.IsValid()) {
1057 ChangeState(PLAY_STATE_SEEKING);
1058 seekWasAborted = true;
1059 } else {
1060 UnpinForSeek();
1061 ChangeState(mNextState);
1062 }
1063 }
1065 PlaybackPositionChanged();
1067 if (mOwner) {
1068 UpdateReadyStateForData();
1069 if (!seekWasAborted) {
1070 mOwner->SeekCompleted();
1071 }
1072 }
1073 }
1075 // This is called when seeking stopped *and* we're at the end of the
1076 // media.
1077 void MediaDecoder::SeekingStoppedAtEnd()
1078 {
1079 MOZ_ASSERT(NS_IsMainThread());
1081 if (mShuttingDown)
1082 return;
1084 bool fireEnded = false;
1085 bool seekWasAborted = false;
1086 {
1087 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1089 // An additional seek was requested while the current seek was
1090 // in operation.
1091 if (mRequestedSeekTarget.IsValid()) {
1092 ChangeState(PLAY_STATE_SEEKING);
1093 seekWasAborted = true;
1094 } else {
1095 UnpinForSeek();
1096 fireEnded = true;
1097 ChangeState(PLAY_STATE_ENDED);
1098 }
1099 }
1101 PlaybackPositionChanged();
1103 if (mOwner) {
1104 UpdateReadyStateForData();
1105 if (!seekWasAborted) {
1106 mOwner->SeekCompleted();
1107 if (fireEnded) {
1108 mOwner->PlaybackEnded();
1109 }
1110 }
1111 }
1112 }
1114 void MediaDecoder::SeekingStarted()
1115 {
1116 MOZ_ASSERT(NS_IsMainThread());
1117 if (mShuttingDown)
1118 return;
1120 if (mOwner) {
1121 UpdateReadyStateForData();
1122 mOwner->SeekStarted();
1123 }
1124 }
1126 void MediaDecoder::ChangeState(PlayState aState)
1127 {
1128 MOZ_ASSERT(NS_IsMainThread());
1129 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1131 if (mNextState == aState) {
1132 mNextState = PLAY_STATE_PAUSED;
1133 }
1135 if ((mPlayState == PLAY_STATE_LOADING && mIsDormant && aState != PLAY_STATE_SHUTDOWN) ||
1136 mPlayState == PLAY_STATE_SHUTDOWN) {
1137 GetReentrantMonitor().NotifyAll();
1138 return;
1139 }
1141 if (mDecodedStream) {
1142 bool blockForPlayState = aState != PLAY_STATE_PLAYING;
1143 if (mDecodedStream->mHaveBlockedForPlayState != blockForPlayState) {
1144 mDecodedStream->mStream->ChangeExplicitBlockerCount(blockForPlayState ? 1 : -1);
1145 mDecodedStream->mHaveBlockedForPlayState = blockForPlayState;
1146 }
1147 }
1148 mPlayState = aState;
1150 ApplyStateToStateMachine(mPlayState);
1152 if (aState!= PLAY_STATE_LOADING) {
1153 mIsDormant = false;
1154 mIsExitingDormant = false;
1155 }
1157 GetReentrantMonitor().NotifyAll();
1158 }
1160 void MediaDecoder::ApplyStateToStateMachine(PlayState aState)
1161 {
1162 MOZ_ASSERT(NS_IsMainThread());
1163 GetReentrantMonitor().AssertCurrentThreadIn();
1165 if (mDecoderStateMachine) {
1166 switch (aState) {
1167 case PLAY_STATE_PLAYING:
1168 mDecoderStateMachine->Play();
1169 break;
1170 case PLAY_STATE_SEEKING:
1171 mDecoderStateMachine->Seek(mRequestedSeekTarget);
1172 mRequestedSeekTarget.Reset();
1173 break;
1174 default:
1175 /* No action needed */
1176 break;
1177 }
1178 }
1179 }
1181 void MediaDecoder::PlaybackPositionChanged()
1182 {
1183 MOZ_ASSERT(NS_IsMainThread());
1184 if (mShuttingDown)
1185 return;
1187 double lastTime = mCurrentTime;
1189 // Control the scope of the monitor so it is not
1190 // held while the timeupdate and the invalidate is run.
1191 {
1192 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1193 if (mDecoderStateMachine) {
1194 if (!IsSeeking()) {
1195 // Only update the current playback position if we're not seeking.
1196 // If we are seeking, the update could have been scheduled on the
1197 // state machine thread while we were playing but after the seek
1198 // algorithm set the current playback position on the main thread,
1199 // and we don't want to override the seek algorithm and change the
1200 // current time after the seek has started but before it has
1201 // completed.
1202 if (GetDecodedStream()) {
1203 mCurrentTime = mDecoderStateMachine->GetCurrentTimeViaMediaStreamSync()/
1204 static_cast<double>(USECS_PER_S);
1205 } else {
1206 mCurrentTime = mDecoderStateMachine->GetCurrentTime();
1207 }
1208 }
1209 mDecoderStateMachine->ClearPositionChangeFlag();
1210 }
1211 }
1213 // Invalidate the frame so any video data is displayed.
1214 // Do this before the timeupdate event so that if that
1215 // event runs JavaScript that queries the media size, the
1216 // frame has reflowed and the size updated beforehand.
1217 Invalidate();
1219 if (mOwner && lastTime != mCurrentTime) {
1220 FireTimeUpdate();
1221 }
1222 }
1224 void MediaDecoder::DurationChanged()
1225 {
1226 MOZ_ASSERT(NS_IsMainThread());
1227 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1228 int64_t oldDuration = mDuration;
1229 mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
1230 // Duration has changed so we should recompute playback rate
1231 UpdatePlaybackRate();
1233 SetInfinite(mDuration == -1);
1235 if (mOwner && oldDuration != mDuration && !IsInfinite()) {
1236 DECODER_LOG(PR_LOG_DEBUG, "Duration changed to %lld", mDuration);
1237 mOwner->DispatchEvent(NS_LITERAL_STRING("durationchange"));
1238 }
1239 }
1241 void MediaDecoder::SetDuration(double aDuration)
1242 {
1243 MOZ_ASSERT(NS_IsMainThread());
1244 if (mozilla::IsInfinite(aDuration)) {
1245 SetInfinite(true);
1246 } else if (IsNaN(aDuration)) {
1247 mDuration = -1;
1248 SetInfinite(true);
1249 } else {
1250 mDuration = static_cast<int64_t>(NS_round(aDuration * static_cast<double>(USECS_PER_S)));
1251 }
1253 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1254 if (mDecoderStateMachine) {
1255 mDecoderStateMachine->SetDuration(mDuration);
1256 }
1258 // Duration has changed so we should recompute playback rate
1259 UpdatePlaybackRate();
1260 }
1262 void MediaDecoder::SetMediaDuration(int64_t aDuration)
1263 {
1264 NS_ENSURE_TRUE_VOID(GetStateMachine());
1265 GetStateMachine()->SetDuration(aDuration);
1266 }
1268 void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
1269 {
1270 if (mPlayState <= PLAY_STATE_LOADING) {
1271 return;
1272 }
1273 NS_ENSURE_TRUE_VOID(GetStateMachine());
1274 GetStateMachine()->UpdateEstimatedDuration(aDuration);
1275 }
1277 void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
1278 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1279 MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread());
1280 mMediaSeekable = aMediaSeekable;
1281 if (mDecoderStateMachine) {
1282 mDecoderStateMachine->SetMediaSeekable(aMediaSeekable);
1283 }
1284 }
1286 void MediaDecoder::SetTransportSeekable(bool aTransportSeekable)
1287 {
1288 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1289 MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread());
1290 mTransportSeekable = aTransportSeekable;
1291 if (mDecoderStateMachine) {
1292 mDecoderStateMachine->SetTransportSeekable(aTransportSeekable);
1293 }
1294 }
1296 bool MediaDecoder::IsTransportSeekable()
1297 {
1298 MOZ_ASSERT(NS_IsMainThread());
1299 return mTransportSeekable;
1300 }
1302 bool MediaDecoder::IsMediaSeekable()
1303 {
1304 NS_ENSURE_TRUE(GetStateMachine(), false);
1305 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1306 MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread());
1307 return mMediaSeekable;
1308 }
1310 nsresult MediaDecoder::GetSeekable(dom::TimeRanges* aSeekable)
1311 {
1312 double initialTime = 0.0;
1314 // We can seek in buffered range if the media is seekable. Also, we can seek
1315 // in unbuffered ranges if the transport level is seekable (local file or the
1316 // server supports range requests, etc.)
1317 if (!IsMediaSeekable()) {
1318 return NS_OK;
1319 } else if (!IsTransportSeekable()) {
1320 return GetBuffered(aSeekable);
1321 } else {
1322 double end = IsInfinite() ? std::numeric_limits<double>::infinity()
1323 : initialTime + GetDuration();
1324 aSeekable->Add(initialTime, end);
1325 return NS_OK;
1326 }
1327 }
1329 void MediaDecoder::SetFragmentEndTime(double aTime)
1330 {
1331 MOZ_ASSERT(NS_IsMainThread());
1332 if (mDecoderStateMachine) {
1333 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1334 mDecoderStateMachine->SetFragmentEndTime(static_cast<int64_t>(aTime * USECS_PER_S));
1335 }
1336 }
1338 void MediaDecoder::SetMediaEndTime(int64_t aTime)
1339 {
1340 NS_ENSURE_TRUE_VOID(GetStateMachine());
1341 GetStateMachine()->SetMediaEndTime(aTime);
1342 }
1344 void MediaDecoder::Suspend()
1345 {
1346 MOZ_ASSERT(NS_IsMainThread());
1347 if (mResource) {
1348 mResource->Suspend(true);
1349 }
1350 }
1352 void MediaDecoder::Resume(bool aForceBuffering)
1353 {
1354 MOZ_ASSERT(NS_IsMainThread());
1355 if (mResource) {
1356 mResource->Resume();
1357 }
1358 if (aForceBuffering) {
1359 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1360 if (mDecoderStateMachine) {
1361 mDecoderStateMachine->StartBuffering();
1362 }
1363 }
1364 }
1366 void MediaDecoder::StopProgressUpdates()
1367 {
1368 MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
1369 GetReentrantMonitor().AssertCurrentThreadIn();
1370 mIgnoreProgressData = true;
1371 if (mResource) {
1372 mResource->SetReadMode(MediaCacheStream::MODE_METADATA);
1373 }
1374 }
1376 void MediaDecoder::StartProgressUpdates()
1377 {
1378 MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
1379 GetReentrantMonitor().AssertCurrentThreadIn();
1380 mIgnoreProgressData = false;
1381 if (mResource) {
1382 mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
1383 mDecoderPosition = mPlaybackPosition = mResource->Tell();
1384 }
1385 }
1387 void MediaDecoder::MoveLoadsToBackground()
1388 {
1389 MOZ_ASSERT(NS_IsMainThread());
1390 if (mResource) {
1391 mResource->MoveLoadsToBackground();
1392 }
1393 }
1395 void MediaDecoder::UpdatePlaybackOffset(int64_t aOffset)
1396 {
1397 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1398 mPlaybackPosition = std::max(aOffset, mPlaybackPosition);
1399 }
1401 bool MediaDecoder::OnStateMachineThread() const
1402 {
1403 return mDecoderStateMachine->OnStateMachineThread();
1404 }
1406 void MediaDecoder::SetPlaybackRate(double aPlaybackRate)
1407 {
1408 if (aPlaybackRate == 0) {
1409 mPausedForPlaybackRateNull = true;
1410 Pause();
1411 return;
1412 } else if (mPausedForPlaybackRateNull) {
1413 // If the playbackRate is no longer null, restart the playback, iff the
1414 // media was playing.
1415 if (mOwner && !mOwner->GetPaused()) {
1416 Play();
1417 }
1418 mPausedForPlaybackRateNull = false;
1419 }
1421 if (mDecoderStateMachine) {
1422 mDecoderStateMachine->SetPlaybackRate(aPlaybackRate);
1423 } else {
1424 mInitialPlaybackRate = aPlaybackRate;
1425 }
1426 }
1428 void MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
1429 {
1430 if (mDecoderStateMachine) {
1431 mDecoderStateMachine->SetPreservesPitch(aPreservesPitch);
1432 } else {
1433 mInitialPreservesPitch = aPreservesPitch;
1434 }
1435 }
1437 bool MediaDecoder::OnDecodeThread() const {
1438 NS_WARN_IF_FALSE(mDecoderStateMachine, "mDecoderStateMachine is null");
1439 return mDecoderStateMachine ? mDecoderStateMachine->OnDecodeThread() : false;
1440 }
1442 ReentrantMonitor& MediaDecoder::GetReentrantMonitor() {
1443 return mReentrantMonitor.GetReentrantMonitor();
1444 }
1446 ImageContainer* MediaDecoder::GetImageContainer()
1447 {
1448 return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer() : nullptr;
1449 }
1451 void MediaDecoder::InvalidateWithFlags(uint32_t aFlags)
1452 {
1453 if (mVideoFrameContainer) {
1454 mVideoFrameContainer->InvalidateWithFlags(aFlags);
1455 }
1456 }
1458 void MediaDecoder::Invalidate()
1459 {
1460 if (mVideoFrameContainer) {
1461 mVideoFrameContainer->Invalidate();
1462 }
1463 }
1465 // Constructs the time ranges representing what segments of the media
1466 // are buffered and playable.
1467 nsresult MediaDecoder::GetBuffered(dom::TimeRanges* aBuffered) {
1468 if (mDecoderStateMachine) {
1469 return mDecoderStateMachine->GetBuffered(aBuffered);
1470 }
1471 return NS_ERROR_FAILURE;
1472 }
1474 size_t MediaDecoder::SizeOfVideoQueue() {
1475 if (mDecoderStateMachine) {
1476 return mDecoderStateMachine->SizeOfVideoQueue();
1477 }
1478 return 0;
1479 }
1481 size_t MediaDecoder::SizeOfAudioQueue() {
1482 if (mDecoderStateMachine) {
1483 return mDecoderStateMachine->SizeOfAudioQueue();
1484 }
1485 return 0;
1486 }
1488 void MediaDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {
1489 if (mDecoderStateMachine) {
1490 mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset);
1491 }
1492 }
1494 void MediaDecoder::UpdatePlaybackPosition(int64_t aTime)
1495 {
1496 mDecoderStateMachine->UpdatePlaybackPosition(aTime);
1497 }
1499 // Provide access to the state machine object
1500 MediaDecoderStateMachine* MediaDecoder::GetStateMachine() const {
1501 return mDecoderStateMachine;
1502 }
1504 void
1505 MediaDecoder::NotifyWaitingForResourcesStatusChanged()
1506 {
1507 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
1508 if (mDecoderStateMachine) {
1509 mDecoderStateMachine->NotifyWaitingForResourcesStatusChanged();
1510 }
1511 }
1513 bool MediaDecoder::IsShutdown() const {
1514 NS_ENSURE_TRUE(GetStateMachine(), true);
1515 return GetStateMachine()->IsShutdown();
1516 }
1518 int64_t MediaDecoder::GetEndMediaTime() const {
1519 NS_ENSURE_TRUE(GetStateMachine(), -1);
1520 return GetStateMachine()->GetEndMediaTime();
1521 }
1523 // Drop reference to state machine. Only called during shutdown dance.
1524 void MediaDecoder::ReleaseStateMachine() {
1525 mDecoderStateMachine = nullptr;
1526 }
1528 MediaDecoderOwner* MediaDecoder::GetMediaOwner() const
1529 {
1530 return mOwner;
1531 }
1533 static void ProgressCallback(nsITimer* aTimer, void* aClosure)
1534 {
1535 MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure);
1536 decoder->Progress(true);
1537 }
1539 void MediaDecoder::Progress(bool aTimer)
1540 {
1541 if (!mOwner)
1542 return;
1544 TimeStamp now = TimeStamp::Now();
1546 if (!aTimer) {
1547 mDataTime = now;
1548 }
1550 // If PROGRESS_MS has passed since the last progress event fired and more
1551 // data has arrived since then, fire another progress event.
1552 if ((mProgressTime.IsNull() ||
1553 now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) &&
1554 !mDataTime.IsNull() &&
1555 now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) {
1556 mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
1557 mProgressTime = now;
1558 }
1560 if (!mDataTime.IsNull() &&
1561 now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) {
1562 mOwner->DownloadStalled();
1563 // Null it out
1564 mDataTime = TimeStamp();
1565 }
1566 }
1568 nsresult MediaDecoder::StartProgress()
1569 {
1570 if (mProgressTimer)
1571 return NS_OK;
1573 mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
1574 return mProgressTimer->InitWithFuncCallback(ProgressCallback,
1575 this,
1576 PROGRESS_MS,
1577 nsITimer::TYPE_REPEATING_SLACK);
1578 }
1580 nsresult MediaDecoder::StopProgress()
1581 {
1582 if (!mProgressTimer)
1583 return NS_OK;
1585 nsresult rv = mProgressTimer->Cancel();
1586 mProgressTimer = nullptr;
1588 return rv;
1589 }
1591 void MediaDecoder::FireTimeUpdate()
1592 {
1593 if (!mOwner)
1594 return;
1595 mOwner->FireTimeUpdate(true);
1596 }
1598 void MediaDecoder::PinForSeek()
1599 {
1600 MediaResource* resource = GetResource();
1601 if (!resource || mPinnedForSeek) {
1602 return;
1603 }
1604 mPinnedForSeek = true;
1605 resource->Pin();
1606 }
1608 void MediaDecoder::UnpinForSeek()
1609 {
1610 MediaResource* resource = GetResource();
1611 if (!resource || !mPinnedForSeek) {
1612 return;
1613 }
1614 mPinnedForSeek = false;
1615 resource->Unpin();
1616 }
1618 bool MediaDecoder::CanPlayThrough()
1619 {
1620 Statistics stats = GetStatistics();
1621 if (!stats.mDownloadRateReliable || !stats.mPlaybackRateReliable) {
1622 return false;
1623 }
1624 int64_t bytesToDownload = stats.mTotalBytes - stats.mDownloadPosition;
1625 int64_t bytesToPlayback = stats.mTotalBytes - stats.mPlaybackPosition;
1626 double timeToDownload = bytesToDownload / stats.mDownloadRate;
1627 double timeToPlay = bytesToPlayback / stats.mPlaybackRate;
1629 if (timeToDownload > timeToPlay) {
1630 // Estimated time to download is greater than the estimated time to play.
1631 // We probably can't play through without having to stop to buffer.
1632 return false;
1633 }
1635 // Estimated time to download is less than the estimated time to play.
1636 // We can probably play through without having to buffer, but ensure that
1637 // we've got a reasonable amount of data buffered after the current
1638 // playback position, so that if the bitrate of the media fluctuates, or if
1639 // our download rate or decode rate estimation is otherwise inaccurate,
1640 // we don't suddenly discover that we need to buffer. This is particularly
1641 // required near the start of the media, when not much data is downloaded.
1642 int64_t readAheadMargin =
1643 static_cast<int64_t>(stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN);
1644 return stats.mTotalBytes == stats.mDownloadPosition ||
1645 stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin;
1646 }
1648 #ifdef MOZ_RAW
1649 bool
1650 MediaDecoder::IsRawEnabled()
1651 {
1652 return Preferences::GetBool("media.raw.enabled");
1653 }
1654 #endif
1656 bool
1657 MediaDecoder::IsOpusEnabled()
1658 {
1659 #ifdef MOZ_OPUS
1660 return Preferences::GetBool("media.opus.enabled");
1661 #else
1662 return false;
1663 #endif
1664 }
1666 bool
1667 MediaDecoder::IsOggEnabled()
1668 {
1669 return Preferences::GetBool("media.ogg.enabled");
1670 }
1672 #ifdef MOZ_WAVE
1673 bool
1674 MediaDecoder::IsWaveEnabled()
1675 {
1676 return Preferences::GetBool("media.wave.enabled");
1677 }
1678 #endif
1680 #ifdef MOZ_WEBM
1681 bool
1682 MediaDecoder::IsWebMEnabled()
1683 {
1684 return Preferences::GetBool("media.webm.enabled");
1685 }
1686 #endif
1688 #ifdef NECKO_PROTOCOL_rtsp
1689 bool
1690 MediaDecoder::IsRtspEnabled()
1691 {
1692 //Currently the Rtsp decoded by omx.
1693 return (Preferences::GetBool("media.rtsp.enabled", false) && IsOmxEnabled());
1694 }
1695 #endif
1697 #ifdef MOZ_GSTREAMER
1698 bool
1699 MediaDecoder::IsGStreamerEnabled()
1700 {
1701 return Preferences::GetBool("media.gstreamer.enabled");
1702 }
1703 #endif
1705 #ifdef MOZ_OMX_DECODER
1706 bool
1707 MediaDecoder::IsOmxEnabled()
1708 {
1709 return Preferences::GetBool("media.omx.enabled", false);
1710 }
1711 #endif
1713 #ifdef MOZ_MEDIA_PLUGINS
1714 bool
1715 MediaDecoder::IsMediaPluginsEnabled()
1716 {
1717 return Preferences::GetBool("media.plugins.enabled");
1718 }
1719 #endif
1721 #ifdef MOZ_WMF
1722 bool
1723 MediaDecoder::IsWMFEnabled()
1724 {
1725 return WMFDecoder::IsEnabled();
1726 }
1727 #endif
1729 #ifdef MOZ_APPLEMEDIA
1730 bool
1731 MediaDecoder::IsAppleMP3Enabled()
1732 {
1733 return Preferences::GetBool("media.apple.mp3.enabled");
1734 }
1735 #endif
1737 NS_IMETHODIMP
1738 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
1739 nsISupports* aData)
1740 {
1741 int64_t video = 0, audio = 0;
1742 size_t resources = 0;
1743 DecodersArray& decoders = Decoders();
1744 for (size_t i = 0; i < decoders.Length(); ++i) {
1745 MediaDecoder* decoder = decoders[i];
1746 video += decoder->SizeOfVideoQueue();
1747 audio += decoder->SizeOfAudioQueue();
1749 if (decoder->GetResource()) {
1750 resources += decoder->GetResource()->SizeOfIncludingThis(MallocSizeOf);
1751 }
1752 }
1754 #define REPORT(_path, _amount, _desc) \
1755 do { \
1756 nsresult rv; \
1757 rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
1758 KIND_HEAP, UNITS_BYTES, _amount, \
1759 NS_LITERAL_CSTRING(_desc), aData); \
1760 NS_ENSURE_SUCCESS(rv, rv); \
1761 } while (0)
1763 REPORT("explicit/media/decoded/video", video,
1764 "Memory used by decoded video frames.");
1766 REPORT("explicit/media/decoded/audio", audio,
1767 "Memory used by decoded audio chunks.");
1769 REPORT("explicit/media/resources", resources,
1770 "Memory used by media resources including streaming buffers, caches, "
1771 "etc.");
1773 #undef REPORT
1775 return NS_OK;
1776 }
1778 MediaDecoderOwner*
1779 MediaDecoder::GetOwner()
1780 {
1781 MOZ_ASSERT(NS_IsMainThread());
1782 return mOwner;
1783 }
1785 MediaMemoryTracker::MediaMemoryTracker()
1786 {
1787 }
1789 void
1790 MediaMemoryTracker::InitMemoryReporter()
1791 {
1792 RegisterWeakMemoryReporter(this);
1793 }
1795 MediaMemoryTracker::~MediaMemoryTracker()
1796 {
1797 UnregisterWeakMemoryReporter(this);
1798 }
1800 } // namespace mozilla
1802 // avoid redefined macro in unified build
1803 #undef DECODER_LOG