LCOV - code coverage report
Current view: directory - content/media - nsBuiltinDecoderStateMachine.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1041 0 0.0 %
Date: 2012-06-02 Functions: 93 0 0.0 %

       1                 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: ML 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is the Mozilla Corporation.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2007
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *  Chris Double <chris.double@double.co.nz>
      23                 :  *  Chris Pearce <chris@pearce.org.nz>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include <limits>
      40                 : #include "nsAudioStream.h"
      41                 : #include "nsTArray.h"
      42                 : #include "nsBuiltinDecoder.h"
      43                 : #include "nsBuiltinDecoderReader.h"
      44                 : #include "nsBuiltinDecoderStateMachine.h"
      45                 : #include "mozilla/mozalloc.h"
      46                 : #include "VideoUtils.h"
      47                 : #include "nsTimeRanges.h"
      48                 : #include "nsDeque.h"
      49                 : 
      50                 : #include "mozilla/Preferences.h"
      51                 : #include "mozilla/StandardInteger.h"
      52                 : #include "mozilla/Util.h"
      53                 : 
      54                 : using namespace mozilla;
      55                 : using namespace mozilla::layers;
      56                 : 
      57                 : #ifdef PR_LOGGING
      58                 : extern PRLogModuleInfo* gBuiltinDecoderLog;
      59                 : #define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
      60                 : #else
      61                 : #define LOG(type, msg)
      62                 : #endif
      63                 : 
      64                 : // Wait this number of milliseconds when buffering, then leave and play
      65                 : // as best as we can if the required amount of data hasn't been
      66                 : // retrieved.
      67                 : static const PRUint32 BUFFERING_WAIT = 30000;
      68                 : 
      69                 : // If audio queue has less than this many usecs of decoded audio, we won't risk
      70                 : // trying to decode the video, we'll skip decoding video up to the next
      71                 : // keyframe. We may increase this value for an individual decoder if we
      72                 : // encounter video frames which take a long time to decode.
      73                 : static const PRUint32 LOW_AUDIO_USECS = 300000;
      74                 : 
      75                 : // If more than this many usecs of decoded audio is queued, we'll hold off
      76                 : // decoding more audio. If we increase the low audio threshold (see
      77                 : // LOW_AUDIO_USECS above) we'll also increase this value to ensure it's not
      78                 : // less than the low audio threshold.
      79                 : const PRInt64 AMPLE_AUDIO_USECS = 1000000;
      80                 : 
      81                 : // Maximum number of bytes we'll allocate and write at once to the audio
      82                 : // hardware when the audio stream contains missing frames and we're
      83                 : // writing silence in order to fill the gap. We limit our silence-writes
      84                 : // to 32KB in order to avoid allocating an impossibly large chunk of
      85                 : // memory if we encounter a large chunk of silence.
      86                 : const PRUint32 SILENCE_BYTES_CHUNK = 32 * 1024;
      87                 : 
      88                 : // If we have fewer than LOW_VIDEO_FRAMES decoded frames, and
      89                 : // we're not "pumping video", we'll skip the video up to the next keyframe
      90                 : // which is at or after the current playback position.
      91                 : static const PRUint32 LOW_VIDEO_FRAMES = 1;
      92                 : 
      93                 : // If we've got more than AMPLE_VIDEO_FRAMES decoded video frames waiting in
      94                 : // the video queue, we will not decode any more video frames until some have
      95                 : // been consumed by the play state machine thread.
      96                 : static const PRUint32 AMPLE_VIDEO_FRAMES = 10;
      97                 : 
      98                 : // Arbitrary "frame duration" when playing only audio.
      99                 : static const int AUDIO_DURATION_USECS = 40000;
     100                 : 
     101                 : // If we increase our "low audio threshold" (see LOW_AUDIO_USECS above), we
     102                 : // use this as a factor in all our calculations. Increasing this will cause
     103                 : // us to be more likely to increase our low audio threshold, and to
     104                 : // increase it by more.
     105                 : static const int THRESHOLD_FACTOR = 2;
     106                 : 
     107                 : // If we have less than this much undecoded data available, we'll consider
     108                 : // ourselves to be running low on undecoded data. We determine how much
     109                 : // undecoded data we have remaining using the reader's GetBuffered()
     110                 : // implementation.
     111                 : static const PRInt64 LOW_DATA_THRESHOLD_USECS = 5000000;
     112                 : 
     113                 : // LOW_DATA_THRESHOLD_USECS needs to be greater than AMPLE_AUDIO_USECS, otherwise
     114                 : // the skip-to-keyframe logic can activate when we're running low on data.
     115                 : PR_STATIC_ASSERT(LOW_DATA_THRESHOLD_USECS > AMPLE_AUDIO_USECS);
     116                 : 
     117                 : // Amount of excess usecs of data to add in to the "should we buffer" calculation.
     118                 : static const PRUint32 EXHAUSTED_DATA_MARGIN_USECS = 60000;
     119                 : 
     120                 : // If we enter buffering within QUICK_BUFFER_THRESHOLD_USECS seconds of starting
     121                 : // decoding, we'll enter "quick buffering" mode, which exits a lot sooner than
     122                 : // normal buffering mode. This exists so that if the decode-ahead exhausts the
     123                 : // downloaded data while decode/playback is just starting up (for example
     124                 : // after a seek while the media is still playing, or when playing a media
     125                 : // as soon as it's load started), we won't necessarily stop for 30s and wait
     126                 : // for buffering. We may actually be able to playback in this case, so exit
     127                 : // buffering early and try to play. If it turns out we can't play, we'll fall
     128                 : // back to buffering normally.
     129                 : static const PRUint32 QUICK_BUFFER_THRESHOLD_USECS = 2000000;
     130                 : 
     131                 : // If we're quick buffering, we'll remain in buffering mode while we have less than
     132                 : // QUICK_BUFFERING_LOW_DATA_USECS of decoded data available.
     133                 : static const PRUint32 QUICK_BUFFERING_LOW_DATA_USECS = 1000000;
     134                 : 
     135                 : // If QUICK_BUFFERING_LOW_DATA_USECS is > AMPLE_AUDIO_USECS, we won't exit
     136                 : // quick buffering in a timely fashion, as the decode pauses when it
     137                 : // reaches AMPLE_AUDIO_USECS decoded data, and thus we'll never reach
     138                 : // QUICK_BUFFERING_LOW_DATA_USECS.
     139                 : PR_STATIC_ASSERT(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS);
     140                 : 
     141               0 : static TimeDuration UsecsToDuration(PRInt64 aUsecs) {
     142               0 :   return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
     143                 : }
     144                 : 
     145               0 : static PRInt64 DurationToUsecs(TimeDuration aDuration) {
     146               0 :   return static_cast<PRInt64>(aDuration.ToSeconds() * USECS_PER_S);
     147                 : }
     148                 : 
     149                 : class nsAudioMetadataEventRunner : public nsRunnable
     150               0 : {
     151                 : private:
     152                 :   nsCOMPtr<nsBuiltinDecoder> mDecoder;
     153                 : public:
     154               0 :   nsAudioMetadataEventRunner(nsBuiltinDecoder* aDecoder, PRUint32 aChannels,
     155                 :                              PRUint32 aRate) :
     156                 :     mDecoder(aDecoder),
     157                 :     mChannels(aChannels),
     158               0 :     mRate(aRate)
     159                 :   {
     160               0 :   }
     161                 : 
     162               0 :   NS_IMETHOD Run()
     163                 :   {
     164               0 :     mDecoder->MetadataLoaded(mChannels, mRate);
     165               0 :     return NS_OK;
     166                 :   }
     167                 : 
     168                 :   const PRUint32 mChannels;
     169                 :   const PRUint32 mRate;
     170                 : };
     171                 : 
     172                 : // Shuts down a thread asynchronously.
     173                 : class ShutdownThreadEvent : public nsRunnable 
     174                 : {
     175                 : public:
     176               0 :   ShutdownThreadEvent(nsIThread* aThread) : mThread(aThread) {}
     177               0 :   ~ShutdownThreadEvent() {}
     178               0 :   NS_IMETHOD Run() {
     179               0 :     mThread->Shutdown();
     180               0 :     mThread = nsnull;
     181               0 :     return NS_OK;
     182                 :   }
     183                 : private:
     184                 :   nsCOMPtr<nsIThread> mThread;
     185                 : };
     186                 : 
     187                 : // Owns the global state machine thread and counts of
     188                 : // state machine and decoder threads. There should
     189                 : // only be one instance of this class.
     190                 : class StateMachineTracker
     191                 : {
     192                 : private:
     193               0 :   StateMachineTracker() :
     194                 :     mMonitor("media.statemachinetracker"),
     195                 :     mStateMachineCount(0),
     196                 :     mDecodeThreadCount(0),
     197               0 :     mStateMachineThread(nsnull)
     198                 :   {
     199               0 :      MOZ_COUNT_CTOR(StateMachineTracker);
     200               0 :      NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     201               0 :   } 
     202                 :  
     203               0 :   ~StateMachineTracker()
     204               0 :   {
     205               0 :     NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     206                 : 
     207               0 :     MOZ_COUNT_DTOR(StateMachineTracker);
     208               0 :   }
     209                 : 
     210                 : public:
     211                 :   // Access singleton instance. This is initially called on the main
     212                 :   // thread in the nsBuiltinDecoderStateMachine constructor resulting
     213                 :   // in the global object being created lazily. Non-main thread
     214                 :   // access always occurs after this and uses the monitor to
     215                 :   // safely access the decode thread counts.
     216                 :   static StateMachineTracker& Instance();
     217                 :  
     218                 :   // Instantiate the global state machine thread if required.
     219                 :   // Call on main thread only.
     220                 :   void EnsureGlobalStateMachine();
     221                 : 
     222                 :   // Destroy global state machine thread if required.
     223                 :   // Call on main thread only.
     224                 :   void CleanupGlobalStateMachine();
     225                 : 
     226                 :   // Return the global state machine thread. Call from any thread.
     227               0 :   nsIThread* GetGlobalStateMachineThread()
     228                 :   {
     229               0 :     ReentrantMonitorAutoEnter mon(mMonitor);
     230               0 :     NS_ASSERTION(mStateMachineThread, "Should have non-null state machine thread!");
     231               0 :     return mStateMachineThread;
     232                 :   }
     233                 : 
     234                 :   // Requests that a decode thread be created for aStateMachine. The thread
     235                 :   // may be created immediately, or after some delay, once a thread becomes
     236                 :   // available. The request can be cancelled using CancelCreateDecodeThread().
     237                 :   // It's the callers responsibility to not call this more than once for any
     238                 :   // given state machine.
     239                 :   nsresult RequestCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine);
     240                 : 
     241                 :   // Cancels a request made by RequestCreateDecodeThread to create a decode
     242                 :   // thread for aStateMachine.
     243                 :   nsresult CancelCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine);
     244                 : 
     245                 :   // Maximum number of active decode threads allowed. When more
     246                 :   // than this number are active the thread creation will fail.
     247                 :   static const PRUint32 MAX_DECODE_THREADS = 25;
     248                 : 
     249                 :   // Returns the number of active decode threads.
     250                 :   // Call on any thread. Holds the internal monitor so don't
     251                 :   // call with any other monitor held to avoid deadlock.
     252                 :   PRUint32 GetDecodeThreadCount();
     253                 : 
     254                 :   // Keep track of the fact that a decode thread was destroyed.
     255                 :   // Call on any thread. Holds the internal monitor so don't
     256                 :   // call with any other monitor held to avoid deadlock.
     257                 :   void NoteDecodeThreadDestroyed();
     258                 : 
     259                 : #ifdef DEBUG
     260                 :   // Returns true if aStateMachine has a pending request for a
     261                 :   // decode thread.
     262                 :   bool IsQueued(nsBuiltinDecoderStateMachine* aStateMachine);
     263                 : #endif
     264                 : 
     265                 : private:
     266                 :   // Holds global instance of StateMachineTracker.
     267                 :   // Writable on main thread only.
     268                 :   static StateMachineTracker* mInstance;
     269                 : 
     270                 :   // Reentrant monitor that must be obtained to access
     271                 :   // the decode thread count member and methods.
     272                 :   ReentrantMonitor mMonitor;
     273                 : 
     274                 :   // Number of instances of nsBuiltinDecoderStateMachine
     275                 :   // that are currently instantiated. Access on the
     276                 :   // main thread only.
     277                 :   PRUint32 mStateMachineCount;
     278                 : 
     279                 :   // Number of instances of decoder threads that are
     280                 :   // currently instantiated. Access only with the
     281                 :   // mMonitor lock held. Can be used from any thread.
     282                 :   PRUint32 mDecodeThreadCount;
     283                 : 
     284                 :   // Global state machine thread. Write on the main thread
     285                 :   // only, read from the decoder threads. Synchronized via
     286                 :   // the mMonitor.
     287                 :   nsIThread* mStateMachineThread;
     288                 : 
     289                 :   // Queue of state machines waiting for decode threads. Entries at the front
     290                 :   // get their threads first.
     291                 :   nsDeque mPending;
     292                 : };
     293                 : 
     294                 : StateMachineTracker* StateMachineTracker::mInstance = nsnull;
     295                 : 
     296               0 : StateMachineTracker& StateMachineTracker::Instance()
     297                 : {
     298               0 :   if (!mInstance) {
     299               0 :     NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     300               0 :     mInstance = new StateMachineTracker();
     301                 :   }
     302               0 :   return *mInstance;
     303                 : }
     304                 : 
     305               0 : void StateMachineTracker::EnsureGlobalStateMachine() 
     306                 : {
     307               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     308               0 :   ReentrantMonitorAutoEnter mon(mMonitor);
     309               0 :   if (mStateMachineCount == 0) {
     310               0 :     NS_ASSERTION(!mStateMachineThread, "Should have null state machine thread!");
     311               0 :     DebugOnly<nsresult> rv = NS_NewThread(&mStateMachineThread, nsnull);
     312               0 :     NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Can't create media state machine thread");
     313                 :   }
     314               0 :   mStateMachineCount++;
     315               0 : }
     316                 : 
     317                 : #ifdef DEBUG
     318               0 : bool StateMachineTracker::IsQueued(nsBuiltinDecoderStateMachine* aStateMachine)
     319                 : {
     320               0 :   ReentrantMonitorAutoEnter mon(mMonitor);
     321               0 :   PRInt32 size = mPending.GetSize();
     322               0 :   for (int i = 0; i < size; ++i) {
     323                 :     nsBuiltinDecoderStateMachine* m =
     324               0 :       static_cast<nsBuiltinDecoderStateMachine*>(mPending.ObjectAt(i));
     325               0 :     if (m == aStateMachine) {
     326               0 :       return true;
     327                 :     }
     328                 :   }
     329               0 :   return false;
     330                 : }
     331                 : #endif
     332                 : 
     333               0 : void StateMachineTracker::CleanupGlobalStateMachine() 
     334                 : {
     335               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     336               0 :   NS_ABORT_IF_FALSE(mStateMachineCount > 0,
     337                 :     "State machine ref count must be > 0");
     338               0 :   mStateMachineCount--;
     339               0 :   if (mStateMachineCount == 0) {
     340               0 :     LOG(PR_LOG_DEBUG, ("Destroying media state machine thread"));
     341               0 :     NS_ASSERTION(mPending.GetSize() == 0, "Shouldn't all requests be handled by now?");
     342                 :     {
     343               0 :       ReentrantMonitorAutoEnter mon(mMonitor);
     344               0 :       nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mStateMachineThread);
     345               0 :       NS_RELEASE(mStateMachineThread);
     346               0 :       mStateMachineThread = nsnull;
     347               0 :       NS_DispatchToMainThread(event);
     348                 : 
     349               0 :       NS_ASSERTION(mDecodeThreadCount == 0, "Decode thread count must be zero.");
     350               0 :       mInstance = nsnull;
     351                 :     }
     352               0 :     delete this;
     353                 :   }
     354               0 : }
     355                 : 
     356               0 : void StateMachineTracker::NoteDecodeThreadDestroyed()
     357                 : {
     358               0 :   ReentrantMonitorAutoEnter mon(mMonitor);
     359               0 :   --mDecodeThreadCount;
     360               0 :   while (mDecodeThreadCount < MAX_DECODE_THREADS && mPending.GetSize() > 0) {
     361                 :     nsBuiltinDecoderStateMachine* m =
     362               0 :       static_cast<nsBuiltinDecoderStateMachine*>(mPending.PopFront());
     363                 :     nsresult rv;
     364                 :     {
     365               0 :       ReentrantMonitorAutoExit exitMon(mMonitor);
     366               0 :       rv = m->StartDecodeThread();
     367                 :     }
     368               0 :     if (NS_SUCCEEDED(rv)) {
     369               0 :       ++mDecodeThreadCount;
     370                 :     }
     371                 :   }
     372               0 : }
     373                 : 
     374               0 : PRUint32 StateMachineTracker::GetDecodeThreadCount()
     375                 : {
     376               0 :   ReentrantMonitorAutoEnter mon(mMonitor);
     377               0 :   return mDecodeThreadCount;
     378                 : }
     379                 : 
     380               0 : nsresult StateMachineTracker::CancelCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine) {
     381               0 :   ReentrantMonitorAutoEnter mon(mMonitor);
     382               0 :   PRInt32 size = mPending.GetSize();
     383               0 :   for (PRInt32 i = 0; i < size; ++i) {
     384               0 :     void* m = static_cast<nsBuiltinDecoderStateMachine*>(mPending.ObjectAt(i));
     385               0 :     if (m == aStateMachine) {
     386               0 :       mPending.RemoveObjectAt(i);
     387               0 :       break;
     388                 :     }
     389                 :   }
     390               0 :   NS_ASSERTION(!IsQueued(aStateMachine), "State machine should no longer have queued request.");
     391               0 :   return NS_OK;
     392                 : }
     393                 : 
     394               0 : nsresult StateMachineTracker::RequestCreateDecodeThread(nsBuiltinDecoderStateMachine* aStateMachine)
     395                 : {
     396               0 :   NS_ENSURE_STATE(aStateMachine);
     397               0 :   ReentrantMonitorAutoEnter mon(mMonitor);
     398               0 :   if (mPending.GetSize() > 0 || mDecodeThreadCount + 1 >= MAX_DECODE_THREADS) {
     399                 :     // If there's already state machines in the queue, or we've exceeded the
     400                 :     // limit, append the state machine to the queue of state machines waiting
     401                 :     // for a decode thread. This ensures state machines already waiting get
     402                 :     // their threads first.
     403               0 :     mPending.Push(aStateMachine);
     404               0 :     return NS_OK;
     405                 :   }
     406                 :   nsresult rv;
     407                 :   {
     408               0 :     ReentrantMonitorAutoExit exitMon(mMonitor);
     409               0 :     rv = aStateMachine->StartDecodeThread();
     410                 :   }
     411               0 :   if (NS_SUCCEEDED(rv)) {
     412               0 :     ++mDecodeThreadCount;
     413                 :   }
     414               0 :   NS_ASSERTION(mDecodeThreadCount <= MAX_DECODE_THREADS,
     415                 :                 "Should keep to thread limit!");
     416               0 :   return NS_OK;
     417                 : }
     418                 : 
     419               0 : nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder,
     420                 :                                                            nsBuiltinDecoderReader* aReader,
     421                 :                                                            bool aRealTime) :
     422                 :   mDecoder(aDecoder),
     423                 :   mState(DECODER_STATE_DECODING_METADATA),
     424                 :   mCbCrSize(0),
     425                 :   mPlayDuration(0),
     426                 :   mStartTime(-1),
     427                 :   mEndTime(-1),
     428                 :   mSeekTime(0),
     429                 :   mFragmentEndTime(-1),
     430                 :   mReader(aReader),
     431                 :   mCurrentFrameTime(0),
     432                 :   mAudioStartTime(-1),
     433                 :   mAudioEndTime(-1),
     434                 :   mVideoFrameEndTime(-1),
     435                 :   mVolume(1.0),
     436                 :   mSeekable(true),
     437                 :   mPositionChangeQueued(false),
     438                 :   mAudioCompleted(false),
     439                 :   mGotDurationFromMetaData(false),
     440                 :   mStopDecodeThread(true),
     441                 :   mDecodeThreadIdle(false),
     442                 :   mStopAudioThread(true),
     443                 :   mQuickBuffering(false),
     444                 :   mIsRunning(false),
     445                 :   mRunAgain(false),
     446                 :   mDispatchedRunEvent(false),
     447                 :   mDecodeThreadWaiting(false),
     448                 :   mRealTime(aRealTime),
     449                 :   mRequestedNewDecodeThread(false),
     450               0 :   mEventManager(aDecoder)
     451                 : {
     452               0 :   MOZ_COUNT_CTOR(nsBuiltinDecoderStateMachine);
     453               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     454                 : 
     455               0 :   StateMachineTracker::Instance().EnsureGlobalStateMachine();
     456                 : 
     457                 :   // only enable realtime mode when "media.realtime_decoder.enabled" is true.
     458               0 :   if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false)
     459               0 :     mRealTime = false;
     460                 : 
     461               0 :   mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT;
     462               0 :   mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS;
     463               0 : }
     464                 : 
     465               0 : nsBuiltinDecoderStateMachine::~nsBuiltinDecoderStateMachine()
     466                 : {
     467               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
     468               0 :   MOZ_COUNT_DTOR(nsBuiltinDecoderStateMachine);
     469               0 :   NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this),
     470                 :     "Should not have a pending request for a new decode thread");
     471               0 :   NS_ASSERTION(!mRequestedNewDecodeThread,
     472                 :     "Should not have (or flagged) a pending request for a new decode thread");
     473               0 :   if (mTimer)
     474               0 :     mTimer->Cancel();
     475               0 :   mTimer = nsnull;
     476                 :  
     477               0 :   StateMachineTracker::Instance().CleanupGlobalStateMachine();
     478               0 : }
     479                 : 
     480               0 : bool nsBuiltinDecoderStateMachine::HasFutureAudio() const {
     481               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     482               0 :   NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
     483                 :   // We've got audio ready to play if:
     484                 :   // 1. We've not completed playback of audio, and
     485                 :   // 2. we either have more than the threshold of decoded audio available, or
     486                 :   //    we've completely decoded all audio (but not finished playing it yet
     487                 :   //    as per 1).
     488               0 :   return !mAudioCompleted &&
     489               0 :          (AudioDecodedUsecs() > LOW_AUDIO_USECS || mReader->mAudioQueue.IsFinished());
     490                 : }
     491                 : 
     492               0 : bool nsBuiltinDecoderStateMachine::HaveNextFrameData() const {
     493               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     494               0 :   return (!HasAudio() || HasFutureAudio()) &&
     495               0 :          (!HasVideo() || mReader->mVideoQueue.GetSize() > 0);
     496                 : }
     497                 : 
     498               0 : PRInt64 nsBuiltinDecoderStateMachine::GetDecodedAudioDuration() {
     499               0 :   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
     500               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     501               0 :   PRInt64 audioDecoded = mReader->mAudioQueue.Duration();
     502               0 :   if (mAudioEndTime != -1) {
     503               0 :     audioDecoded += mAudioEndTime - GetMediaTime();
     504                 :   }
     505               0 :   return audioDecoded;
     506                 : }
     507                 : 
     508               0 : void nsBuiltinDecoderStateMachine::DecodeThreadRun()
     509                 : {
     510               0 :   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
     511               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     512                 : 
     513               0 :   if (mState == DECODER_STATE_DECODING_METADATA) {
     514               0 :     if (NS_FAILED(DecodeMetadata())) {
     515               0 :       NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
     516                 :                    "Should be in shutdown state if metadata loading fails.");
     517               0 :       LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decode thread"));
     518                 :     }
     519                 :   }
     520                 : 
     521               0 :   while (mState != DECODER_STATE_SHUTDOWN &&
     522                 :          mState != DECODER_STATE_COMPLETED &&
     523               0 :          !mStopDecodeThread)
     524                 :   {
     525               0 :     if (mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) {
     526               0 :       DecodeLoop();
     527               0 :     } else if (mState == DECODER_STATE_SEEKING) {
     528               0 :       DecodeSeek();
     529                 :     }
     530                 :   }
     531                 : 
     532               0 :   mDecodeThreadIdle = true;
     533               0 :   LOG(PR_LOG_DEBUG, ("%p Decode thread finished", mDecoder.get()));
     534               0 : }
     535                 : 
     536               0 : void nsBuiltinDecoderStateMachine::DecodeLoop()
     537                 : {
     538               0 :   LOG(PR_LOG_DEBUG, ("%p Start DecodeLoop()", mDecoder.get()));
     539                 : 
     540               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     541               0 :   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
     542                 : 
     543                 :   // We want to "pump" the decode until we've got a few frames decoded
     544                 :   // before we consider whether decode is falling behind.
     545               0 :   bool audioPump = true;
     546               0 :   bool videoPump = true;
     547                 : 
     548                 :   // If the video decode is falling behind the audio, we'll start dropping the
     549                 :   // inter-frames up until the next keyframe which is at or before the current
     550                 :   // playback position. skipToNextKeyframe is true if we're currently
     551                 :   // skipping up to the next keyframe.
     552               0 :   bool skipToNextKeyframe = false;
     553                 : 
     554                 :   // Once we've decoded more than videoPumpThreshold video frames, we'll
     555                 :   // no longer be considered to be "pumping video".
     556               0 :   const unsigned videoPumpThreshold = mRealTime ? 0 : AMPLE_VIDEO_FRAMES / 2;
     557                 : 
     558                 :   // After the audio decode fills with more than audioPumpThreshold usecs
     559                 :   // of decoded audio, we'll start to check whether the audio or video decode
     560                 :   // is falling behind.
     561               0 :   const unsigned audioPumpThreshold = mRealTime ? 0 : LOW_AUDIO_USECS * 2;
     562                 : 
     563                 :   // Our local low audio threshold. We may increase this if we're slow to
     564                 :   // decode video frames, in order to reduce the chance of audio underruns.
     565               0 :   PRInt64 lowAudioThreshold = LOW_AUDIO_USECS;
     566                 : 
     567                 :   // Our local ample audio threshold. If we increase lowAudioThreshold, we'll
     568                 :   // also increase this too appropriately (we don't want lowAudioThreshold to
     569                 :   // be greater than ampleAudioThreshold, else we'd stop decoding!).
     570               0 :   PRInt64 ampleAudioThreshold = AMPLE_AUDIO_USECS;
     571                 : 
     572               0 :   MediaQueue<VideoData>& videoQueue = mReader->mVideoQueue;
     573               0 :   MediaQueue<AudioData>& audioQueue = mReader->mAudioQueue;
     574                 : 
     575                 :   // Main decode loop.
     576               0 :   bool videoPlaying = HasVideo();
     577               0 :   bool audioPlaying = HasAudio();
     578               0 :   while ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) &&
     579               0 :          !mStopDecodeThread &&
     580                 :          (videoPlaying || audioPlaying))
     581                 :   {
     582                 :     // We don't want to consider skipping to the next keyframe if we've
     583                 :     // only just started up the decode loop, so wait until we've decoded
     584                 :     // some frames before enabling the keyframe skip logic on video.
     585               0 :     if (videoPump &&
     586               0 :         static_cast<PRUint32>(videoQueue.GetSize()) >= videoPumpThreshold)
     587                 :     {
     588               0 :       videoPump = false;
     589                 :     }
     590                 : 
     591                 :     // We don't want to consider skipping to the next keyframe if we've
     592                 :     // only just started up the decode loop, so wait until we've decoded
     593                 :     // some audio data before enabling the keyframe skip logic on audio.
     594               0 :     if (audioPump && GetDecodedAudioDuration() >= audioPumpThreshold) {
     595               0 :       audioPump = false;
     596                 :     }
     597                 : 
     598                 :     // We'll skip the video decode to the nearest keyframe if we're low on
     599                 :     // audio, or if we're low on video, provided we're not running low on
     600                 :     // data to decode. If we're running low on downloaded data to decode,
     601                 :     // we won't start keyframe skipping, as we'll be pausing playback to buffer
     602                 :     // soon anyway and we'll want to be able to display frames immediately
     603                 :     // after buffering finishes.
     604               0 :     if (mState == DECODER_STATE_DECODING &&
     605               0 :         !skipToNextKeyframe &&
     606                 :         videoPlaying &&
     607               0 :         ((!audioPump && audioPlaying && GetDecodedAudioDuration() < lowAudioThreshold) ||
     608               0 :          (!videoPump &&
     609                 :            videoPlaying &&
     610               0 :            static_cast<PRUint32>(videoQueue.GetSize()) < LOW_VIDEO_FRAMES)) &&
     611               0 :         !HasLowUndecodedData())
     612                 : 
     613                 :     {
     614               0 :       skipToNextKeyframe = true;
     615               0 :       LOG(PR_LOG_DEBUG, ("%p Skipping video decode to the next keyframe", mDecoder.get()));
     616                 :     }
     617                 : 
     618                 :     // Video decode.
     619               0 :     if (videoPlaying &&
     620               0 :         static_cast<PRUint32>(videoQueue.GetSize()) < AMPLE_VIDEO_FRAMES)
     621                 :     {
     622                 :       // Time the video decode, so that if it's slow, we can increase our low
     623                 :       // audio threshold to reduce the chance of an audio underrun while we're
     624                 :       // waiting for a video decode to complete.
     625               0 :       TimeDuration decodeTime;
     626                 :       {
     627               0 :         PRInt64 currentTime = GetMediaTime();
     628               0 :         ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     629               0 :         TimeStamp start = TimeStamp::Now();
     630               0 :         videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime);
     631               0 :         decodeTime = TimeStamp::Now() - start;
     632                 :       }
     633               0 :       if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > lowAudioThreshold &&
     634               0 :           !HasLowUndecodedData())
     635                 :       {
     636                 :         lowAudioThreshold =
     637               0 :           NS_MIN(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), AMPLE_AUDIO_USECS);
     638                 :         ampleAudioThreshold = NS_MAX(THRESHOLD_FACTOR * lowAudioThreshold,
     639               0 :                                      ampleAudioThreshold);
     640               0 :         LOG(PR_LOG_DEBUG,
     641                 :             ("Slow video decode, set lowAudioThreshold=%lld ampleAudioThreshold=%lld",
     642                 :              lowAudioThreshold, ampleAudioThreshold));
     643                 :       }
     644                 :     }
     645                 : 
     646                 :     // Audio decode.
     647               0 :     if (audioPlaying &&
     648               0 :         (GetDecodedAudioDuration() < ampleAudioThreshold || audioQueue.GetSize() == 0))
     649                 :     {
     650               0 :       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     651               0 :       audioPlaying = mReader->DecodeAudioData();
     652                 :     }
     653                 : 
     654                 :     // Notify to ensure that the AudioLoop() is not waiting, in case it was
     655                 :     // waiting for more audio to be decoded.
     656               0 :     mDecoder->GetReentrantMonitor().NotifyAll();
     657                 : 
     658                 :     // The ready state can change when we've decoded data, so update the
     659                 :     // ready state, so that DOM events can fire.
     660               0 :     UpdateReadyState();
     661                 : 
     662               0 :     if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) &&
     663               0 :         !mStopDecodeThread &&
     664                 :         (videoPlaying || audioPlaying) &&
     665               0 :         (!audioPlaying || (GetDecodedAudioDuration() >= ampleAudioThreshold &&
     666               0 :                            audioQueue.GetSize() > 0))
     667                 :         &&
     668               0 :         (!videoPlaying ||
     669               0 :           static_cast<PRUint32>(videoQueue.GetSize()) >= AMPLE_VIDEO_FRAMES))
     670                 :     {
     671                 :       // All active bitstreams' decode is well ahead of the playback
     672                 :       // position, we may as well wait for the playback to catch up. Note the
     673                 :       // audio push thread acquires and notifies the decoder monitor every time
     674                 :       // it pops AudioData off the audio queue. So if the audio push thread pops
     675                 :       // the last AudioData off the audio queue right after that queue reported
     676                 :       // it was non-empty here, we'll receive a notification on the decoder
     677                 :       // monitor which will wake us up shortly after we sleep, thus preventing
     678                 :       // both the decode and audio push threads waiting at the same time.
     679                 :       // See bug 620326.
     680               0 :       mDecodeThreadWaiting = true;
     681               0 :       if (mDecoder->GetState() != nsBuiltinDecoder::PLAY_STATE_PLAYING) {
     682                 :         // We're not playing, and the decode is about to wait. This means
     683                 :         // the decode thread may not be needed in future. Signal the state
     684                 :         // machine thread to run, so it can decide whether to shutdown the
     685                 :         // decode thread.
     686               0 :         ScheduleStateMachine();
     687                 :       }
     688               0 :       mDecoder->GetReentrantMonitor().Wait();
     689               0 :       mDecodeThreadWaiting = false;
     690                 :     }
     691                 : 
     692                 :   } // End decode loop.
     693                 : 
     694               0 :   if (!mStopDecodeThread &&
     695                 :       mState != DECODER_STATE_SHUTDOWN &&
     696                 :       mState != DECODER_STATE_SEEKING)
     697                 :   {
     698               0 :     mState = DECODER_STATE_COMPLETED;
     699               0 :     ScheduleStateMachine();
     700                 :   }
     701                 : 
     702               0 :   LOG(PR_LOG_DEBUG, ("%p Exiting DecodeLoop", mDecoder.get()));
     703               0 : }
     704                 : 
     705               0 : bool nsBuiltinDecoderStateMachine::IsPlaying()
     706                 : {
     707               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     708                 : 
     709               0 :   return !mPlayStartTime.IsNull();
     710                 : }
     711                 : 
     712               0 : void nsBuiltinDecoderStateMachine::AudioLoop()
     713                 : {
     714               0 :   NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
     715               0 :   LOG(PR_LOG_DEBUG, ("%p Begun audio thread/loop", mDecoder.get()));
     716               0 :   PRInt64 audioDuration = 0;
     717               0 :   PRInt64 audioStartTime = -1;
     718                 :   PRUint32 channels, rate;
     719               0 :   double volume = -1;
     720                 :   bool setVolume;
     721               0 :   PRInt32 minWriteFrames = -1;
     722                 :   {
     723               0 :     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     724               0 :     mAudioCompleted = false;
     725               0 :     audioStartTime = mAudioStartTime;
     726               0 :     channels = mInfo.mAudioChannels;
     727               0 :     rate = mInfo.mAudioRate;
     728               0 :     NS_ASSERTION(audioStartTime != -1, "Should have audio start time by now");
     729                 :   }
     730                 : 
     731                 :   // It is unsafe to call some methods of nsAudioStream with the decoder
     732                 :   // monitor held, as on Android those methods do a synchronous dispatch to
     733                 :   // the main thread. If the audio thread holds the decoder monitor while
     734                 :   // it does a synchronous dispatch to the main thread, we can get deadlocks
     735                 :   // if the main thread tries to acquire the decoder monitor before the
     736                 :   // dispatched event has finished (or even started!) running. Methods which
     737                 :   // are unsafe to call with the decoder monitor held are documented as such
     738                 :   // in nsAudioStream.h.
     739               0 :   nsRefPtr<nsAudioStream> audioStream = nsAudioStream::AllocateStream();
     740               0 :   audioStream->Init(channels, rate, MOZ_AUDIO_DATA_FORMAT);
     741                 : 
     742                 :   {
     743                 :     // We must hold the monitor while setting mAudioStream or whenever we query
     744                 :     // the playback position off the audio thread. This ensures the audio stream
     745                 :     // is always alive when we use it off the audio thread. Note that querying
     746                 :     // the playback position does not do a synchronous dispatch to the main
     747                 :     // thread, so it's safe to call with the decoder monitor held.
     748               0 :     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     749               0 :     mAudioStream = audioStream;
     750               0 :     volume = mVolume;
     751               0 :     mAudioStream->SetVolume(volume);
     752                 :   }
     753               0 :   while (1) {
     754                 : 
     755                 :     // Wait while we're not playing, and we're not shutting down, or we're
     756                 :     // playing and we've got no audio to play.
     757                 :     {
     758               0 :       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     759               0 :       NS_ASSERTION(mState != DECODER_STATE_DECODING_METADATA,
     760                 :                    "Should have meta data before audio started playing.");
     761               0 :       while (mState != DECODER_STATE_SHUTDOWN &&
     762               0 :              !mStopAudioThread &&
     763               0 :              (!IsPlaying() ||
     764                 :               mState == DECODER_STATE_BUFFERING ||
     765               0 :               (mReader->mAudioQueue.GetSize() == 0 &&
     766               0 :                !mReader->mAudioQueue.AtEndOfStream())))
     767                 :       {
     768               0 :         if (!IsPlaying() && !mAudioStream->IsPaused()) {
     769               0 :           mAudioStream->Pause();
     770                 :         }
     771               0 :         mon.Wait();
     772                 :       }
     773                 : 
     774                 :       // If we're shutting down, break out and exit the audio thread.
     775               0 :       if (mState == DECODER_STATE_SHUTDOWN ||
     776                 :           mStopAudioThread ||
     777               0 :           mReader->mAudioQueue.AtEndOfStream())
     778                 :       {
     779                 :         break;
     780                 :       }
     781                 : 
     782                 :       // We only want to go to the expense of changing the volume if
     783                 :       // the volume has changed.
     784               0 :       setVolume = volume != mVolume;
     785               0 :       volume = mVolume;
     786                 : 
     787                 :       // Note audio stream IsPaused() does not do synchronous dispatch to the
     788                 :       // main thread on Android, so can be called safely with the decoder
     789                 :       // monitor held.
     790               0 :       if (IsPlaying() && mAudioStream->IsPaused()) {
     791               0 :         mAudioStream->Resume();
     792                 :       }
     793                 :     }
     794                 : 
     795               0 :     if (setVolume) {
     796               0 :       mAudioStream->SetVolume(volume);
     797                 :     }
     798               0 :     if (minWriteFrames == -1) {
     799               0 :       minWriteFrames = mAudioStream->GetMinWriteSize();
     800                 :     }
     801               0 :     NS_ASSERTION(mReader->mAudioQueue.GetSize() > 0,
     802                 :                  "Should have data to play");
     803                 :     // See if there's a gap in the audio. If there is, push silence into the
     804                 :     // audio hardware, so we can play across the gap.
     805               0 :     const AudioData* s = mReader->mAudioQueue.PeekFront();
     806                 : 
     807                 :     // Calculate the number of frames that have been pushed onto the audio
     808                 :     // hardware.
     809                 :     CheckedInt64 playedFrames = UsecsToFrames(audioStartTime, rate) +
     810               0 :                                               audioDuration;
     811                 :     // Calculate the timestamp of the next chunk of audio in numbers of
     812                 :     // samples.
     813               0 :     CheckedInt64 sampleTime = UsecsToFrames(s->mTime, rate);
     814               0 :     CheckedInt64 missingFrames = sampleTime - playedFrames;
     815               0 :     if (!missingFrames.valid() || !sampleTime.valid()) {
     816               0 :       NS_WARNING("Int overflow adding in AudioLoop()");
     817               0 :       break;
     818                 :     }
     819                 : 
     820               0 :     PRInt64 framesWritten = 0;
     821               0 :     if (missingFrames.value() > 0) {
     822                 :       // The next audio chunk begins some time after the end of the last chunk
     823                 :       // we pushed to the audio hardware. We must push silence into the audio
     824                 :       // hardware so that the next audio chunk begins playback at the correct
     825                 :       // time.
     826                 :       missingFrames = NS_MIN(static_cast<PRInt64>(PR_UINT32_MAX),
     827               0 :                              missingFrames.value());
     828               0 :       framesWritten = PlaySilence(static_cast<PRUint32>(missingFrames.value()),
     829               0 :                                   channels, playedFrames.value());
     830                 :     } else {
     831               0 :       framesWritten = PlayFromAudioQueue(sampleTime.value(), channels);
     832                 :     }
     833               0 :     audioDuration += framesWritten;
     834                 :     {
     835               0 :       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     836               0 :       CheckedInt64 playedUsecs = FramesToUsecs(audioDuration, rate) + audioStartTime;
     837               0 :       if (!playedUsecs.valid()) {
     838               0 :         NS_WARNING("Int overflow calculating audio end time");
     839                 :         break;
     840                 :       }
     841               0 :       mAudioEndTime = playedUsecs.value();
     842                 :     }
     843                 :   }
     844               0 :   if (mReader->mAudioQueue.AtEndOfStream() &&
     845                 :       mState != DECODER_STATE_SHUTDOWN &&
     846               0 :       !mStopAudioThread)
     847                 :   {
     848                 :     // Last frame pushed to audio hardware, wait for the audio to finish,
     849                 :     // before the audio thread terminates.
     850               0 :     bool seeking = false;
     851                 :     {
     852               0 :       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     853               0 :       PRInt64 unplayedFrames = audioDuration % minWriteFrames;
     854               0 :       if (minWriteFrames > 1 && unplayedFrames > 0) {
     855                 :         // Sound is written by libsydneyaudio to the hardware in blocks of
     856                 :         // frames of size minWriteFrames. So if the number of frames we've
     857                 :         // written isn't an exact multiple of minWriteFrames, we'll have
     858                 :         // left over audio data which hasn't yet been written to the hardware,
     859                 :         // and so that audio will not start playing. Write silence to ensure
     860                 :         // the last block gets pushed to hardware, so that playback starts.
     861               0 :         PRInt64 framesToWrite = minWriteFrames - unplayedFrames;
     862               0 :         if (framesToWrite < PR_UINT32_MAX / channels) {
     863                 :           // Write silence manually rather than using PlaySilence(), so that
     864                 :           // the AudioAPI doesn't get a copy of the audio frames.
     865               0 :           PRUint32 numSamples = framesToWrite * channels;
     866               0 :           nsAutoArrayPtr<AudioDataValue> buf(new AudioDataValue[numSamples]);
     867               0 :           memset(buf.get(), 0, numSamples * sizeof(AudioDataValue));
     868               0 :           mAudioStream->Write(buf, framesToWrite);
     869                 :         }
     870                 :       }
     871                 : 
     872               0 :       PRInt64 oldPosition = -1;
     873               0 :       PRInt64 position = GetMediaTime();
     874               0 :       while (oldPosition != position &&
     875                 :              mAudioEndTime - position > 0 &&
     876                 :              mState != DECODER_STATE_SEEKING &&
     877                 :              mState != DECODER_STATE_SHUTDOWN)
     878                 :       {
     879               0 :         const PRInt64 DRAIN_BLOCK_USECS = 100000;
     880               0 :         Wait(NS_MIN(mAudioEndTime - position, DRAIN_BLOCK_USECS));
     881               0 :         oldPosition = position;
     882               0 :         position = GetMediaTime();
     883                 :       }
     884               0 :       seeking = mState == DECODER_STATE_SEEKING;
     885                 :     }
     886                 : 
     887               0 :     if (!seeking && !mAudioStream->IsPaused()) {
     888               0 :       mAudioStream->Drain();
     889                 :       // Fire one last event for any extra frames that didn't fill a framebuffer.
     890               0 :       mEventManager.Drain(mAudioEndTime);
     891                 :     }
     892                 :   }
     893               0 :   LOG(PR_LOG_DEBUG, ("%p Reached audio stream end.", mDecoder.get()));
     894                 :   {
     895                 :     // Must hold lock while anulling the audio stream to prevent
     896                 :     // state machine thread trying to use it while we're destroying it.
     897               0 :     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     898               0 :     mAudioStream = nsnull;
     899               0 :     mEventManager.Clear();
     900               0 :     mAudioCompleted = true;
     901               0 :     UpdateReadyState();
     902                 :     // Kick the decode thread; it may be sleeping waiting for this to finish.
     903               0 :     mDecoder->GetReentrantMonitor().NotifyAll();
     904                 :   }
     905                 : 
     906                 :   // Must not hold the decoder monitor while we shutdown the audio stream, as
     907                 :   // it makes a synchronous dispatch on Android.
     908               0 :   audioStream->Shutdown();
     909               0 :   audioStream = nsnull;
     910                 : 
     911               0 :   LOG(PR_LOG_DEBUG, ("%p Audio stream finished playing, audio thread exit", mDecoder.get()));
     912               0 : }
     913                 : 
     914               0 : PRUint32 nsBuiltinDecoderStateMachine::PlaySilence(PRUint32 aFrames,
     915                 :                                                    PRUint32 aChannels,
     916                 :                                                    PRUint64 aFrameOffset)
     917                 : 
     918                 : {
     919               0 :   NS_ASSERTION(OnAudioThread(), "Only call on audio thread.");
     920               0 :   NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
     921               0 :   PRUint32 maxFrames = SILENCE_BYTES_CHUNK / aChannels / sizeof(AudioDataValue);
     922               0 :   PRUint32 frames = NS_MIN(aFrames, maxFrames);
     923               0 :   PRUint32 numSamples = frames * aChannels;
     924               0 :   nsAutoArrayPtr<AudioDataValue> buf(new AudioDataValue[numSamples]);
     925               0 :   memset(buf.get(), 0, numSamples * sizeof(AudioDataValue));
     926               0 :   mAudioStream->Write(buf, frames);
     927                 :   // Dispatch events to the DOM for the audio just written.
     928                 :   mEventManager.QueueWrittenAudioData(buf.get(), frames * aChannels,
     929               0 :                                       (aFrameOffset + frames) * aChannels);
     930               0 :   return frames;
     931                 : }
     932                 : 
     933               0 : PRUint32 nsBuiltinDecoderStateMachine::PlayFromAudioQueue(PRUint64 aFrameOffset,
     934                 :                                                           PRUint32 aChannels)
     935                 : {
     936               0 :   NS_ASSERTION(OnAudioThread(), "Only call on audio thread.");
     937               0 :   NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
     938               0 :   nsAutoPtr<AudioData> audio(mReader->mAudioQueue.PopFront());
     939                 :   {
     940               0 :     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     941               0 :     NS_WARN_IF_FALSE(IsPlaying(), "Should be playing");
     942                 :     // Awaken the decode loop if it's waiting for space to free up in the
     943                 :     // audio queue.
     944               0 :     mDecoder->GetReentrantMonitor().NotifyAll();
     945                 :   }
     946               0 :   PRInt64 offset = -1;
     947               0 :   PRUint32 frames = 0;
     948                 :   // The state machine could have paused since we've released the decoder
     949                 :   // monitor and acquired the audio monitor. Rather than acquire both
     950                 :   // monitors, the audio stream also maintains whether its paused or not.
     951                 :   // This prevents us from doing a blocking write while holding the audio
     952                 :   // monitor while paused; we would block, and the state machine won't be
     953                 :   // able to acquire the audio monitor in order to resume or destroy the
     954                 :   // audio stream.
     955               0 :   if (!mAudioStream->IsPaused()) {
     956               0 :     mAudioStream->Write(audio->mAudioData,
     957               0 :                         audio->mFrames);
     958                 : 
     959               0 :     offset = audio->mOffset;
     960               0 :     frames = audio->mFrames;
     961                 : 
     962                 :     // Dispatch events to the DOM for the audio just written.
     963               0 :     mEventManager.QueueWrittenAudioData(audio->mAudioData.get(),
     964               0 :                                         audio->mFrames * aChannels,
     965               0 :                                         (aFrameOffset + frames) * aChannels);
     966                 :   } else {
     967               0 :     mReader->mAudioQueue.PushFront(audio);
     968               0 :     audio.forget();
     969                 :   }
     970               0 :   if (offset != -1) {
     971               0 :     mDecoder->UpdatePlaybackOffset(offset);
     972                 :   }
     973               0 :   return frames;
     974                 : }
     975                 : 
     976               0 : nsresult nsBuiltinDecoderStateMachine::Init(nsDecoderStateMachine* aCloneDonor)
     977                 : {
     978               0 :   nsBuiltinDecoderReader* cloneReader = nsnull;
     979               0 :   if (aCloneDonor) {
     980               0 :     cloneReader = static_cast<nsBuiltinDecoderStateMachine*>(aCloneDonor)->mReader;
     981                 :   }
     982               0 :   return mReader->Init(cloneReader);
     983                 : }
     984                 : 
     985               0 : void nsBuiltinDecoderStateMachine::StopPlayback()
     986                 : {
     987               0 :   LOG(PR_LOG_DEBUG, ("%p StopPlayback()", mDecoder.get()));
     988                 : 
     989               0 :   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
     990                 :                "Should be on state machine thread.");
     991               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
     992                 : 
     993               0 :   mDecoder->mPlaybackStatistics.Stop(TimeStamp::Now());
     994                 : 
     995                 :   // Reset mPlayStartTime before we pause/shutdown the nsAudioStream. This is
     996                 :   // so that if the audio loop is about to write audio, it will have the chance
     997                 :   // to check to see if we're paused and not write the audio. If not, the
     998                 :   // audio thread can block in the write, and we deadlock trying to acquire
     999                 :   // the audio monitor upon resume playback.
    1000               0 :   if (IsPlaying()) {
    1001               0 :     mPlayDuration += DurationToUsecs(TimeStamp::Now() - mPlayStartTime);
    1002               0 :     mPlayStartTime = TimeStamp();
    1003                 :   }
    1004                 :   // Notify the audio thread, so that it notices that we've stopped playing,
    1005                 :   // so it can pause audio playback.
    1006               0 :   mDecoder->GetReentrantMonitor().NotifyAll();
    1007               0 :   NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
    1008               0 : }
    1009                 : 
    1010               0 : void nsBuiltinDecoderStateMachine::StartPlayback()
    1011                 : {
    1012               0 :   LOG(PR_LOG_DEBUG, ("%p StartPlayback()", mDecoder.get()));
    1013                 : 
    1014               0 :   NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
    1015               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1016               0 :   LOG(PR_LOG_DEBUG, ("%p StartPlayback", mDecoder.get()));
    1017               0 :   mDecoder->mPlaybackStatistics.Start(TimeStamp::Now());
    1018               0 :   mPlayStartTime = TimeStamp::Now();
    1019                 : 
    1020               0 :   NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
    1021               0 :   if (NS_FAILED(StartAudioThread())) {
    1022               0 :     NS_WARNING("Failed to create audio thread"); 
    1023                 :   }
    1024               0 :   mDecoder->GetReentrantMonitor().NotifyAll();
    1025               0 : }
    1026                 : 
    1027               0 : void nsBuiltinDecoderStateMachine::UpdatePlaybackPositionInternal(PRInt64 aTime)
    1028                 : {
    1029               0 :   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
    1030                 :                "Should be on state machine thread.");
    1031               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1032                 : 
    1033               0 :   NS_ASSERTION(mStartTime >= 0, "Should have positive mStartTime");
    1034               0 :   mCurrentFrameTime = aTime - mStartTime;
    1035               0 :   NS_ASSERTION(mCurrentFrameTime >= 0, "CurrentTime should be positive!");
    1036               0 :   if (aTime > mEndTime) {
    1037               0 :     NS_ASSERTION(mCurrentFrameTime > GetDuration(),
    1038                 :                  "CurrentTime must be after duration if aTime > endTime!");
    1039               0 :     mEndTime = aTime;
    1040                 :     nsCOMPtr<nsIRunnable> event =
    1041               0 :       NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DurationChanged);
    1042               0 :     NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
    1043                 :   }
    1044               0 : }
    1045                 : 
    1046               0 : void nsBuiltinDecoderStateMachine::UpdatePlaybackPosition(PRInt64 aTime)
    1047                 : {
    1048               0 :   UpdatePlaybackPositionInternal(aTime);
    1049                 : 
    1050               0 :   bool fragmentEnded = mFragmentEndTime >= 0 && GetMediaTime() >= mFragmentEndTime;
    1051               0 :   if (!mPositionChangeQueued || fragmentEnded) {
    1052               0 :     mPositionChangeQueued = true;
    1053                 :     nsCOMPtr<nsIRunnable> event =
    1054               0 :       NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::PlaybackPositionChanged);
    1055               0 :     NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
    1056                 :   }
    1057                 : 
    1058                 :   // Notify DOM of any queued up audioavailable events
    1059               0 :   mEventManager.DispatchPendingEvents(GetMediaTime());
    1060                 : 
    1061               0 :   if (fragmentEnded) {
    1062               0 :     StopPlayback();
    1063                 :   }
    1064               0 : }
    1065                 : 
    1066               0 : void nsBuiltinDecoderStateMachine::ClearPositionChangeFlag()
    1067                 : {
    1068               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    1069               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1070                 : 
    1071               0 :   mPositionChangeQueued = false;
    1072               0 : }
    1073                 : 
    1074               0 : nsHTMLMediaElement::NextFrameStatus nsBuiltinDecoderStateMachine::GetNextFrameStatus()
    1075                 : {
    1076               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    1077               0 :   if (IsBuffering() || IsSeeking()) {
    1078               0 :     return nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING;
    1079               0 :   } else if (HaveNextFrameData()) {
    1080               0 :     return nsHTMLMediaElement::NEXT_FRAME_AVAILABLE;
    1081                 :   }
    1082               0 :   return nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE;
    1083                 : }
    1084                 : 
    1085               0 : void nsBuiltinDecoderStateMachine::SetVolume(double volume)
    1086                 : {
    1087               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    1088               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    1089               0 :   mVolume = volume;
    1090               0 : }
    1091                 : 
    1092               0 : double nsBuiltinDecoderStateMachine::GetCurrentTime() const
    1093                 : {
    1094               0 :   NS_ASSERTION(NS_IsMainThread() ||
    1095                 :                OnStateMachineThread() ||
    1096                 :                OnDecodeThread(),
    1097                 :                "Should be on main, decode, or state machine thread.");
    1098                 : 
    1099               0 :   return static_cast<double>(mCurrentFrameTime) / static_cast<double>(USECS_PER_S);
    1100                 : }
    1101                 : 
    1102               0 : PRInt64 nsBuiltinDecoderStateMachine::GetDuration()
    1103                 : {
    1104               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1105                 : 
    1106               0 :   if (mEndTime == -1 || mStartTime == -1)
    1107               0 :     return -1;
    1108               0 :   return mEndTime - mStartTime;
    1109                 : }
    1110                 : 
    1111               0 : void nsBuiltinDecoderStateMachine::SetDuration(PRInt64 aDuration)
    1112                 : {
    1113               0 :   NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
    1114                 :                "Should be on main or decode thread.");
    1115               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1116                 : 
    1117               0 :   if (aDuration == -1) {
    1118               0 :     return;
    1119                 :   }
    1120                 : 
    1121               0 :   if (mStartTime != -1) {
    1122               0 :     mEndTime = mStartTime + aDuration;
    1123                 :   } else {
    1124               0 :     mStartTime = 0;
    1125               0 :     mEndTime = aDuration;
    1126                 :   }
    1127                 : }
    1128                 : 
    1129               0 : void nsBuiltinDecoderStateMachine::SetEndTime(PRInt64 aEndTime)
    1130                 : {
    1131               0 :   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread");
    1132               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1133                 : 
    1134               0 :   mEndTime = aEndTime;
    1135               0 : }
    1136                 : 
    1137               0 : void nsBuiltinDecoderStateMachine::SetFragmentEndTime(PRInt64 aEndTime)
    1138                 : {
    1139               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1140                 : 
    1141               0 :   mFragmentEndTime = aEndTime < 0 ? aEndTime : aEndTime + mStartTime;
    1142               0 : }
    1143                 : 
    1144               0 : void nsBuiltinDecoderStateMachine::SetSeekable(bool aSeekable)
    1145                 : {
    1146               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    1147               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1148                 : 
    1149               0 :   mSeekable = aSeekable;
    1150               0 : }
    1151                 : 
    1152               0 : void nsBuiltinDecoderStateMachine::Shutdown()
    1153                 : {
    1154               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    1155                 : 
    1156                 :   // Once we've entered the shutdown state here there's no going back.
    1157               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    1158                 : 
    1159                 :   // Change state before issuing shutdown request to threads so those
    1160                 :   // threads can start exiting cleanly during the Shutdown call.
    1161               0 :   LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN", mDecoder.get()));
    1162               0 :   ScheduleStateMachine();
    1163               0 :   mState = DECODER_STATE_SHUTDOWN;
    1164               0 :   mDecoder->GetReentrantMonitor().NotifyAll();
    1165               0 : }
    1166                 : 
    1167               0 : void nsBuiltinDecoderStateMachine::StartDecoding()
    1168                 : {
    1169               0 :   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
    1170                 :                "Should be on state machine or decode thread.");
    1171               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    1172               0 :   if (mState != DECODER_STATE_DECODING) {
    1173               0 :     mDecodeStartTime = TimeStamp::Now();
    1174                 :   }
    1175               0 :   mState = DECODER_STATE_DECODING;
    1176               0 :   ScheduleStateMachine();
    1177               0 : }
    1178                 : 
    1179               0 : void nsBuiltinDecoderStateMachine::Play()
    1180                 : {
    1181               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    1182                 :   // When asked to play, switch to decoding state only if
    1183                 :   // we are currently buffering. In other cases, we'll start playing anyway
    1184                 :   // when the state machine notices the decoder's state change to PLAYING.
    1185               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    1186               0 :   if (mState == DECODER_STATE_BUFFERING) {
    1187               0 :     LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder.get()));
    1188               0 :     mState = DECODER_STATE_DECODING;
    1189               0 :     mDecodeStartTime = TimeStamp::Now();
    1190                 :   }
    1191               0 :   ScheduleStateMachine();
    1192               0 : }
    1193                 : 
    1194               0 : void nsBuiltinDecoderStateMachine::ResetPlayback()
    1195                 : {
    1196               0 :   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
    1197               0 :   mVideoFrameEndTime = -1;
    1198               0 :   mAudioStartTime = -1;
    1199               0 :   mAudioEndTime = -1;
    1200               0 :   mAudioCompleted = false;
    1201               0 : }
    1202                 : 
    1203               0 : void nsBuiltinDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
    1204                 :                                                      PRUint32 aLength,
    1205                 :                                                      PRInt64 aOffset)
    1206                 : {
    1207               0 :   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
    1208               0 :   mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
    1209                 : 
    1210                 :   // While playing an unseekable stream of unknown duration, mEndTime is
    1211                 :   // updated (in AdvanceFrame()) as we play. But if data is being downloaded
    1212                 :   // faster than played, mEndTime won't reflect the end of playable data
    1213                 :   // since we haven't played the frame at the end of buffered data. So update
    1214                 :   // mEndTime here as new data is downloaded to prevent such a lag.
    1215               0 :   nsTimeRanges buffered;
    1216               0 :   if (mDecoder->IsInfinite() &&
    1217               0 :       NS_SUCCEEDED(mDecoder->GetBuffered(&buffered)))
    1218                 :   {
    1219               0 :     PRUint32 length = 0;
    1220               0 :     buffered.GetLength(&length);
    1221               0 :     if (length) {
    1222               0 :       double end = 0;
    1223               0 :       buffered.End(length - 1, &end);
    1224               0 :       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    1225               0 :       mEndTime = NS_MAX<PRInt64>(mEndTime, end * USECS_PER_S);
    1226                 :     }
    1227                 :   }
    1228               0 : }
    1229                 : 
    1230               0 : void nsBuiltinDecoderStateMachine::Seek(double aTime)
    1231                 : {
    1232               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    1233               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    1234                 :   // nsBuiltinDecoder::mPlayState should be SEEKING while we seek, and
    1235                 :   // in that case nsBuiltinDecoder shouldn't be calling us.
    1236               0 :   NS_ASSERTION(mState != DECODER_STATE_SEEKING,
    1237                 :                "We shouldn't already be seeking");
    1238               0 :   NS_ASSERTION(mState >= DECODER_STATE_DECODING,
    1239                 :                "We should have loaded metadata");
    1240               0 :   double t = aTime * static_cast<double>(USECS_PER_S);
    1241               0 :   if (t > INT64_MAX) {
    1242                 :     // Prevent integer overflow.
    1243                 :     return;
    1244                 :   }
    1245                 : 
    1246               0 :   mSeekTime = static_cast<PRInt64>(t) + mStartTime;
    1247               0 :   NS_ASSERTION(mSeekTime >= mStartTime && mSeekTime <= mEndTime,
    1248                 :                "Can only seek in range [0,duration]");
    1249                 : 
    1250                 :   // Bound the seek time to be inside the media range.
    1251               0 :   NS_ASSERTION(mStartTime != -1, "Should know start time by now");
    1252               0 :   NS_ASSERTION(mEndTime != -1, "Should know end time by now");
    1253               0 :   mSeekTime = NS_MIN(mSeekTime, mEndTime);
    1254               0 :   mSeekTime = NS_MAX(mStartTime, mSeekTime);
    1255               0 :   LOG(PR_LOG_DEBUG, ("%p Changed state to SEEKING (to %f)", mDecoder.get(), aTime));
    1256               0 :   mState = DECODER_STATE_SEEKING;
    1257               0 :   ScheduleStateMachine();
    1258                 : }
    1259                 : 
    1260               0 : void nsBuiltinDecoderStateMachine::StopDecodeThread()
    1261                 : {
    1262               0 :   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
    1263               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1264               0 :   if (mRequestedNewDecodeThread) {
    1265                 :     // We've requested that the decode be created, but it hasn't been yet.
    1266                 :     // Cancel that request.
    1267               0 :     NS_ASSERTION(!mDecodeThread,
    1268                 :       "Shouldn't have a decode thread until after request processed");
    1269               0 :     StateMachineTracker::Instance().CancelCreateDecodeThread(this);
    1270               0 :     mRequestedNewDecodeThread = false;
    1271                 :   }
    1272               0 :   mStopDecodeThread = true;
    1273               0 :   mDecoder->GetReentrantMonitor().NotifyAll();
    1274               0 :   if (mDecodeThread) {
    1275               0 :     LOG(PR_LOG_DEBUG, ("%p Shutdown decode thread", mDecoder.get()));
    1276                 :     {
    1277               0 :       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    1278               0 :       mDecodeThread->Shutdown();
    1279               0 :       StateMachineTracker::Instance().NoteDecodeThreadDestroyed();
    1280                 :     }
    1281               0 :     mDecodeThread = nsnull;
    1282               0 :     mDecodeThreadIdle = false;
    1283                 :   }
    1284               0 :   NS_ASSERTION(!mRequestedNewDecodeThread,
    1285                 :     "Any pending requests for decode threads must be canceled and unflagged");
    1286               0 :   NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this),
    1287                 :     "Any pending requests for decode threads must be canceled");
    1288               0 : }
    1289                 : 
    1290               0 : void nsBuiltinDecoderStateMachine::StopAudioThread()
    1291                 : {
    1292               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1293               0 :   mStopAudioThread = true;
    1294               0 :   mDecoder->GetReentrantMonitor().NotifyAll();
    1295               0 :   if (mAudioThread) {
    1296               0 :     LOG(PR_LOG_DEBUG, ("%p Shutdown audio thread", mDecoder.get()));
    1297                 :     {
    1298               0 :       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    1299               0 :       mAudioThread->Shutdown();
    1300                 :     }
    1301               0 :     mAudioThread = nsnull;
    1302                 :   }
    1303               0 : }
    1304                 : 
    1305                 : nsresult
    1306               0 : nsBuiltinDecoderStateMachine::ScheduleDecodeThread()
    1307                 : {
    1308               0 :   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
    1309               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1310                 :  
    1311               0 :   mStopDecodeThread = false;
    1312               0 :   if (mState >= DECODER_STATE_COMPLETED) {
    1313               0 :     return NS_OK;
    1314                 :   }
    1315               0 :   if (mDecodeThread) {
    1316               0 :     NS_ASSERTION(!mRequestedNewDecodeThread,
    1317                 :       "Shouldn't have requested new decode thread when we have a decode thread");
    1318                 :     // We already have a decode thread...
    1319               0 :     if (mDecodeThreadIdle) {
    1320                 :       // ... and it's not been shutdown yet, wake it up.
    1321                 :       nsCOMPtr<nsIRunnable> event =
    1322               0 :         NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeThreadRun);
    1323               0 :       mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
    1324               0 :       mDecodeThreadIdle = false;
    1325                 :     }
    1326               0 :     return NS_OK;
    1327               0 :   } else if (!mRequestedNewDecodeThread) {
    1328                 :   // We don't already have a decode thread, request a new one.
    1329               0 :     mRequestedNewDecodeThread = true;
    1330               0 :     ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor());
    1331               0 :     StateMachineTracker::Instance().RequestCreateDecodeThread(this);
    1332                 :   }
    1333               0 :   return NS_OK;
    1334                 : }
    1335                 : 
    1336                 : nsresult
    1337               0 : nsBuiltinDecoderStateMachine::StartDecodeThread()
    1338                 : {
    1339               0 :   NS_ASSERTION(StateMachineTracker::Instance().GetDecodeThreadCount() <
    1340                 :                StateMachineTracker::MAX_DECODE_THREADS,
    1341                 :                "Should not have reached decode thread limit");
    1342                 : 
    1343               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    1344               0 :   NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this),
    1345                 :     "Should not already have a pending request for a new decode thread.");
    1346               0 :   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
    1347               0 :   NS_ASSERTION(!mDecodeThread, "Should not have decode thread yet");
    1348               0 :   NS_ASSERTION(mRequestedNewDecodeThread, "Should have requested this...");
    1349                 : 
    1350               0 :   mRequestedNewDecodeThread = false;
    1351                 : 
    1352               0 :   nsresult rv = NS_NewThread(getter_AddRefs(mDecodeThread),
    1353                 :                               nsnull,
    1354               0 :                               MEDIA_THREAD_STACK_SIZE);
    1355               0 :   if (NS_FAILED(rv)) {
    1356                 :     // Give up, report error to media element.
    1357                 :     nsCOMPtr<nsIRunnable> event =
    1358               0 :       NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError);
    1359               0 :     NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
    1360               0 :     return rv;
    1361                 :   }
    1362                 : 
    1363                 :   nsCOMPtr<nsIRunnable> event =
    1364               0 :     NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeThreadRun);
    1365               0 :   mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
    1366               0 :   mDecodeThreadIdle = false;
    1367                 : 
    1368               0 :   return NS_OK;
    1369                 : }
    1370                 : 
    1371                 : nsresult
    1372               0 : nsBuiltinDecoderStateMachine::StartAudioThread()
    1373                 : {
    1374               0 :   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
    1375                 :                "Should be on state machine or decode thread.");
    1376               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1377               0 :   mStopAudioThread = false;
    1378               0 :   if (HasAudio() && !mAudioThread) {
    1379               0 :     nsresult rv = NS_NewThread(getter_AddRefs(mAudioThread),
    1380                 :                                nsnull,
    1381               0 :                                MEDIA_THREAD_STACK_SIZE);
    1382               0 :     if (NS_FAILED(rv)) {
    1383               0 :       LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN because failed to create audio thread", mDecoder.get()));
    1384               0 :       mState = DECODER_STATE_SHUTDOWN;
    1385               0 :       return rv;
    1386                 :     }
    1387                 :     nsCOMPtr<nsIRunnable> event =
    1388               0 :       NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::AudioLoop);
    1389               0 :     mAudioThread->Dispatch(event, NS_DISPATCH_NORMAL);
    1390                 :   }
    1391               0 :   return NS_OK;
    1392                 : }
    1393                 : 
    1394               0 : PRInt64 nsBuiltinDecoderStateMachine::AudioDecodedUsecs() const
    1395                 : {
    1396               0 :   NS_ASSERTION(HasAudio(),
    1397                 :                "Should only call AudioDecodedUsecs() when we have audio");
    1398                 :   // The amount of audio we have decoded is the amount of audio data we've
    1399                 :   // already decoded and pushed to the hardware, plus the amount of audio
    1400                 :   // data waiting to be pushed to the hardware.
    1401               0 :   PRInt64 pushed = (mAudioEndTime != -1) ? (mAudioEndTime - GetMediaTime()) : 0;
    1402               0 :   return pushed + mReader->mAudioQueue.Duration();
    1403                 : }
    1404                 : 
    1405               0 : bool nsBuiltinDecoderStateMachine::HasLowDecodedData(PRInt64 aAudioUsecs) const
    1406                 : {
    1407               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1408                 :   // We consider ourselves low on decoded data if we're low on audio,
    1409                 :   // provided we've not decoded to the end of the audio stream, or
    1410                 :   // if we're only playing video and we're low on video frames, provided
    1411                 :   // we've not decoded to the end of the video stream.
    1412               0 :   return ((HasAudio() &&
    1413               0 :            !mReader->mAudioQueue.IsFinished() &&
    1414               0 :            AudioDecodedUsecs() < aAudioUsecs)
    1415                 :           ||
    1416               0 :          (!HasAudio() &&
    1417               0 :           HasVideo() &&
    1418               0 :           !mReader->mVideoQueue.IsFinished() &&
    1419               0 :           static_cast<PRUint32>(mReader->mVideoQueue.GetSize()) < LOW_VIDEO_FRAMES));
    1420                 : }
    1421                 : 
    1422               0 : bool nsBuiltinDecoderStateMachine::HasLowUndecodedData() const
    1423                 : {
    1424               0 :   return GetUndecodedData() < mLowDataThresholdUsecs;
    1425                 : }
    1426                 : 
    1427               0 : PRInt64 nsBuiltinDecoderStateMachine::GetUndecodedData() const
    1428                 : {
    1429               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1430               0 :   NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
    1431                 :                "Must have loaded metadata for GetBuffered() to work");
    1432               0 :   nsTimeRanges buffered;
    1433                 :   
    1434               0 :   nsresult res = mDecoder->GetBuffered(&buffered);
    1435               0 :   NS_ENSURE_SUCCESS(res, 0);
    1436               0 :   double currentTime = GetCurrentTime();
    1437                 : 
    1438               0 :   nsIDOMTimeRanges* r = static_cast<nsIDOMTimeRanges*>(&buffered);
    1439               0 :   PRUint32 length = 0;
    1440               0 :   res = r->GetLength(&length);
    1441               0 :   NS_ENSURE_SUCCESS(res, 0);
    1442                 : 
    1443               0 :   for (PRUint32 index = 0; index < length; ++index) {
    1444                 :     double start, end;
    1445               0 :     res = r->Start(index, &start);
    1446               0 :     NS_ENSURE_SUCCESS(res, 0);
    1447                 : 
    1448               0 :     res = r->End(index, &end);
    1449               0 :     NS_ENSURE_SUCCESS(res, 0);
    1450                 : 
    1451               0 :     if (start <= currentTime && end >= currentTime) {
    1452               0 :       return static_cast<PRInt64>((end - currentTime) * USECS_PER_S);
    1453                 :     }
    1454                 :   }
    1455               0 :   return 0;
    1456                 : }
    1457                 : 
    1458               0 : void nsBuiltinDecoderStateMachine::SetFrameBufferLength(PRUint32 aLength)
    1459                 : {
    1460               0 :   NS_ASSERTION(aLength >= 512 && aLength <= 16384,
    1461                 :                "The length must be between 512 and 16384");
    1462               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1463               0 :   mEventManager.SetSignalBufferLength(aLength);
    1464               0 : }
    1465                 : 
    1466               0 : nsresult nsBuiltinDecoderStateMachine::DecodeMetadata()
    1467                 : {
    1468               0 :   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
    1469               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1470               0 :   NS_ASSERTION(mState == DECODER_STATE_DECODING_METADATA,
    1471                 :                "Only call when in metadata decoding state");
    1472                 : 
    1473               0 :   LOG(PR_LOG_DEBUG, ("%p Decoding Media Headers", mDecoder.get()));
    1474                 :   nsresult res;
    1475               0 :   nsVideoInfo info;
    1476                 :   {
    1477               0 :     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    1478               0 :     res = mReader->ReadMetadata(&info);
    1479                 :   }
    1480               0 :   mInfo = info;
    1481                 : 
    1482               0 :   if (NS_FAILED(res) || (!info.mHasVideo && !info.mHasAudio)) {
    1483                 :     // Dispatch the event to call DecodeError synchronously. This ensures
    1484                 :     // we're in shutdown state by the time we exit the decode thread.
    1485                 :     // If we just moved to shutdown state here on the decode thread, we may
    1486                 :     // cause the state machine to shutdown/free memory without closing its
    1487                 :     // media stream properly, and we'll get callbacks from the media stream
    1488                 :     // causing a crash. Note the state machine shutdown joins this decode
    1489                 :     // thread during shutdown (and other state machines can run on the state
    1490                 :     // machine thread while the join is waiting), so it's safe to do this
    1491                 :     // synchronously.
    1492                 :     nsCOMPtr<nsIRunnable> event =
    1493               0 :       NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::DecodeError);
    1494               0 :     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    1495               0 :     NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
    1496               0 :     return NS_ERROR_FAILURE;
    1497                 :   }
    1498               0 :   mDecoder->StartProgressUpdates();
    1499               0 :   mGotDurationFromMetaData = (GetDuration() != -1);
    1500                 : 
    1501               0 :   VideoData* videoData = FindStartTime();
    1502               0 :   if (videoData) {
    1503               0 :     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    1504               0 :     RenderVideoFrame(videoData, TimeStamp::Now());
    1505                 :   }
    1506                 : 
    1507               0 :   if (mState == DECODER_STATE_SHUTDOWN) {
    1508               0 :     return NS_ERROR_FAILURE;
    1509                 :   }
    1510                 : 
    1511               0 :   NS_ASSERTION(mStartTime != -1, "Must have start time");
    1512               0 :   NS_ASSERTION((!HasVideo() && !HasAudio()) ||
    1513                 :                 !mSeekable || mEndTime != -1,
    1514                 :                 "Active seekable media should have end time");
    1515               0 :   NS_ASSERTION(!mSeekable || GetDuration() != -1, "Seekable media should have duration");
    1516               0 :   LOG(PR_LOG_DEBUG, ("%p Media goes from %lld to %lld (duration %lld) seekable=%d",
    1517                 :                       mDecoder.get(), mStartTime, mEndTime, GetDuration(), mSeekable));
    1518                 : 
    1519                 :   // Inform the element that we've loaded the metadata and the first frame,
    1520                 :   // setting the default framebuffer size for audioavailable events.  Also,
    1521                 :   // if there is audio, let the MozAudioAvailable event manager know about
    1522                 :   // the metadata.
    1523               0 :   if (HasAudio()) {
    1524               0 :     mEventManager.Init(mInfo.mAudioChannels, mInfo.mAudioRate);
    1525                 :     // Set the buffer length at the decoder level to be able, to be able
    1526                 :     // to retrive the value via media element method. The RequestFrameBufferLength
    1527                 :     // will call the nsBuiltinDecoderStateMachine::SetFrameBufferLength().
    1528               0 :     PRUint32 frameBufferLength = mInfo.mAudioChannels * FRAMEBUFFER_LENGTH_PER_CHANNEL;
    1529               0 :     mDecoder->RequestFrameBufferLength(frameBufferLength);
    1530                 :   }
    1531                 :   nsCOMPtr<nsIRunnable> metadataLoadedEvent =
    1532               0 :     new nsAudioMetadataEventRunner(mDecoder, mInfo.mAudioChannels, mInfo.mAudioRate);
    1533               0 :   NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
    1534                 : 
    1535               0 :   if (mState == DECODER_STATE_DECODING_METADATA) {
    1536               0 :     LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder.get()));
    1537               0 :     StartDecoding();
    1538                 :   }
    1539                 : 
    1540               0 :   if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED) &&
    1541               0 :       mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
    1542               0 :       !IsPlaying())
    1543                 :   {
    1544               0 :     StartPlayback();
    1545                 :   }
    1546                 : 
    1547               0 :   return NS_OK;
    1548                 : }
    1549                 : 
    1550               0 : void nsBuiltinDecoderStateMachine::DecodeSeek()
    1551                 : {
    1552               0 :   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
    1553               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1554               0 :   NS_ASSERTION(mState == DECODER_STATE_SEEKING,
    1555                 :                "Only call when in seeking state");
    1556                 : 
    1557                 :   // During the seek, don't have a lock on the decoder state,
    1558                 :   // otherwise long seek operations can block the main thread.
    1559                 :   // The events dispatched to the main thread are SYNC calls.
    1560                 :   // These calls are made outside of the decode monitor lock so
    1561                 :   // it is safe for the main thread to makes calls that acquire
    1562                 :   // the lock since it won't deadlock. We check the state when
    1563                 :   // acquiring the lock again in case shutdown has occurred
    1564                 :   // during the time when we didn't have the lock.
    1565               0 :   PRInt64 seekTime = mSeekTime;
    1566               0 :   mDecoder->StopProgressUpdates();
    1567                 : 
    1568               0 :   bool currentTimeChanged = false;
    1569               0 :   PRInt64 mediaTime = GetMediaTime();
    1570               0 :   if (mediaTime != seekTime) {
    1571               0 :     currentTimeChanged = true;
    1572               0 :     UpdatePlaybackPositionInternal(seekTime);
    1573                 :   }
    1574                 : 
    1575                 :   // SeekingStarted will do a UpdateReadyStateForData which will
    1576                 :   // inform the element and its users that we have no frames
    1577                 :   // to display
    1578                 :   {
    1579               0 :     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    1580                 :     nsCOMPtr<nsIRunnable> startEvent =
    1581               0 :       NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStarted);
    1582               0 :     NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
    1583                 :   }
    1584                 : 
    1585               0 :   if (currentTimeChanged) {
    1586                 :     // The seek target is different than the current playback position,
    1587                 :     // we'll need to seek the playback position, so shutdown our decode
    1588                 :     // and audio threads.
    1589               0 :     StopPlayback();
    1590               0 :     StopAudioThread();
    1591               0 :     ResetPlayback();
    1592                 :     nsresult res;
    1593                 :     {
    1594               0 :       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    1595                 :       // Now perform the seek. We must not hold the state machine monitor
    1596                 :       // while we seek, since the seek reads, which could block on I/O.
    1597               0 :       res = mReader->Seek(seekTime,
    1598                 :                           mStartTime,
    1599                 :                           mEndTime,
    1600               0 :                           mediaTime);
    1601                 :     }
    1602               0 :     if (NS_SUCCEEDED(res)) {
    1603               0 :       AudioData* audio = HasAudio() ? mReader->mAudioQueue.PeekFront() : nsnull;
    1604               0 :       NS_ASSERTION(!audio || (audio->mTime <= seekTime &&
    1605                 :                               seekTime <= audio->mTime + audio->mDuration),
    1606                 :                     "Seek target should lie inside the first audio block after seek");
    1607               0 :       PRInt64 startTime = (audio && audio->mTime < seekTime) ? audio->mTime : seekTime;
    1608               0 :       mAudioStartTime = startTime;
    1609               0 :       mPlayDuration = startTime - mStartTime;
    1610               0 :       if (HasVideo()) {
    1611               0 :         nsAutoPtr<VideoData> video(mReader->mVideoQueue.PeekFront());
    1612               0 :         if (video) {
    1613               0 :           NS_ASSERTION(video->mTime <= seekTime && seekTime <= video->mEndTime,
    1614                 :                         "Seek target should lie inside the first frame after seek");
    1615                 :           {
    1616               0 :             ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    1617               0 :             RenderVideoFrame(video, TimeStamp::Now());
    1618                 :           }
    1619               0 :           mReader->mVideoQueue.PopFront();
    1620                 :           nsCOMPtr<nsIRunnable> event =
    1621               0 :             NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::Invalidate);
    1622               0 :           NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
    1623                 :         }
    1624                 :       }
    1625                 :     }
    1626                 :   }
    1627               0 :   mDecoder->StartProgressUpdates();
    1628               0 :   if (mState == DECODER_STATE_SHUTDOWN)
    1629               0 :     return;
    1630                 : 
    1631                 :   // Try to decode another frame to detect if we're at the end...
    1632               0 :   LOG(PR_LOG_DEBUG, ("%p Seek completed, mCurrentFrameTime=%lld\n",
    1633                 :       mDecoder.get(), mCurrentFrameTime));
    1634                 : 
    1635                 :   // Change state to DECODING or COMPLETED now. SeekingStopped will
    1636                 :   // call nsBuiltinDecoderStateMachine::Seek to reset our state to SEEKING
    1637                 :   // if we need to seek again.
    1638                 : 
    1639               0 :   nsCOMPtr<nsIRunnable> stopEvent;
    1640               0 :   bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
    1641               0 :   if (GetMediaTime() == mEndTime && !isLiveStream) {
    1642                 :     // Seeked to end of media, move to COMPLETED state. Note we don't do
    1643                 :     // this if we're playing a live stream, since the end of media will advance
    1644                 :     // once we download more data!
    1645               0 :     LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to COMPLETED",
    1646                 :                         mDecoder.get(), seekTime));
    1647               0 :     stopEvent = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStoppedAtEnd);
    1648               0 :     mState = DECODER_STATE_COMPLETED;
    1649                 :   } else {
    1650               0 :     LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to DECODING",
    1651                 :                         mDecoder.get(), seekTime));
    1652               0 :     stopEvent = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::SeekingStopped);
    1653               0 :     StartDecoding();
    1654                 :   }
    1655                 :   {
    1656               0 :     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    1657               0 :     NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
    1658                 :   }
    1659                 : 
    1660                 :   // Reset quick buffering status. This ensures that if we began the
    1661                 :   // seek while quick-buffering, we won't bypass quick buffering mode
    1662                 :   // if we need to buffer after the seek.
    1663               0 :   mQuickBuffering = false;
    1664                 : 
    1665               0 :   ScheduleStateMachine();
    1666                 : }
    1667                 : 
    1668                 : // Runnable to dispose of the decoder and state machine on the main thread.
    1669               0 : class nsDecoderDisposeEvent : public nsRunnable {
    1670                 : public:
    1671               0 :   nsDecoderDisposeEvent(already_AddRefed<nsBuiltinDecoder> aDecoder,
    1672                 :                         already_AddRefed<nsBuiltinDecoderStateMachine> aStateMachine)
    1673               0 :     : mDecoder(aDecoder), mStateMachine(aStateMachine) {}
    1674               0 :   NS_IMETHOD Run() {
    1675               0 :     NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
    1676               0 :     mStateMachine->ReleaseDecoder();
    1677               0 :     mDecoder->ReleaseStateMachine();
    1678               0 :     mStateMachine = nsnull;
    1679               0 :     mDecoder = nsnull;
    1680               0 :     return NS_OK;
    1681                 :   }
    1682                 : private:
    1683                 :   nsRefPtr<nsBuiltinDecoder> mDecoder;
    1684                 :   nsCOMPtr<nsBuiltinDecoderStateMachine> mStateMachine;
    1685                 : };
    1686                 : 
    1687                 : // Runnable which dispatches an event to the main thread to dispose of the
    1688                 : // decoder and state machine. This runs on the state machine thread after
    1689                 : // the state machine has shutdown, and all events for that state machine have
    1690                 : // finished running.
    1691               0 : class nsDispatchDisposeEvent : public nsRunnable {
    1692                 : public:
    1693               0 :   nsDispatchDisposeEvent(nsBuiltinDecoder* aDecoder,
    1694                 :                          nsBuiltinDecoderStateMachine* aStateMachine)
    1695               0 :     : mDecoder(aDecoder), mStateMachine(aStateMachine) {}
    1696               0 :   NS_IMETHOD Run() {
    1697               0 :     NS_DispatchToMainThread(new nsDecoderDisposeEvent(mDecoder.forget(),
    1698               0 :                                                       mStateMachine.forget()));
    1699               0 :     return NS_OK;
    1700                 :   }
    1701                 : private:
    1702                 :   nsRefPtr<nsBuiltinDecoder> mDecoder;
    1703                 :   nsCOMPtr<nsBuiltinDecoderStateMachine> mStateMachine;
    1704                 : };
    1705                 : 
    1706               0 : nsresult nsBuiltinDecoderStateMachine::RunStateMachine()
    1707                 : {
    1708               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1709                 : 
    1710               0 :   MediaResource* resource = mDecoder->GetResource();
    1711               0 :   NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
    1712                 : 
    1713               0 :   switch (mState) {
    1714                 :     case DECODER_STATE_SHUTDOWN: {
    1715               0 :       if (IsPlaying()) {
    1716               0 :         StopPlayback();
    1717                 :       }
    1718               0 :       StopAudioThread();
    1719               0 :       StopDecodeThread();
    1720               0 :       NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
    1721                 :                    "How did we escape from the shutdown state?");
    1722                 :       // We must daisy-chain these events to destroy the decoder. We must
    1723                 :       // destroy the decoder on the main thread, but we can't destroy the
    1724                 :       // decoder while this thread holds the decoder monitor. We can't
    1725                 :       // dispatch an event to the main thread to destroy the decoder from
    1726                 :       // here, as the event may run before the dispatch returns, and we
    1727                 :       // hold the decoder monitor here. We also want to guarantee that the
    1728                 :       // state machine is destroyed on the main thread, and so the
    1729                 :       // event runner running this function (which holds a reference to the
    1730                 :       // state machine) needs to finish and be released in order to allow
    1731                 :       // that. So we dispatch an event to run after this event runner has
    1732                 :       // finished and released its monitor/references. That event then will
    1733                 :       // dispatch an event to the main thread to release the decoder and
    1734                 :       // state machine.
    1735               0 :       NS_DispatchToCurrentThread(new nsDispatchDisposeEvent(mDecoder, this));
    1736               0 :       return NS_OK;
    1737                 :     }
    1738                 : 
    1739                 :     case DECODER_STATE_DECODING_METADATA: {
    1740                 :       // Ensure we have a decode thread to decode metadata.
    1741               0 :       return ScheduleDecodeThread();
    1742                 :     }
    1743                 :   
    1744                 :     case DECODER_STATE_DECODING: {
    1745               0 :       if (mDecoder->GetState() != nsBuiltinDecoder::PLAY_STATE_PLAYING &&
    1746               0 :           IsPlaying())
    1747                 :       {
    1748                 :         // We're playing, but the element/decoder is in paused state. Stop
    1749                 :         // playing! Note we do this before StopDecodeThread() below because
    1750                 :         // that blocks this state machine's execution, and can cause a
    1751                 :         // perceptible delay between the pause command, and playback actually
    1752                 :         // pausing.
    1753               0 :         StopPlayback();
    1754                 :       }
    1755                 : 
    1756               0 :       if (IsPausedAndDecoderWaiting()) {
    1757                 :         // The decode buffers are full, and playback is paused. Shutdown the
    1758                 :         // decode thread.
    1759               0 :         StopDecodeThread();
    1760               0 :         return NS_OK;
    1761                 :       }
    1762                 : 
    1763                 :       // We're playing and/or our decode buffers aren't full. Ensure we have
    1764                 :       // an active decode thread.
    1765               0 :       if (NS_FAILED(ScheduleDecodeThread())) {
    1766               0 :         NS_WARNING("Failed to start media decode thread!");
    1767               0 :         return NS_ERROR_FAILURE;
    1768                 :       }
    1769                 : 
    1770               0 :       AdvanceFrame();
    1771               0 :       NS_ASSERTION(mDecoder->GetState() != nsBuiltinDecoder::PLAY_STATE_PLAYING ||
    1772                 :                    IsStateMachineScheduled(), "Must have timer scheduled");
    1773               0 :       return NS_OK;
    1774                 :     }
    1775                 : 
    1776                 :     case DECODER_STATE_BUFFERING: {
    1777               0 :       if (IsPausedAndDecoderWaiting()) {
    1778                 :         // The decode buffers are full, and playback is paused. Shutdown the
    1779                 :         // decode thread.
    1780               0 :         StopDecodeThread();
    1781               0 :         return NS_OK;
    1782                 :       }
    1783                 : 
    1784               0 :       TimeStamp now = TimeStamp::Now();
    1785               0 :       NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
    1786                 : 
    1787                 :       // We will remain in the buffering state if we've not decoded enough
    1788                 :       // data to begin playback, or if we've not downloaded a reasonable
    1789                 :       // amount of data inside our buffering time.
    1790               0 :       TimeDuration elapsed = now - mBufferingStart;
    1791               0 :       bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
    1792               0 :       if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
    1793               0 :             elapsed < TimeDuration::FromSeconds(mBufferingWait) &&
    1794               0 :             (mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
    1795               0 :                             : (GetUndecodedData() < mBufferingWait * USECS_PER_S / 1000)) &&
    1796               0 :             !resource->IsDataCachedToEndOfResource(mDecoder->mDecoderPosition) &&
    1797               0 :             !resource->IsSuspended())
    1798                 :       {
    1799               0 :         LOG(PR_LOG_DEBUG,
    1800                 :             ("%p Buffering: %.3lfs/%ds, timeout in %.3lfs %s",
    1801                 :               mDecoder.get(),
    1802                 :               GetUndecodedData() / static_cast<double>(USECS_PER_S),
    1803                 :               mBufferingWait,
    1804                 :               mBufferingWait - elapsed.ToSeconds(),
    1805                 :               (mQuickBuffering ? "(quick exit)" : "")));
    1806               0 :         ScheduleStateMachine(USECS_PER_S);
    1807               0 :         return NS_OK;
    1808                 :       } else {
    1809               0 :         LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder.get()));
    1810               0 :         LOG(PR_LOG_DEBUG, ("%p Buffered for %.3lfs",
    1811                 :                             mDecoder.get(),
    1812                 :                             (now - mBufferingStart).ToSeconds()));
    1813               0 :         StartDecoding();
    1814                 :       }
    1815                 : 
    1816                 :       // Notify to allow blocked decoder thread to continue
    1817               0 :       mDecoder->GetReentrantMonitor().NotifyAll();
    1818               0 :       UpdateReadyState();
    1819               0 :       if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
    1820               0 :           !IsPlaying())
    1821                 :       {
    1822               0 :         StartPlayback();
    1823                 :       }
    1824               0 :       NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
    1825               0 :       return NS_OK;
    1826                 :     }
    1827                 : 
    1828                 :     case DECODER_STATE_SEEKING: {
    1829                 :       // Ensure we have a decode thread to perform the seek.
    1830               0 :      return ScheduleDecodeThread();
    1831                 :     }
    1832                 : 
    1833                 :     case DECODER_STATE_COMPLETED: {
    1834               0 :       StopDecodeThread();
    1835                 : 
    1836               0 :       if (mState != DECODER_STATE_COMPLETED) {
    1837                 :         // While we're waiting for the decode thread to shutdown, we can
    1838                 :         // change state, for example to seeking or shutdown state.
    1839                 :         // Whatever changed our state should have scheduled another state
    1840                 :         // machine run.
    1841               0 :         NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
    1842               0 :         return NS_OK;
    1843                 :       }
    1844                 : 
    1845                 :       // Play the remaining media. We want to run AdvanceFrame() at least
    1846                 :       // once to ensure the current playback position is advanced to the
    1847                 :       // end of the media, and so that we update the readyState.
    1848               0 :       if (mState == DECODER_STATE_COMPLETED &&
    1849               0 :           (mReader->mVideoQueue.GetSize() > 0 ||
    1850               0 :           (HasAudio() && !mAudioCompleted)))
    1851                 :       {
    1852               0 :         AdvanceFrame();
    1853               0 :         NS_ASSERTION(mDecoder->GetState() != nsBuiltinDecoder::PLAY_STATE_PLAYING ||
    1854                 :                      IsStateMachineScheduled(),
    1855                 :                      "Must have timer scheduled");
    1856               0 :         return NS_OK;
    1857                 :       }
    1858                 : 
    1859                 :       // StopPlayback in order to reset the IsPlaying() state so audio
    1860                 :       // is restarted correctly.
    1861               0 :       StopPlayback();
    1862                 : 
    1863               0 :       if (mState != DECODER_STATE_COMPLETED) {
    1864                 :         // While we're presenting a frame we can change state. Whatever changed
    1865                 :         // our state should have scheduled another state machine run.
    1866               0 :         NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
    1867               0 :         return NS_OK;
    1868                 :       }
    1869                 :  
    1870               0 :       StopAudioThread();
    1871               0 :       if (mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING) {
    1872               0 :         PRInt64 videoTime = HasVideo() ? mVideoFrameEndTime : 0;
    1873               0 :         PRInt64 clockTime = NS_MAX(mEndTime, NS_MAX(videoTime, GetAudioClock()));
    1874               0 :         UpdatePlaybackPosition(clockTime);
    1875                 :         nsCOMPtr<nsIRunnable> event =
    1876               0 :           NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::PlaybackEnded);
    1877               0 :         NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
    1878                 :       }
    1879               0 :       return NS_OK;
    1880                 :     }
    1881                 :   }
    1882                 : 
    1883               0 :   return NS_OK;
    1884                 : }
    1885                 : 
    1886               0 : void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData,
    1887                 :                                                     TimeStamp aTarget)
    1888                 : {
    1889               0 :   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
    1890                 :                "Should be on state machine or decode thread.");
    1891               0 :   mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
    1892                 : 
    1893               0 :   if (aData->mDuplicate) {
    1894               0 :     return;
    1895                 :   }
    1896                 : 
    1897               0 :   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
    1898               0 :   if (container) {
    1899               0 :     container->SetCurrentFrame(aData->mDisplay, aData->mImage, aTarget);
    1900                 :   }
    1901                 : }
    1902                 : 
    1903                 : PRInt64
    1904               0 : nsBuiltinDecoderStateMachine::GetAudioClock()
    1905                 : {
    1906               0 :   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
    1907               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1908               0 :   if (!HasAudio())
    1909               0 :     return -1;
    1910                 :   // We must hold the decoder monitor while using the audio stream off the
    1911                 :   // audio thread to ensure that it doesn't get destroyed on the audio thread
    1912                 :   // while we're using it.
    1913               0 :   if (!mAudioStream) {
    1914                 :     // Audio thread hasn't played any data yet.
    1915               0 :     return mAudioStartTime;
    1916                 :   }
    1917                 :   // Note that querying the playback position does not do a synchronous
    1918                 :   // dispatch to the main thread on Android, so it's safe to call with
    1919                 :   // the decoder monitor held here.
    1920               0 :   PRInt64 t = mAudioStream->GetPosition();
    1921               0 :   return (t == -1) ? -1 : t + mAudioStartTime;
    1922                 : }
    1923                 : 
    1924               0 : void nsBuiltinDecoderStateMachine::AdvanceFrame()
    1925                 : {
    1926               0 :   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
    1927               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    1928               0 :   NS_ASSERTION(!HasAudio() || mAudioStartTime != -1,
    1929                 :                "Should know audio start time if we have audio.");
    1930                 : 
    1931               0 :   if (mDecoder->GetState() != nsBuiltinDecoder::PLAY_STATE_PLAYING) {
    1932               0 :     return;
    1933                 :   }
    1934                 : 
    1935                 :   // Determine the clock time. If we've got audio, and we've not reached
    1936                 :   // the end of the audio, use the audio clock. However if we've finished
    1937                 :   // audio, or don't have audio, use the system clock.
    1938               0 :   PRInt64 clock_time = -1;
    1939               0 :   if (!IsPlaying()) {
    1940               0 :     clock_time = mPlayDuration + mStartTime;
    1941                 :   } else {
    1942               0 :     PRInt64 audio_time = GetAudioClock();
    1943               0 :     if (HasAudio() && !mAudioCompleted && audio_time != -1) {
    1944               0 :       clock_time = audio_time;
    1945                 :       // Resync against the audio clock, while we're trusting the
    1946                 :       // audio clock. This ensures no "drift", particularly on Linux.
    1947               0 :       mPlayDuration = clock_time - mStartTime;
    1948               0 :       mPlayStartTime = TimeStamp::Now();
    1949                 :     } else {
    1950                 :       // Audio is disabled on this system. Sync to the system clock.
    1951               0 :       clock_time = DurationToUsecs(TimeStamp::Now() - mPlayStartTime) + mPlayDuration;
    1952                 :       // Ensure the clock can never go backwards.
    1953               0 :       NS_ASSERTION(mCurrentFrameTime <= clock_time, "Clock should go forwards");
    1954               0 :       clock_time = NS_MAX(mCurrentFrameTime, clock_time) + mStartTime;
    1955                 :     }
    1956                 :   }
    1957                 : 
    1958                 :   // Skip frames up to the frame at the playback position, and figure out
    1959                 :   // the time remaining until it's time to display the next frame.
    1960               0 :   PRInt64 remainingTime = AUDIO_DURATION_USECS;
    1961               0 :   NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
    1962               0 :   nsAutoPtr<VideoData> currentFrame;
    1963               0 :   if (mReader->mVideoQueue.GetSize() > 0) {
    1964               0 :     VideoData* frame = mReader->mVideoQueue.PeekFront();
    1965               0 :     while (mRealTime || clock_time >= frame->mTime) {
    1966               0 :       mVideoFrameEndTime = frame->mEndTime;
    1967               0 :       currentFrame = frame;
    1968               0 :       mReader->mVideoQueue.PopFront();
    1969                 :       // Notify the decode thread that the video queue's buffers may have
    1970                 :       // free'd up space for more frames.
    1971               0 :       mDecoder->GetReentrantMonitor().NotifyAll();
    1972               0 :       mDecoder->UpdatePlaybackOffset(frame->mOffset);
    1973               0 :       if (mReader->mVideoQueue.GetSize() == 0)
    1974               0 :         break;
    1975               0 :       frame = mReader->mVideoQueue.PeekFront();
    1976                 :     }
    1977                 :     // Current frame has already been presented, wait until it's time to
    1978                 :     // present the next frame.
    1979               0 :     if (frame && !currentFrame) {
    1980               0 :       PRInt64 now = IsPlaying()
    1981               0 :         ? (DurationToUsecs(TimeStamp::Now() - mPlayStartTime) + mPlayDuration)
    1982               0 :         : mPlayDuration;
    1983               0 :       remainingTime = frame->mTime - mStartTime - now;
    1984                 :     }
    1985                 :   }
    1986                 : 
    1987                 :   // Check to see if we don't have enough data to play up to the next frame.
    1988                 :   // If we don't, switch to buffering mode.
    1989               0 :   MediaResource* resource = mDecoder->GetResource();
    1990               0 :   if (mState == DECODER_STATE_DECODING &&
    1991               0 :       mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
    1992               0 :       HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
    1993               0 :       !resource->IsDataCachedToEndOfResource(mDecoder->mDecoderPosition) &&
    1994               0 :       !resource->IsSuspended() &&
    1995               0 :       (JustExitedQuickBuffering() || HasLowUndecodedData()))
    1996                 :   {
    1997               0 :     if (currentFrame) {
    1998               0 :       mReader->mVideoQueue.PushFront(currentFrame.forget());
    1999                 :     }
    2000               0 :     StartBuffering();
    2001               0 :     ScheduleStateMachine();
    2002                 :     return;
    2003                 :   }
    2004                 : 
    2005                 :   // We've got enough data to keep playing until at least the next frame.
    2006                 :   // Start playing now if need be.
    2007               0 :   if (!IsPlaying() && ((mFragmentEndTime >= 0 && clock_time < mFragmentEndTime) || mFragmentEndTime < 0)) {
    2008               0 :     StartPlayback();
    2009                 :   }
    2010                 : 
    2011               0 :   if (currentFrame) {
    2012                 :     // Decode one frame and display it.
    2013               0 :     TimeStamp presTime = mPlayStartTime - UsecsToDuration(mPlayDuration) +
    2014               0 :                           UsecsToDuration(currentFrame->mTime - mStartTime);
    2015               0 :     NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time");
    2016                 :     {
    2017               0 :       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    2018                 :       // If we have video, we want to increment the clock in steps of the frame
    2019                 :       // duration.
    2020               0 :       RenderVideoFrame(currentFrame, presTime);
    2021                 :     }
    2022                 :     // If we're no longer playing after dropping and reacquiring the lock,
    2023                 :     // playback must've been stopped on the decode thread (by a seek, for
    2024                 :     // example).  In that case, the current frame is probably out of date.
    2025               0 :     if (!IsPlaying()) {
    2026               0 :       ScheduleStateMachine();
    2027                 :       return;
    2028                 :     }
    2029               0 :     mDecoder->GetFrameStatistics().NotifyPresentedFrame();
    2030               0 :     PRInt64 now = DurationToUsecs(TimeStamp::Now() - mPlayStartTime) + mPlayDuration;
    2031               0 :     remainingTime = currentFrame->mEndTime - mStartTime - now;
    2032               0 :     currentFrame = nsnull;
    2033                 :   }
    2034                 : 
    2035                 :   // Cap the current time to the larger of the audio and video end time.
    2036                 :   // This ensures that if we're running off the system clock, we don't
    2037                 :   // advance the clock to after the media end time.
    2038               0 :   if (mVideoFrameEndTime != -1 || mAudioEndTime != -1) {
    2039                 :     // These will be non -1 if we've displayed a video frame, or played an audio frame.
    2040               0 :     clock_time = NS_MIN(clock_time, NS_MAX(mVideoFrameEndTime, mAudioEndTime));
    2041               0 :     if (clock_time > GetMediaTime()) {
    2042                 :       // Only update the playback position if the clock time is greater
    2043                 :       // than the previous playback position. The audio clock can
    2044                 :       // sometimes report a time less than its previously reported in
    2045                 :       // some situations, and we need to gracefully handle that.
    2046               0 :       UpdatePlaybackPosition(clock_time);
    2047                 :     }
    2048                 :   }
    2049                 : 
    2050                 :   // If the number of audio/video frames queued has changed, either by
    2051                 :   // this function popping and playing a video frame, or by the audio
    2052                 :   // thread popping and playing an audio frame, we may need to update our
    2053                 :   // ready state. Post an update to do so.
    2054               0 :   UpdateReadyState();
    2055                 : 
    2056               0 :   ScheduleStateMachine(remainingTime);
    2057                 : }
    2058                 : 
    2059               0 : void nsBuiltinDecoderStateMachine::Wait(PRInt64 aUsecs) {
    2060               0 :   NS_ASSERTION(OnAudioThread(), "Only call on the audio thread");
    2061               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    2062               0 :   TimeStamp end = TimeStamp::Now() + UsecsToDuration(NS_MAX<PRInt64>(USECS_PER_MS, aUsecs));
    2063               0 :   TimeStamp now;
    2064               0 :   while ((now = TimeStamp::Now()) < end &&
    2065                 :          mState != DECODER_STATE_SHUTDOWN &&
    2066                 :          mState != DECODER_STATE_SEEKING &&
    2067               0 :          !mStopAudioThread &&
    2068               0 :          IsPlaying())
    2069                 :   {
    2070               0 :     PRInt64 ms = static_cast<PRInt64>(NS_round((end - now).ToSeconds() * 1000));
    2071               0 :     if (ms == 0 || ms > PR_UINT32_MAX) {
    2072               0 :       break;
    2073                 :     }
    2074               0 :     mDecoder->GetReentrantMonitor().Wait(PR_MillisecondsToInterval(static_cast<PRUint32>(ms)));
    2075                 :   }
    2076               0 : }
    2077                 : 
    2078               0 : VideoData* nsBuiltinDecoderStateMachine::FindStartTime()
    2079                 : {
    2080               0 :   NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
    2081               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    2082               0 :   PRInt64 startTime = 0;
    2083               0 :   mStartTime = 0;
    2084               0 :   VideoData* v = nsnull;
    2085                 :   {
    2086               0 :     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
    2087               0 :     v = mReader->FindStartTime(startTime);
    2088                 :   }
    2089               0 :   if (startTime != 0) {
    2090               0 :     mStartTime = startTime;
    2091               0 :     if (mGotDurationFromMetaData) {
    2092               0 :       NS_ASSERTION(mEndTime != -1,
    2093                 :                    "We should have mEndTime as supplied duration here");
    2094                 :       // We were specified a duration from a Content-Duration HTTP header.
    2095                 :       // Adjust mEndTime so that mEndTime-mStartTime matches the specified
    2096                 :       // duration.
    2097               0 :       mEndTime = mStartTime + mEndTime;
    2098                 :     }
    2099                 :   }
    2100                 :   // Set the audio start time to be start of media. If this lies before the
    2101                 :   // first actual audio frame we have, we'll inject silence during playback
    2102                 :   // to ensure the audio starts at the correct time.
    2103               0 :   mAudioStartTime = mStartTime;
    2104               0 :   LOG(PR_LOG_DEBUG, ("%p Media start time is %lld", mDecoder.get(), mStartTime));
    2105               0 :   return v;
    2106                 : }
    2107                 : 
    2108               0 : void nsBuiltinDecoderStateMachine::UpdateReadyState() {
    2109               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    2110                 : 
    2111               0 :   nsCOMPtr<nsIRunnable> event;
    2112               0 :   switch (GetNextFrameStatus()) {
    2113                 :     case nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE_BUFFERING:
    2114               0 :       event = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::NextFrameUnavailableBuffering);
    2115               0 :       break;
    2116                 :     case nsHTMLMediaElement::NEXT_FRAME_AVAILABLE:
    2117               0 :       event = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::NextFrameAvailable);
    2118               0 :       break;
    2119                 :     case nsHTMLMediaElement::NEXT_FRAME_UNAVAILABLE:
    2120               0 :       event = NS_NewRunnableMethod(mDecoder, &nsBuiltinDecoder::NextFrameUnavailable);
    2121               0 :       break;
    2122                 :     default:
    2123               0 :       PR_NOT_REACHED("unhandled frame state");
    2124                 :   }
    2125                 : 
    2126               0 :   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
    2127               0 : }
    2128                 : 
    2129               0 : bool nsBuiltinDecoderStateMachine::JustExitedQuickBuffering()
    2130                 : {
    2131               0 :   return !mDecodeStartTime.IsNull() &&
    2132                 :     mQuickBuffering &&
    2133               0 :     (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromSeconds(QUICK_BUFFER_THRESHOLD_USECS);
    2134                 : }
    2135                 : 
    2136               0 : void nsBuiltinDecoderStateMachine::StartBuffering()
    2137                 : {
    2138               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    2139                 : 
    2140               0 :   if (IsPlaying()) {
    2141               0 :     StopPlayback();
    2142                 :   }
    2143                 : 
    2144               0 :   TimeDuration decodeDuration = TimeStamp::Now() - mDecodeStartTime;
    2145                 :   // Go into quick buffering mode provided we've not just left buffering using
    2146                 :   // a "quick exit". This stops us flip-flopping between playing and buffering
    2147                 :   // when the download speed is similar to the decode speed.
    2148                 :   mQuickBuffering =
    2149               0 :     !JustExitedQuickBuffering() &&
    2150               0 :     decodeDuration < UsecsToDuration(QUICK_BUFFER_THRESHOLD_USECS);
    2151               0 :   mBufferingStart = TimeStamp::Now();
    2152                 : 
    2153                 :   // We need to tell the element that buffering has started.
    2154                 :   // We can't just directly send an asynchronous runnable that
    2155                 :   // eventually fires the "waiting" event. The problem is that
    2156                 :   // there might be pending main-thread events, such as "data
    2157                 :   // received" notifications, that mean we're not actually still
    2158                 :   // buffering by the time this runnable executes. So instead
    2159                 :   // we just trigger UpdateReadyStateForData; when it runs, it
    2160                 :   // will check the current state and decide whether to tell
    2161                 :   // the element we're buffering or not.
    2162               0 :   UpdateReadyState();
    2163               0 :   mState = DECODER_STATE_BUFFERING;
    2164               0 :   LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING to BUFFERING, decoded for %.3lfs",
    2165                 :                      mDecoder.get(), decodeDuration.ToSeconds()));
    2166               0 :   nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
    2167               0 :   LOG(PR_LOG_DEBUG, ("%p Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
    2168                 :     mDecoder.get(),
    2169                 :     stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
    2170                 :     stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"));
    2171               0 : }
    2172                 : 
    2173               0 : nsresult nsBuiltinDecoderStateMachine::GetBuffered(nsTimeRanges* aBuffered) {
    2174               0 :   MediaResource* resource = mDecoder->GetResource();
    2175               0 :   NS_ENSURE_TRUE(resource, NS_ERROR_FAILURE);
    2176               0 :   resource->Pin();
    2177               0 :   nsresult res = mReader->GetBuffered(aBuffered, mStartTime);
    2178               0 :   resource->Unpin();
    2179               0 :   return res;
    2180                 : }
    2181                 : 
    2182               0 : bool nsBuiltinDecoderStateMachine::IsPausedAndDecoderWaiting() {
    2183               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    2184               0 :   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
    2185                 : 
    2186                 :   return
    2187                 :     mDecodeThreadWaiting &&
    2188               0 :     mDecoder->GetState() != nsBuiltinDecoder::PLAY_STATE_PLAYING &&
    2189               0 :     (mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING);
    2190                 : }
    2191                 : 
    2192               0 : nsresult nsBuiltinDecoderStateMachine::Run()
    2193                 : {
    2194               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    2195               0 :   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
    2196                 : 
    2197               0 :   return CallRunStateMachine();
    2198                 : }
    2199                 : 
    2200               0 : nsresult nsBuiltinDecoderStateMachine::CallRunStateMachine()
    2201                 : {
    2202               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    2203               0 :   NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
    2204                 :   // This will be set to true by ScheduleStateMachine() if it's called
    2205                 :   // while we're in RunStateMachine().
    2206               0 :   mRunAgain = false;
    2207                 : 
    2208                 :   // Set to true whenever we dispatch an event to run this state machine.
    2209                 :   // This flag prevents us from dispatching
    2210               0 :   mDispatchedRunEvent = false;
    2211                 : 
    2212               0 :   mTimeout = TimeStamp();
    2213                 : 
    2214               0 :   mIsRunning = true;
    2215               0 :   nsresult res = RunStateMachine();
    2216               0 :   mIsRunning = false;
    2217                 : 
    2218               0 :   if (mRunAgain && !mDispatchedRunEvent) {
    2219               0 :     mDispatchedRunEvent = true;
    2220               0 :     return NS_DispatchToCurrentThread(this);
    2221                 :   }
    2222                 : 
    2223               0 :   return res;
    2224                 : }
    2225                 : 
    2226               0 : static void TimeoutExpired(nsITimer *aTimer, void *aClosure) {
    2227                 :   nsBuiltinDecoderStateMachine *machine =
    2228               0 :     static_cast<nsBuiltinDecoderStateMachine*>(aClosure);
    2229               0 :   NS_ASSERTION(machine, "Must have been passed state machine");
    2230               0 :   machine->TimeoutExpired();
    2231               0 : }
    2232                 : 
    2233               0 : void nsBuiltinDecoderStateMachine::TimeoutExpired()
    2234                 : {
    2235               0 :   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
    2236               0 :   NS_ASSERTION(OnStateMachineThread(), "Must be on state machine thread");
    2237               0 :   if (mIsRunning) {
    2238               0 :     mRunAgain = true;
    2239               0 :   } else if (!mDispatchedRunEvent) {
    2240                 :     // We don't have an event dispatched to run the state machine, so we
    2241                 :     // can just run it from here.
    2242               0 :     CallRunStateMachine();
    2243                 :   }
    2244                 :   // Otherwise, an event has already been dispatched to run the state machine
    2245                 :   // as soon as possible. Nothing else needed to do, the state machine is
    2246                 :   // going to run anyway.
    2247               0 : }
    2248                 : 
    2249               0 : nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine() {
    2250               0 :   return ScheduleStateMachine(0);
    2251                 : }
    2252                 : 
    2253               0 : nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) {
    2254               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    2255               0 :   NS_ABORT_IF_FALSE(GetStateMachineThread(),
    2256                 :     "Must have a state machine thread to schedule");
    2257                 : 
    2258               0 :   if (mState == DECODER_STATE_SHUTDOWN) {
    2259               0 :     return NS_ERROR_FAILURE;
    2260                 :   }
    2261               0 :   aUsecs = PR_MAX(aUsecs, 0);
    2262                 : 
    2263               0 :   TimeStamp timeout = TimeStamp::Now() + UsecsToDuration(aUsecs);
    2264               0 :   if (!mTimeout.IsNull()) {
    2265               0 :     if (timeout >= mTimeout) {
    2266                 :       // We've already scheduled a timer set to expire at or before this time,
    2267                 :       // or have an event dispatched to run the state machine.
    2268               0 :       return NS_OK;
    2269                 :     }
    2270               0 :     if (mTimer) {
    2271                 :       // We've been asked to schedule a timer to run before an existing timer.
    2272                 :       // Cancel the existing timer.
    2273               0 :       mTimer->Cancel();
    2274                 :     }
    2275                 :   }
    2276                 : 
    2277               0 :   PRUint32 ms = static_cast<PRUint32>((aUsecs / USECS_PER_MS) & 0xFFFFFFFF);
    2278               0 :   if (mRealTime && ms > 40)
    2279               0 :     ms = 40;
    2280               0 :   if (ms == 0) {
    2281               0 :     if (mIsRunning) {
    2282                 :       // We're currently running this state machine on the state machine
    2283                 :       // thread. Signal it to run again once it finishes its current cycle.
    2284               0 :       mRunAgain = true;
    2285               0 :       return NS_OK;
    2286               0 :     } else if (!mDispatchedRunEvent) {
    2287                 :       // We're not currently running this state machine on the state machine
    2288                 :       // thread. Dispatch an event to run one cycle of the state machine.
    2289               0 :       mDispatchedRunEvent = true;
    2290               0 :       return GetStateMachineThread()->Dispatch(this, NS_DISPATCH_NORMAL);
    2291                 :     }
    2292                 :     // We're not currently running this state machine on the state machine
    2293                 :     // thread, but something has already dispatched an event to run it again,
    2294                 :     // so just exit; it's going to run real soon.
    2295               0 :     return NS_OK;
    2296                 :   }
    2297                 : 
    2298               0 :   mTimeout = timeout;
    2299                 : 
    2300                 :   nsresult res;
    2301               0 :   if (!mTimer) {
    2302               0 :     mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
    2303               0 :     if (NS_FAILED(res)) return res;
    2304               0 :     mTimer->SetTarget(GetStateMachineThread());
    2305                 :   }
    2306                 : 
    2307               0 :   res = mTimer->InitWithFuncCallback(::TimeoutExpired,
    2308                 :                                      this,
    2309                 :                                      ms,
    2310               0 :                                      nsITimer::TYPE_ONE_SHOT);
    2311               0 :   return res;
    2312                 : }
    2313                 : 
    2314               0 : bool nsBuiltinDecoderStateMachine::OnStateMachineThread() const
    2315                 : {
    2316               0 :     return IsCurrentThread(GetStateMachineThread());
    2317                 : }
    2318                 :  
    2319               0 : nsIThread* nsBuiltinDecoderStateMachine::GetStateMachineThread()
    2320                 : {
    2321               0 :   return StateMachineTracker::Instance().GetGlobalStateMachineThread();
    2322                 : }
    2323                 : 
    2324               0 : void nsBuiltinDecoderStateMachine::NotifyAudioAvailableListener()
    2325                 : {
    2326               0 :   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
    2327               0 :   mEventManager.NotifyAudioAvailableListener();
    2328               0 : }

Generated by: LCOV version 1.7