content/media/MediaDecoder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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();
  1004   if (NS_SUCCEEDED(aStatus)) {
  1005     ResourceLoaded();
  1007   else if (aStatus != NS_BASE_STREAM_CLOSED) {
  1008     NetworkError();
  1010   UpdateReadyStateForData();
  1013 void MediaDecoder::NotifyPrincipalChanged()
  1015   if (mOwner) {
  1016     mOwner->NotifyDecoderPrincipalChanged();
  1020 void MediaDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset)
  1022   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  1023   NS_ENSURE_TRUE_VOID(mDecoderStateMachine);
  1024   if (mIgnoreProgressData) {
  1025     return;
  1027   if (aOffset >= mDecoderPosition) {
  1028     mPlaybackStatistics.AddBytes(aBytes);
  1030   mDecoderPosition = aOffset + aBytes;
  1033 void MediaDecoder::UpdateReadyStateForData()
  1035   MOZ_ASSERT(NS_IsMainThread());
  1036   if (!mOwner || mShuttingDown || !mDecoderStateMachine)
  1037     return;
  1038   MediaDecoderOwner::NextFrameStatus frameStatus =
  1039     mDecoderStateMachine->GetNextFrameStatus();
  1040   mOwner->UpdateReadyStateForData(frameStatus);
  1043 void MediaDecoder::SeekingStopped()
  1045   MOZ_ASSERT(NS_IsMainThread());
  1047   if (mShuttingDown)
  1048     return;
  1050   bool seekWasAborted = false;
  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);
  1065   PlaybackPositionChanged();
  1067   if (mOwner) {
  1068     UpdateReadyStateForData();
  1069     if (!seekWasAborted) {
  1070       mOwner->SeekCompleted();
  1075 // This is called when seeking stopped *and* we're at the end of the
  1076 // media.
  1077 void MediaDecoder::SeekingStoppedAtEnd()
  1079   MOZ_ASSERT(NS_IsMainThread());
  1081   if (mShuttingDown)
  1082     return;
  1084   bool fireEnded = false;
  1085   bool seekWasAborted = false;
  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);
  1101   PlaybackPositionChanged();
  1103   if (mOwner) {
  1104     UpdateReadyStateForData();
  1105     if (!seekWasAborted) {
  1106       mOwner->SeekCompleted();
  1107       if (fireEnded) {
  1108         mOwner->PlaybackEnded();
  1114 void MediaDecoder::SeekingStarted()
  1116   MOZ_ASSERT(NS_IsMainThread());
  1117   if (mShuttingDown)
  1118     return;
  1120   if (mOwner) {
  1121     UpdateReadyStateForData();
  1122     mOwner->SeekStarted();
  1126 void MediaDecoder::ChangeState(PlayState aState)
  1128   MOZ_ASSERT(NS_IsMainThread());
  1129   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  1131   if (mNextState == aState) {
  1132     mNextState = PLAY_STATE_PAUSED;
  1135   if ((mPlayState == PLAY_STATE_LOADING && mIsDormant && aState != PLAY_STATE_SHUTDOWN) ||
  1136        mPlayState == PLAY_STATE_SHUTDOWN) {
  1137     GetReentrantMonitor().NotifyAll();
  1138     return;
  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;
  1148   mPlayState = aState;
  1150   ApplyStateToStateMachine(mPlayState);
  1152   if (aState!= PLAY_STATE_LOADING) {
  1153     mIsDormant = false;
  1154     mIsExitingDormant = false;
  1157   GetReentrantMonitor().NotifyAll();
  1160 void MediaDecoder::ApplyStateToStateMachine(PlayState aState)
  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;
  1181 void MediaDecoder::PlaybackPositionChanged()
  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.
  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();
  1209       mDecoderStateMachine->ClearPositionChangeFlag();
  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();
  1224 void MediaDecoder::DurationChanged()
  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"));
  1241 void MediaDecoder::SetDuration(double aDuration)
  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)));
  1253   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  1254   if (mDecoderStateMachine) {
  1255     mDecoderStateMachine->SetDuration(mDuration);
  1258   // Duration has changed so we should recompute playback rate
  1259   UpdatePlaybackRate();
  1262 void MediaDecoder::SetMediaDuration(int64_t aDuration)
  1264   NS_ENSURE_TRUE_VOID(GetStateMachine());
  1265   GetStateMachine()->SetDuration(aDuration);
  1268 void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
  1270   if (mPlayState <= PLAY_STATE_LOADING) {
  1271     return;
  1273   NS_ENSURE_TRUE_VOID(GetStateMachine());
  1274   GetStateMachine()->UpdateEstimatedDuration(aDuration);
  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);
  1286 void MediaDecoder::SetTransportSeekable(bool aTransportSeekable)
  1288   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  1289   MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread());
  1290   mTransportSeekable = aTransportSeekable;
  1291   if (mDecoderStateMachine) {
  1292     mDecoderStateMachine->SetTransportSeekable(aTransportSeekable);
  1296 bool MediaDecoder::IsTransportSeekable()
  1298   MOZ_ASSERT(NS_IsMainThread());
  1299   return mTransportSeekable;
  1302 bool MediaDecoder::IsMediaSeekable()
  1304   NS_ENSURE_TRUE(GetStateMachine(), false);
  1305   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  1306   MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread());
  1307   return mMediaSeekable;
  1310 nsresult MediaDecoder::GetSeekable(dom::TimeRanges* aSeekable)
  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;
  1329 void MediaDecoder::SetFragmentEndTime(double aTime)
  1331   MOZ_ASSERT(NS_IsMainThread());
  1332   if (mDecoderStateMachine) {
  1333     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  1334     mDecoderStateMachine->SetFragmentEndTime(static_cast<int64_t>(aTime * USECS_PER_S));
  1338 void MediaDecoder::SetMediaEndTime(int64_t aTime)
  1340   NS_ENSURE_TRUE_VOID(GetStateMachine());
  1341   GetStateMachine()->SetMediaEndTime(aTime);
  1344 void MediaDecoder::Suspend()
  1346   MOZ_ASSERT(NS_IsMainThread());
  1347   if (mResource) {
  1348     mResource->Suspend(true);
  1352 void MediaDecoder::Resume(bool aForceBuffering)
  1354   MOZ_ASSERT(NS_IsMainThread());
  1355   if (mResource) {
  1356     mResource->Resume();
  1358   if (aForceBuffering) {
  1359     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  1360     if (mDecoderStateMachine) {
  1361       mDecoderStateMachine->StartBuffering();
  1366 void MediaDecoder::StopProgressUpdates()
  1368   MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
  1369   GetReentrantMonitor().AssertCurrentThreadIn();
  1370   mIgnoreProgressData = true;
  1371   if (mResource) {
  1372     mResource->SetReadMode(MediaCacheStream::MODE_METADATA);
  1376 void MediaDecoder::StartProgressUpdates()
  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();
  1387 void MediaDecoder::MoveLoadsToBackground()
  1389   MOZ_ASSERT(NS_IsMainThread());
  1390   if (mResource) {
  1391     mResource->MoveLoadsToBackground();
  1395 void MediaDecoder::UpdatePlaybackOffset(int64_t aOffset)
  1397   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  1398   mPlaybackPosition = std::max(aOffset, mPlaybackPosition);
  1401 bool MediaDecoder::OnStateMachineThread() const
  1403   return mDecoderStateMachine->OnStateMachineThread();
  1406 void MediaDecoder::SetPlaybackRate(double aPlaybackRate)
  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();
  1418     mPausedForPlaybackRateNull = false;
  1421   if (mDecoderStateMachine) {
  1422     mDecoderStateMachine->SetPlaybackRate(aPlaybackRate);
  1423   } else {
  1424     mInitialPlaybackRate = aPlaybackRate;
  1428 void MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
  1430   if (mDecoderStateMachine) {
  1431     mDecoderStateMachine->SetPreservesPitch(aPreservesPitch);
  1432   } else {
  1433     mInitialPreservesPitch = aPreservesPitch;
  1437 bool MediaDecoder::OnDecodeThread() const {
  1438   NS_WARN_IF_FALSE(mDecoderStateMachine, "mDecoderStateMachine is null");
  1439   return mDecoderStateMachine ? mDecoderStateMachine->OnDecodeThread() : false;
  1442 ReentrantMonitor& MediaDecoder::GetReentrantMonitor() {
  1443   return mReentrantMonitor.GetReentrantMonitor();
  1446 ImageContainer* MediaDecoder::GetImageContainer()
  1448   return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer() : nullptr;
  1451 void MediaDecoder::InvalidateWithFlags(uint32_t aFlags)
  1453   if (mVideoFrameContainer) {
  1454     mVideoFrameContainer->InvalidateWithFlags(aFlags);
  1458 void MediaDecoder::Invalidate()
  1460   if (mVideoFrameContainer) {
  1461     mVideoFrameContainer->Invalidate();
  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);
  1471   return NS_ERROR_FAILURE;
  1474 size_t MediaDecoder::SizeOfVideoQueue() {
  1475   if (mDecoderStateMachine) {
  1476     return mDecoderStateMachine->SizeOfVideoQueue();
  1478   return 0;
  1481 size_t MediaDecoder::SizeOfAudioQueue() {
  1482   if (mDecoderStateMachine) {
  1483     return mDecoderStateMachine->SizeOfAudioQueue();
  1485   return 0;
  1488 void MediaDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {
  1489   if (mDecoderStateMachine) {
  1490     mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset);
  1494 void MediaDecoder::UpdatePlaybackPosition(int64_t aTime)
  1496   mDecoderStateMachine->UpdatePlaybackPosition(aTime);
  1499 // Provide access to the state machine object
  1500 MediaDecoderStateMachine* MediaDecoder::GetStateMachine() const {
  1501   return mDecoderStateMachine;
  1504 void
  1505 MediaDecoder::NotifyWaitingForResourcesStatusChanged()
  1507   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
  1508   if (mDecoderStateMachine) {
  1509     mDecoderStateMachine->NotifyWaitingForResourcesStatusChanged();
  1513 bool MediaDecoder::IsShutdown() const {
  1514   NS_ENSURE_TRUE(GetStateMachine(), true);
  1515   return GetStateMachine()->IsShutdown();
  1518 int64_t MediaDecoder::GetEndMediaTime() const {
  1519   NS_ENSURE_TRUE(GetStateMachine(), -1);
  1520   return GetStateMachine()->GetEndMediaTime();
  1523 // Drop reference to state machine.  Only called during shutdown dance.
  1524 void MediaDecoder::ReleaseStateMachine() {
  1525   mDecoderStateMachine = nullptr;
  1528 MediaDecoderOwner* MediaDecoder::GetMediaOwner() const
  1530   return mOwner;
  1533 static void ProgressCallback(nsITimer* aTimer, void* aClosure)
  1535   MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure);
  1536   decoder->Progress(true);
  1539 void MediaDecoder::Progress(bool aTimer)
  1541   if (!mOwner)
  1542     return;
  1544   TimeStamp now = TimeStamp::Now();
  1546   if (!aTimer) {
  1547     mDataTime = now;
  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;
  1560   if (!mDataTime.IsNull() &&
  1561       now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) {
  1562     mOwner->DownloadStalled();
  1563     // Null it out
  1564     mDataTime = TimeStamp();
  1568 nsresult MediaDecoder::StartProgress()
  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);
  1580 nsresult MediaDecoder::StopProgress()
  1582   if (!mProgressTimer)
  1583     return NS_OK;
  1585   nsresult rv = mProgressTimer->Cancel();
  1586   mProgressTimer = nullptr;
  1588   return rv;
  1591 void MediaDecoder::FireTimeUpdate()
  1593   if (!mOwner)
  1594     return;
  1595   mOwner->FireTimeUpdate(true);
  1598 void MediaDecoder::PinForSeek()
  1600   MediaResource* resource = GetResource();
  1601   if (!resource || mPinnedForSeek) {
  1602     return;
  1604   mPinnedForSeek = true;
  1605   resource->Pin();
  1608 void MediaDecoder::UnpinForSeek()
  1610   MediaResource* resource = GetResource();
  1611   if (!resource || !mPinnedForSeek) {
  1612     return;
  1614   mPinnedForSeek = false;
  1615   resource->Unpin();
  1618 bool MediaDecoder::CanPlayThrough()
  1620   Statistics stats = GetStatistics();
  1621   if (!stats.mDownloadRateReliable || !stats.mPlaybackRateReliable) {
  1622     return false;
  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;
  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;
  1648 #ifdef MOZ_RAW
  1649 bool
  1650 MediaDecoder::IsRawEnabled()
  1652   return Preferences::GetBool("media.raw.enabled");
  1654 #endif
  1656 bool
  1657 MediaDecoder::IsOpusEnabled()
  1659 #ifdef MOZ_OPUS
  1660   return Preferences::GetBool("media.opus.enabled");
  1661 #else
  1662   return false;
  1663 #endif
  1666 bool
  1667 MediaDecoder::IsOggEnabled()
  1669   return Preferences::GetBool("media.ogg.enabled");
  1672 #ifdef MOZ_WAVE
  1673 bool
  1674 MediaDecoder::IsWaveEnabled()
  1676   return Preferences::GetBool("media.wave.enabled");
  1678 #endif
  1680 #ifdef MOZ_WEBM
  1681 bool
  1682 MediaDecoder::IsWebMEnabled()
  1684   return Preferences::GetBool("media.webm.enabled");
  1686 #endif
  1688 #ifdef NECKO_PROTOCOL_rtsp
  1689 bool
  1690 MediaDecoder::IsRtspEnabled()
  1692   //Currently the Rtsp decoded by omx.
  1693   return (Preferences::GetBool("media.rtsp.enabled", false) && IsOmxEnabled());
  1695 #endif
  1697 #ifdef MOZ_GSTREAMER
  1698 bool
  1699 MediaDecoder::IsGStreamerEnabled()
  1701   return Preferences::GetBool("media.gstreamer.enabled");
  1703 #endif
  1705 #ifdef MOZ_OMX_DECODER
  1706 bool
  1707 MediaDecoder::IsOmxEnabled()
  1709   return Preferences::GetBool("media.omx.enabled", false);
  1711 #endif
  1713 #ifdef MOZ_MEDIA_PLUGINS
  1714 bool
  1715 MediaDecoder::IsMediaPluginsEnabled()
  1717   return Preferences::GetBool("media.plugins.enabled");
  1719 #endif
  1721 #ifdef MOZ_WMF
  1722 bool
  1723 MediaDecoder::IsWMFEnabled()
  1725   return WMFDecoder::IsEnabled();
  1727 #endif
  1729 #ifdef MOZ_APPLEMEDIA
  1730 bool
  1731 MediaDecoder::IsAppleMP3Enabled()
  1733   return Preferences::GetBool("media.apple.mp3.enabled");
  1735 #endif
  1737 NS_IMETHODIMP
  1738 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
  1739                                    nsISupports* aData)
  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);
  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;
  1778 MediaDecoderOwner*
  1779 MediaDecoder::GetOwner()
  1781   MOZ_ASSERT(NS_IsMainThread());
  1782   return mOwner;
  1785 MediaMemoryTracker::MediaMemoryTracker()
  1789 void
  1790 MediaMemoryTracker::InitMemoryReporter()
  1792   RegisterWeakMemoryReporter(this);
  1795 MediaMemoryTracker::~MediaMemoryTracker()
  1797   UnregisterWeakMemoryReporter(this);
  1800 } // namespace mozilla
  1802 // avoid redefined macro in unified build
  1803 #undef DECODER_LOG

mercurial