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 : }
|