Bug 828901 - Get the seek time as mBasePosition instead of the stream position in...
[gecko.git] / content / media / MediaDecoderStateMachine.cpp
blob1f477979e4ec02cfa4a2b19500f94f5111941444
1 /* vim:set ts=2 sw=2 sts=2 et cindent: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/DebugOnly.h"
7 #include "mozilla/StandardInteger.h"
8 #include "mozilla/Util.h"
10 #include "MediaDecoderStateMachine.h"
11 #include <limits>
12 #include "AudioStream.h"
13 #include "nsTArray.h"
14 #include "MediaDecoder.h"
15 #include "MediaDecoderReader.h"
16 #include "mozilla/mozalloc.h"
17 #include "VideoUtils.h"
18 #include "nsTimeRanges.h"
19 #include "nsDeque.h"
20 #include "AudioSegment.h"
21 #include "VideoSegment.h"
22 #include "ImageContainer.h"
24 #include "prenv.h"
25 #include "mozilla/Preferences.h"
26 #include <algorithm>
28 namespace mozilla {
30 using namespace mozilla::layers;
31 using namespace mozilla::dom;
33 #ifdef PR_LOGGING
34 extern PRLogModuleInfo* gMediaDecoderLog;
35 #define LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
36 #else
37 #define LOG(type, msg)
38 #endif
40 // Wait this number of seconds when buffering, then leave and play
41 // as best as we can if the required amount of data hasn't been
42 // retrieved.
43 static const uint32_t BUFFERING_WAIT_S = 30;
45 // If audio queue has less than this many usecs of decoded audio, we won't risk
46 // trying to decode the video, we'll skip decoding video up to the next
47 // keyframe. We may increase this value for an individual decoder if we
48 // encounter video frames which take a long time to decode.
49 static const uint32_t LOW_AUDIO_USECS = 300000;
51 // If more than this many usecs of decoded audio is queued, we'll hold off
52 // decoding more audio. If we increase the low audio threshold (see
53 // LOW_AUDIO_USECS above) we'll also increase this value to ensure it's not
54 // less than the low audio threshold.
55 const int64_t AMPLE_AUDIO_USECS = 1000000;
57 // Maximum number of bytes we'll allocate and write at once to the audio
58 // hardware when the audio stream contains missing frames and we're
59 // writing silence in order to fill the gap. We limit our silence-writes
60 // to 32KB in order to avoid allocating an impossibly large chunk of
61 // memory if we encounter a large chunk of silence.
62 const uint32_t SILENCE_BYTES_CHUNK = 32 * 1024;
64 // If we have fewer than LOW_VIDEO_FRAMES decoded frames, and
65 // we're not "pumping video", we'll skip the video up to the next keyframe
66 // which is at or after the current playback position.
67 static const uint32_t LOW_VIDEO_FRAMES = 1;
69 // Arbitrary "frame duration" when playing only audio.
70 static const int AUDIO_DURATION_USECS = 40000;
72 // If we increase our "low audio threshold" (see LOW_AUDIO_USECS above), we
73 // use this as a factor in all our calculations. Increasing this will cause
74 // us to be more likely to increase our low audio threshold, and to
75 // increase it by more.
76 static const int THRESHOLD_FACTOR = 2;
78 // If we have less than this much undecoded data available, we'll consider
79 // ourselves to be running low on undecoded data. We determine how much
80 // undecoded data we have remaining using the reader's GetBuffered()
81 // implementation.
82 static const int64_t LOW_DATA_THRESHOLD_USECS = 5000000;
84 // LOW_DATA_THRESHOLD_USECS needs to be greater than AMPLE_AUDIO_USECS, otherwise
85 // the skip-to-keyframe logic can activate when we're running low on data.
86 PR_STATIC_ASSERT(LOW_DATA_THRESHOLD_USECS > AMPLE_AUDIO_USECS);
88 // Amount of excess usecs of data to add in to the "should we buffer" calculation.
89 static const uint32_t EXHAUSTED_DATA_MARGIN_USECS = 60000;
91 // If we enter buffering within QUICK_BUFFER_THRESHOLD_USECS seconds of starting
92 // decoding, we'll enter "quick buffering" mode, which exits a lot sooner than
93 // normal buffering mode. This exists so that if the decode-ahead exhausts the
94 // downloaded data while decode/playback is just starting up (for example
95 // after a seek while the media is still playing, or when playing a media
96 // as soon as it's load started), we won't necessarily stop for 30s and wait
97 // for buffering. We may actually be able to playback in this case, so exit
98 // buffering early and try to play. If it turns out we can't play, we'll fall
99 // back to buffering normally.
100 static const uint32_t QUICK_BUFFER_THRESHOLD_USECS = 2000000;
102 // If we're quick buffering, we'll remain in buffering mode while we have less than
103 // QUICK_BUFFERING_LOW_DATA_USECS of decoded data available.
104 static const uint32_t QUICK_BUFFERING_LOW_DATA_USECS = 1000000;
106 // If QUICK_BUFFERING_LOW_DATA_USECS is > AMPLE_AUDIO_USECS, we won't exit
107 // quick buffering in a timely fashion, as the decode pauses when it
108 // reaches AMPLE_AUDIO_USECS decoded data, and thus we'll never reach
109 // QUICK_BUFFERING_LOW_DATA_USECS.
110 PR_STATIC_ASSERT(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS);
112 // This value has been chosen empirically.
113 static const uint32_t AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS = 200000;
115 static TimeDuration UsecsToDuration(int64_t aUsecs) {
116 return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
119 static int64_t DurationToUsecs(TimeDuration aDuration) {
120 return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S);
123 // Owns the global state machine thread and counts of
124 // state machine and decoder threads. There should
125 // only be one instance of this class.
126 class StateMachineTracker
128 private:
129 StateMachineTracker() :
130 mMonitor("media.statemachinetracker"),
131 mStateMachineCount(0),
132 mDecodeThreadCount(0),
133 mStateMachineThread(nullptr)
135 MOZ_COUNT_CTOR(StateMachineTracker);
136 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
139 ~StateMachineTracker()
141 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
143 MOZ_COUNT_DTOR(StateMachineTracker);
146 public:
147 // Access singleton instance. This is initially called on the main
148 // thread in the MediaDecoderStateMachine constructor resulting
149 // in the global object being created lazily. Non-main thread
150 // access always occurs after this and uses the monitor to
151 // safely access the decode thread counts.
152 static StateMachineTracker& Instance();
154 // Instantiate the global state machine thread if required.
155 // Call on main thread only.
156 void EnsureGlobalStateMachine();
158 // Destroy global state machine thread if required.
159 // Call on main thread only.
160 void CleanupGlobalStateMachine();
162 // Return the global state machine thread. Call from any thread.
163 nsIThread* GetGlobalStateMachineThread()
165 ReentrantMonitorAutoEnter mon(mMonitor);
166 NS_ASSERTION(mStateMachineThread, "Should have non-null state machine thread!");
167 return mStateMachineThread;
170 // Requests that a decode thread be created for aStateMachine. The thread
171 // may be created immediately, or after some delay, once a thread becomes
172 // available. The request can be cancelled using CancelCreateDecodeThread().
173 // It's the callers responsibility to not call this more than once for any
174 // given state machine.
175 nsresult RequestCreateDecodeThread(MediaDecoderStateMachine* aStateMachine);
177 // Cancels a request made by RequestCreateDecodeThread to create a decode
178 // thread for aStateMachine.
179 nsresult CancelCreateDecodeThread(MediaDecoderStateMachine* aStateMachine);
181 // Maximum number of active decode threads allowed. When more
182 // than this number are active the thread creation will fail.
183 static const uint32_t MAX_DECODE_THREADS = 25;
185 // Returns the number of active decode threads.
186 // Call on any thread. Holds the internal monitor so don't
187 // call with any other monitor held to avoid deadlock.
188 uint32_t GetDecodeThreadCount();
190 // Keep track of the fact that a decode thread was destroyed.
191 // Call on any thread. Holds the internal monitor so don't
192 // call with any other monitor held to avoid deadlock.
193 void NoteDecodeThreadDestroyed();
195 #ifdef DEBUG
196 // Returns true if aStateMachine has a pending request for a
197 // decode thread.
198 bool IsQueued(MediaDecoderStateMachine* aStateMachine);
199 #endif
201 private:
202 // Holds global instance of StateMachineTracker.
203 // Writable on main thread only.
204 static StateMachineTracker* sInstance;
206 // Reentrant monitor that must be obtained to access
207 // the decode thread count member and methods.
208 ReentrantMonitor mMonitor;
210 // Number of instances of MediaDecoderStateMachine
211 // that are currently instantiated. Access on the
212 // main thread only.
213 uint32_t mStateMachineCount;
215 // Number of instances of decoder threads that are
216 // currently instantiated. Access only with the
217 // mMonitor lock held. Can be used from any thread.
218 uint32_t mDecodeThreadCount;
220 // Global state machine thread. Write on the main thread
221 // only, read from the decoder threads. Synchronized via
222 // the mMonitor.
223 nsIThread* mStateMachineThread;
225 // Queue of state machines waiting for decode threads. Entries at the front
226 // get their threads first.
227 nsDeque mPending;
230 StateMachineTracker* StateMachineTracker::sInstance = nullptr;
232 StateMachineTracker& StateMachineTracker::Instance()
234 if (!sInstance) {
235 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
236 sInstance = new StateMachineTracker();
238 return *sInstance;
241 void StateMachineTracker::EnsureGlobalStateMachine()
243 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
244 ReentrantMonitorAutoEnter mon(mMonitor);
245 if (mStateMachineCount == 0) {
246 NS_ASSERTION(!mStateMachineThread, "Should have null state machine thread!");
247 DebugOnly<nsresult> rv = NS_NewNamedThread("Media State", &mStateMachineThread, nullptr);
248 NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Can't create media state machine thread");
250 mStateMachineCount++;
253 #ifdef DEBUG
254 bool StateMachineTracker::IsQueued(MediaDecoderStateMachine* aStateMachine)
256 ReentrantMonitorAutoEnter mon(mMonitor);
257 int32_t size = mPending.GetSize();
258 for (int i = 0; i < size; ++i) {
259 MediaDecoderStateMachine* m =
260 static_cast<MediaDecoderStateMachine*>(mPending.ObjectAt(i));
261 if (m == aStateMachine) {
262 return true;
265 return false;
267 #endif
269 void StateMachineTracker::CleanupGlobalStateMachine()
271 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
272 NS_ABORT_IF_FALSE(mStateMachineCount > 0,
273 "State machine ref count must be > 0");
274 mStateMachineCount--;
275 if (mStateMachineCount == 0) {
276 LOG(PR_LOG_DEBUG, ("Destroying media state machine thread"));
277 NS_ASSERTION(mPending.GetSize() == 0, "Shouldn't all requests be handled by now?");
279 ReentrantMonitorAutoEnter mon(mMonitor);
280 nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(mStateMachineThread);
281 NS_RELEASE(mStateMachineThread);
282 mStateMachineThread = nullptr;
283 NS_DispatchToMainThread(event);
285 NS_ASSERTION(mDecodeThreadCount == 0, "Decode thread count must be zero.");
286 sInstance = nullptr;
288 delete this;
292 void StateMachineTracker::NoteDecodeThreadDestroyed()
294 ReentrantMonitorAutoEnter mon(mMonitor);
295 --mDecodeThreadCount;
296 while (mDecodeThreadCount < MAX_DECODE_THREADS && mPending.GetSize() > 0) {
297 MediaDecoderStateMachine* m =
298 static_cast<MediaDecoderStateMachine*>(mPending.PopFront());
299 nsresult rv;
301 ReentrantMonitorAutoExit exitMon(mMonitor);
302 rv = m->StartDecodeThread();
304 if (NS_SUCCEEDED(rv)) {
305 ++mDecodeThreadCount;
310 uint32_t StateMachineTracker::GetDecodeThreadCount()
312 ReentrantMonitorAutoEnter mon(mMonitor);
313 return mDecodeThreadCount;
316 nsresult StateMachineTracker::CancelCreateDecodeThread(MediaDecoderStateMachine* aStateMachine) {
317 ReentrantMonitorAutoEnter mon(mMonitor);
318 int32_t size = mPending.GetSize();
319 for (int32_t i = 0; i < size; ++i) {
320 void* m = static_cast<MediaDecoderStateMachine*>(mPending.ObjectAt(i));
321 if (m == aStateMachine) {
322 mPending.RemoveObjectAt(i);
323 break;
326 NS_ASSERTION(!IsQueued(aStateMachine), "State machine should no longer have queued request.");
327 return NS_OK;
330 nsresult StateMachineTracker::RequestCreateDecodeThread(MediaDecoderStateMachine* aStateMachine)
332 NS_ENSURE_STATE(aStateMachine);
333 ReentrantMonitorAutoEnter mon(mMonitor);
334 if (mPending.GetSize() > 0 || mDecodeThreadCount + 1 >= MAX_DECODE_THREADS) {
335 // If there's already state machines in the queue, or we've exceeded the
336 // limit, append the state machine to the queue of state machines waiting
337 // for a decode thread. This ensures state machines already waiting get
338 // their threads first.
339 mPending.Push(aStateMachine);
340 return NS_OK;
342 nsresult rv;
344 ReentrantMonitorAutoExit exitMon(mMonitor);
345 rv = aStateMachine->StartDecodeThread();
347 if (NS_SUCCEEDED(rv)) {
348 ++mDecodeThreadCount;
350 NS_ASSERTION(mDecodeThreadCount <= MAX_DECODE_THREADS,
351 "Should keep to thread limit!");
352 return NS_OK;
355 MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
356 MediaDecoderReader* aReader,
357 bool aRealTime) :
358 mDecoder(aDecoder),
359 mState(DECODER_STATE_DECODING_METADATA),
360 mResetPlayStartTime(false),
361 mPlayDuration(0),
362 mStartTime(-1),
363 mEndTime(-1),
364 mSeekTime(0),
365 mFragmentEndTime(-1),
366 mReader(aReader),
367 mCurrentFrameTime(0),
368 mAudioStartTime(-1),
369 mAudioEndTime(-1),
370 mVideoFrameEndTime(-1),
371 mVolume(1.0),
372 mPlaybackRate(1.0),
373 mPreservesPitch(true),
374 mBasePosition(0),
375 mAudioCaptured(false),
376 mTransportSeekable(true),
377 mMediaSeekable(true),
378 mPositionChangeQueued(false),
379 mAudioCompleted(false),
380 mGotDurationFromMetaData(false),
381 mStopDecodeThread(true),
382 mDecodeThreadIdle(false),
383 mStopAudioThread(true),
384 mQuickBuffering(false),
385 mIsRunning(false),
386 mRunAgain(false),
387 mDispatchedRunEvent(false),
388 mDecodeThreadWaiting(false),
389 mRealTime(aRealTime),
390 mDidThrottleAudioDecoding(false),
391 mDidThrottleVideoDecoding(false),
392 mRequestedNewDecodeThread(false),
393 mEventManager(aDecoder),
394 mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED)
396 MOZ_COUNT_CTOR(MediaDecoderStateMachine);
397 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
399 StateMachineTracker::Instance().EnsureGlobalStateMachine();
401 // only enable realtime mode when "media.realtime_decoder.enabled" is true.
402 if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false)
403 mRealTime = false;
405 mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT_S;
406 mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS;
408 // If we've got more than mAmpleVideoFrames decoded video frames waiting in
409 // the video queue, we will not decode any more video frames until some have
410 // been consumed by the play state machine thread.
411 #if defined(MOZ_WIDGET_GONK) || defined(MOZ_MEDIA_PLUGINS)
412 // On B2G and Android this is decided by a similar value which varies for
413 // each OMX decoder |OMX_PARAM_PORTDEFINITIONTYPE::nBufferCountMin|. This
414 // number must be less than the OMX equivalent or gecko will think it is
415 // chronically starved of video frames. All decoders seen so far have a value
416 // of at least 4.
417 mAmpleVideoFrames = Preferences::GetUint("media.video-queue.default-size", 3);
418 #else
419 mAmpleVideoFrames = Preferences::GetUint("media.video-queue.default-size", 10);
420 #endif
421 if (mAmpleVideoFrames < 2) {
422 mAmpleVideoFrames = 2;
426 MediaDecoderStateMachine::~MediaDecoderStateMachine()
428 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
429 MOZ_COUNT_DTOR(MediaDecoderStateMachine);
430 NS_ASSERTION(!mPendingWakeDecoder.get(),
431 "WakeDecoder should have been revoked already");
432 NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this),
433 "Should not have a pending request for a new decode thread");
434 NS_ASSERTION(!mRequestedNewDecodeThread,
435 "Should not have (or flagged) a pending request for a new decode thread");
436 if (mTimer)
437 mTimer->Cancel();
438 mTimer = nullptr;
439 mReader = nullptr;
441 StateMachineTracker::Instance().CleanupGlobalStateMachine();
444 bool MediaDecoderStateMachine::HasFutureAudio() const {
445 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
446 NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
447 // We've got audio ready to play if:
448 // 1. We've not completed playback of audio, and
449 // 2. we either have more than the threshold of decoded audio available, or
450 // we've completely decoded all audio (but not finished playing it yet
451 // as per 1).
452 return !mAudioCompleted &&
453 (AudioDecodedUsecs() > LOW_AUDIO_USECS * mPlaybackRate || mReader->AudioQueue().IsFinished());
456 bool MediaDecoderStateMachine::HaveNextFrameData() const {
457 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
458 return (!HasAudio() || HasFutureAudio()) &&
459 (!HasVideo() || mReader->VideoQueue().GetSize() > 0);
462 int64_t MediaDecoderStateMachine::GetDecodedAudioDuration() {
463 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
464 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
465 int64_t audioDecoded = mReader->AudioQueue().Duration();
466 if (mAudioEndTime != -1) {
467 audioDecoded += mAudioEndTime - GetMediaTime();
469 return audioDecoded;
472 void MediaDecoderStateMachine::DecodeThreadRun()
474 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
475 mReader->OnDecodeThreadStart();
478 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
480 if (mState == DECODER_STATE_DECODING_METADATA &&
481 NS_FAILED(DecodeMetadata())) {
482 NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
483 "Should be in shutdown state if metadata loading fails.");
484 LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decode thread"));
487 while (mState != DECODER_STATE_SHUTDOWN &&
488 mState != DECODER_STATE_COMPLETED &&
489 !mStopDecodeThread)
491 if (mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) {
492 DecodeLoop();
493 } else if (mState == DECODER_STATE_SEEKING) {
494 DecodeSeek();
498 mDecodeThreadIdle = true;
499 LOG(PR_LOG_DEBUG, ("%p Decode thread finished", mDecoder.get()));
502 mReader->OnDecodeThreadFinish();
505 void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio,
506 DecodedStreamData* aStream,
507 AudioSegment* aOutput)
509 NS_ASSERTION(OnDecodeThread() ||
510 OnStateMachineThread(), "Should be on decode thread or state machine thread");
511 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
513 if (aAudio->mTime <= aStream->mLastAudioPacketTime) {
514 // ignore packet that we've already processed
515 return;
517 aStream->mLastAudioPacketTime = aAudio->mTime;
518 aStream->mLastAudioPacketEndTime = aAudio->GetEnd();
520 // This logic has to mimic AudioLoop closely to make sure we write
521 // the exact same silences
522 CheckedInt64 audioWrittenOffset = UsecsToFrames(mInfo.mAudioRate,
523 aStream->mInitialTime + mStartTime) + aStream->mAudioFramesWritten;
524 CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudioRate, aAudio->mTime);
525 if (!audioWrittenOffset.isValid() || !frameOffset.isValid())
526 return;
527 if (audioWrittenOffset.value() < frameOffset.value()) {
528 // Write silence to catch up
529 LOG(PR_LOG_DEBUG, ("%p Decoder writing %d frames of silence to MediaStream",
530 mDecoder.get(), int32_t(frameOffset.value() - audioWrittenOffset.value())));
531 AudioSegment silence;
532 silence.InsertNullDataAtStart(frameOffset.value() - audioWrittenOffset.value());
533 aStream->mAudioFramesWritten += silence.GetDuration();
534 aOutput->AppendFrom(&silence);
537 int64_t offset;
538 if (aStream->mAudioFramesWritten == 0) {
539 NS_ASSERTION(frameOffset.value() <= audioWrittenOffset.value(),
540 "Otherwise we'd have taken the write-silence path");
541 // We're starting in the middle of a packet. Split the packet.
542 offset = audioWrittenOffset.value() - frameOffset.value();
543 } else {
544 // Write the entire packet.
545 offset = 0;
548 if (offset >= aAudio->mFrames)
549 return;
551 aAudio->EnsureAudioBuffer();
552 nsRefPtr<SharedBuffer> buffer = aAudio->mAudioBuffer;
553 AudioDataValue* bufferData = static_cast<AudioDataValue*>(buffer->Data());
554 nsAutoTArray<const AudioDataValue*,2> channels;
555 for (uint32_t i = 0; i < aAudio->mChannels; ++i) {
556 channels.AppendElement(bufferData + i*aAudio->mFrames + offset);
558 aOutput->AppendFrames(buffer.forget(), channels, aAudio->mFrames);
559 LOG(PR_LOG_DEBUG, ("%p Decoder writing %d frames of data to MediaStream for AudioData at %lld",
560 mDecoder.get(), aAudio->mFrames - int32_t(offset), aAudio->mTime));
561 aStream->mAudioFramesWritten += aAudio->mFrames - int32_t(offset);
564 static void WriteVideoToMediaStream(layers::Image* aImage,
565 int64_t aDuration, const gfxIntSize& aIntrinsicSize,
566 VideoSegment* aOutput)
568 nsRefPtr<layers::Image> image = aImage;
569 aOutput->AppendFrame(image.forget(), aDuration, aIntrinsicSize);
572 static const TrackID TRACK_AUDIO = 1;
573 static const TrackID TRACK_VIDEO = 2;
574 static const TrackRate RATE_VIDEO = USECS_PER_S;
576 void MediaDecoderStateMachine::SendStreamData()
578 NS_ASSERTION(OnDecodeThread() ||
579 OnStateMachineThread(), "Should be on decode thread or state machine thread");
580 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
582 DecodedStreamData* stream = mDecoder->GetDecodedStream();
583 if (!stream)
584 return;
586 if (mState == DECODER_STATE_DECODING_METADATA)
587 return;
589 // If there's still an audio thread alive, then we can't send any stream
590 // data yet since both SendStreamData and the audio thread want to be in
591 // charge of popping the audio queue. We're waiting for the audio thread
592 // to die before sending anything to our stream.
593 if (mAudioThread)
594 return;
596 int64_t minLastAudioPacketTime = INT64_MAX;
597 SourceMediaStream* mediaStream = stream->mStream;
598 StreamTime endPosition = 0;
600 if (!stream->mStreamInitialized) {
601 if (mInfo.mHasAudio) {
602 AudioSegment* audio = new AudioSegment();
603 mediaStream->AddTrack(TRACK_AUDIO, mInfo.mAudioRate, 0, audio);
605 if (mInfo.mHasVideo) {
606 VideoSegment* video = new VideoSegment();
607 mediaStream->AddTrack(TRACK_VIDEO, RATE_VIDEO, 0, video);
609 stream->mStreamInitialized = true;
612 if (mInfo.mHasAudio) {
613 nsAutoTArray<AudioData*,10> audio;
614 // It's OK to hold references to the AudioData because while audio
615 // is captured, only the decoder thread pops from the queue (see below).
616 mReader->AudioQueue().GetElementsAfter(stream->mLastAudioPacketTime, &audio);
617 AudioSegment output;
618 for (uint32_t i = 0; i < audio.Length(); ++i) {
619 SendStreamAudio(audio[i], stream, &output);
621 if (output.GetDuration() > 0) {
622 mediaStream->AppendToTrack(TRACK_AUDIO, &output);
624 if (mReader->AudioQueue().IsFinished() && !stream->mHaveSentFinishAudio) {
625 mediaStream->EndTrack(TRACK_AUDIO);
626 stream->mHaveSentFinishAudio = true;
628 minLastAudioPacketTime = std::min(minLastAudioPacketTime, stream->mLastAudioPacketTime);
629 endPosition = std::max(endPosition,
630 TicksToTimeRoundDown(mInfo.mAudioRate, stream->mAudioFramesWritten));
633 if (mInfo.mHasVideo) {
634 nsAutoTArray<VideoData*,10> video;
635 // It's OK to hold references to the VideoData only the decoder thread
636 // pops from the queue.
637 mReader->VideoQueue().GetElementsAfter(stream->mNextVideoTime + mStartTime, &video);
638 VideoSegment output;
639 for (uint32_t i = 0; i < video.Length(); ++i) {
640 VideoData* v = video[i];
641 if (stream->mNextVideoTime + mStartTime < v->mTime) {
642 LOG(PR_LOG_DEBUG, ("%p Decoder writing last video to MediaStream %p for %lld ms",
643 mDecoder.get(), mediaStream,
644 v->mTime - (stream->mNextVideoTime + mStartTime)));
645 // Write last video frame to catch up. mLastVideoImage can be null here
646 // which is fine, it just means there's no video.
647 WriteVideoToMediaStream(stream->mLastVideoImage,
648 v->mTime - (stream->mNextVideoTime + mStartTime), stream->mLastVideoImageDisplaySize,
649 &output);
650 stream->mNextVideoTime = v->mTime - mStartTime;
652 if (stream->mNextVideoTime + mStartTime < v->mEndTime) {
653 LOG(PR_LOG_DEBUG, ("%p Decoder writing video frame %lld to MediaStream %p for %lld ms",
654 mDecoder.get(), v->mTime, mediaStream,
655 v->mEndTime - (stream->mNextVideoTime + mStartTime)));
656 WriteVideoToMediaStream(v->mImage,
657 v->mEndTime - (stream->mNextVideoTime + mStartTime), v->mDisplay,
658 &output);
659 stream->mNextVideoTime = v->mEndTime - mStartTime;
660 stream->mLastVideoImage = v->mImage;
661 stream->mLastVideoImageDisplaySize = v->mDisplay;
662 } else {
663 LOG(PR_LOG_DEBUG, ("%p Decoder skipping writing video frame %lld to MediaStream",
664 mDecoder.get(), v->mTime));
667 if (output.GetDuration() > 0) {
668 mediaStream->AppendToTrack(TRACK_VIDEO, &output);
670 if (mReader->VideoQueue().IsFinished() && !stream->mHaveSentFinishVideo) {
671 mediaStream->EndTrack(TRACK_VIDEO);
672 stream->mHaveSentFinishVideo = true;
674 endPosition = std::max(endPosition,
675 TicksToTimeRoundDown(RATE_VIDEO, stream->mNextVideoTime - stream->mInitialTime));
678 if (!stream->mHaveSentFinish) {
679 stream->mStream->AdvanceKnownTracksTime(endPosition);
682 bool finished =
683 (!mInfo.mHasAudio || mReader->AudioQueue().IsFinished()) &&
684 (!mInfo.mHasVideo || mReader->VideoQueue().IsFinished());
685 if (finished && !stream->mHaveSentFinish) {
686 stream->mHaveSentFinish = true;
687 stream->mStream->Finish();
690 if (mAudioCaptured) {
691 // Discard audio packets that are no longer needed.
692 int64_t audioPacketTimeToDiscard =
693 std::min(minLastAudioPacketTime, mStartTime + mCurrentFrameTime);
694 while (true) {
695 nsAutoPtr<AudioData> a(mReader->AudioQueue().PopFront());
696 if (!a)
697 break;
698 // Packet times are not 100% reliable so this may discard packets that
699 // actually contain data for mCurrentFrameTime. This means if someone might
700 // create a new output stream and we actually don't have the audio for the
701 // very start. That's OK, we'll play silence instead for a brief moment.
702 // That's OK. Seeking to this time would have a similar issue for such
703 // badly muxed resources.
704 if (a->GetEnd() >= audioPacketTimeToDiscard) {
705 mReader->AudioQueue().PushFront(a.forget());
706 break;
710 if (finished) {
711 mAudioCompleted = true;
712 UpdateReadyState();
717 MediaDecoderStateMachine::WakeDecoderRunnable*
718 MediaDecoderStateMachine::GetWakeDecoderRunnable()
720 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
722 if (!mPendingWakeDecoder.get()) {
723 mPendingWakeDecoder = new WakeDecoderRunnable(this);
725 return mPendingWakeDecoder.get();
728 bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
730 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
732 if (mReader->AudioQueue().GetSize() == 0 ||
733 GetDecodedAudioDuration() < aAmpleAudioUSecs) {
734 return false;
736 if (!mAudioCaptured) {
737 return true;
740 DecodedStreamData* stream = mDecoder->GetDecodedStream();
741 if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishAudio) {
742 if (!stream->mStream->HaveEnoughBuffered(TRACK_AUDIO)) {
743 return false;
745 stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_AUDIO,
746 GetStateMachineThread(), GetWakeDecoderRunnable());
749 return true;
752 bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
754 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
756 if (static_cast<uint32_t>(mReader->VideoQueue().GetSize()) < GetAmpleVideoFrames() * mPlaybackRate) {
757 return false;
760 DecodedStreamData* stream = mDecoder->GetDecodedStream();
761 if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishVideo) {
762 if (!stream->mStream->HaveEnoughBuffered(TRACK_VIDEO)) {
763 return false;
765 stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_VIDEO,
766 GetStateMachineThread(), GetWakeDecoderRunnable());
769 return true;
772 void MediaDecoderStateMachine::DecodeLoop()
774 LOG(PR_LOG_DEBUG, ("%p Start DecodeLoop()", mDecoder.get()));
776 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
777 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
779 // We want to "pump" the decode until we've got a few frames decoded
780 // before we consider whether decode is falling behind.
781 bool audioPump = true;
782 bool videoPump = true;
784 // If the video decode is falling behind the audio, we'll start dropping the
785 // inter-frames up until the next keyframe which is at or before the current
786 // playback position. skipToNextKeyframe is true if we're currently
787 // skipping up to the next keyframe.
788 bool skipToNextKeyframe = false;
790 // Once we've decoded more than videoPumpThreshold video frames, we'll
791 // no longer be considered to be "pumping video".
792 const unsigned videoPumpThreshold = mRealTime ? 0 : GetAmpleVideoFrames() / 2;
794 // After the audio decode fills with more than audioPumpThreshold usecs
795 // of decoded audio, we'll start to check whether the audio or video decode
796 // is falling behind.
797 const unsigned audioPumpThreshold = mRealTime ? 0 : LOW_AUDIO_USECS * 2;
799 // Our local low audio threshold. We may increase this if we're slow to
800 // decode video frames, in order to reduce the chance of audio underruns.
801 int64_t lowAudioThreshold = LOW_AUDIO_USECS;
803 // Our local ample audio threshold. If we increase lowAudioThreshold, we'll
804 // also increase this too appropriately (we don't want lowAudioThreshold to
805 // be greater than ampleAudioThreshold, else we'd stop decoding!).
806 int64_t ampleAudioThreshold = AMPLE_AUDIO_USECS;
808 // Main decode loop.
809 bool videoPlaying = HasVideo();
810 bool audioPlaying = HasAudio();
811 while ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) &&
812 !mStopDecodeThread &&
813 (videoPlaying || audioPlaying))
815 mReader->PrepareToDecode();
817 // We don't want to consider skipping to the next keyframe if we've
818 // only just started up the decode loop, so wait until we've decoded
819 // some frames before enabling the keyframe skip logic on video.
820 if (videoPump &&
821 (static_cast<uint32_t>(mReader->VideoQueue().GetSize())
822 >= videoPumpThreshold * mPlaybackRate))
824 videoPump = false;
827 // We don't want to consider skipping to the next keyframe if we've
828 // only just started up the decode loop, so wait until we've decoded
829 // some audio data before enabling the keyframe skip logic on audio.
830 if (audioPump && GetDecodedAudioDuration() >= audioPumpThreshold * mPlaybackRate) {
831 audioPump = false;
834 // We'll skip the video decode to the nearest keyframe if we're low on
835 // audio, or if we're low on video, provided we're not running low on
836 // data to decode. If we're running low on downloaded data to decode,
837 // we won't start keyframe skipping, as we'll be pausing playback to buffer
838 // soon anyway and we'll want to be able to display frames immediately
839 // after buffering finishes.
840 if (mState == DECODER_STATE_DECODING &&
841 !skipToNextKeyframe &&
842 videoPlaying &&
843 ((!audioPump && audioPlaying && !mDidThrottleAudioDecoding &&
844 GetDecodedAudioDuration() < lowAudioThreshold * mPlaybackRate) ||
845 (!videoPump && videoPlaying && !mDidThrottleVideoDecoding &&
846 (static_cast<uint32_t>(mReader->VideoQueue().GetSize())
847 < LOW_VIDEO_FRAMES * mPlaybackRate))) &&
848 !HasLowUndecodedData())
850 skipToNextKeyframe = true;
851 LOG(PR_LOG_DEBUG, ("%p Skipping video decode to the next keyframe", mDecoder.get()));
854 // Video decode.
855 bool throttleVideoDecoding = !videoPlaying || HaveEnoughDecodedVideo();
856 if (mDidThrottleVideoDecoding && !throttleVideoDecoding) {
857 videoPump = true;
859 mDidThrottleVideoDecoding = throttleVideoDecoding;
860 if (!throttleVideoDecoding)
862 // Time the video decode, so that if it's slow, we can increase our low
863 // audio threshold to reduce the chance of an audio underrun while we're
864 // waiting for a video decode to complete.
865 TimeDuration decodeTime;
867 int64_t currentTime = GetMediaTime();
868 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
869 TimeStamp start = TimeStamp::Now();
870 videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime);
871 decodeTime = TimeStamp::Now() - start;
873 if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > lowAudioThreshold &&
874 !HasLowUndecodedData())
876 lowAudioThreshold =
877 std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), AMPLE_AUDIO_USECS);
878 ampleAudioThreshold = std::max(THRESHOLD_FACTOR * lowAudioThreshold,
879 ampleAudioThreshold);
880 LOG(PR_LOG_DEBUG,
881 ("Slow video decode, set lowAudioThreshold=%lld ampleAudioThreshold=%lld",
882 lowAudioThreshold, ampleAudioThreshold));
886 // Audio decode.
887 bool throttleAudioDecoding = !audioPlaying || HaveEnoughDecodedAudio(ampleAudioThreshold * mPlaybackRate);
888 if (mDidThrottleAudioDecoding && !throttleAudioDecoding) {
889 audioPump = true;
891 mDidThrottleAudioDecoding = throttleAudioDecoding;
892 if (!mDidThrottleAudioDecoding) {
893 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
894 audioPlaying = mReader->DecodeAudioData();
897 SendStreamData();
899 // Notify to ensure that the AudioLoop() is not waiting, in case it was
900 // waiting for more audio to be decoded.
901 mDecoder->GetReentrantMonitor().NotifyAll();
903 // The ready state can change when we've decoded data, so update the
904 // ready state, so that DOM events can fire.
905 UpdateReadyState();
907 if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING) &&
908 !mStopDecodeThread &&
909 (videoPlaying || audioPlaying) &&
910 throttleAudioDecoding && throttleVideoDecoding)
912 // All active bitstreams' decode is well ahead of the playback
913 // position, we may as well wait for the playback to catch up. Note the
914 // audio push thread acquires and notifies the decoder monitor every time
915 // it pops AudioData off the audio queue. So if the audio push thread pops
916 // the last AudioData off the audio queue right after that queue reported
917 // it was non-empty here, we'll receive a notification on the decoder
918 // monitor which will wake us up shortly after we sleep, thus preventing
919 // both the decode and audio push threads waiting at the same time.
920 // See bug 620326.
921 mDecodeThreadWaiting = true;
922 if (mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING) {
923 // We're not playing, and the decode is about to wait. This means
924 // the decode thread may not be needed in future. Signal the state
925 // machine thread to run, so it can decide whether to shutdown the
926 // decode thread.
927 ScheduleStateMachine();
929 mDecoder->GetReentrantMonitor().Wait();
930 mDecodeThreadWaiting = false;
933 } // End decode loop.
935 if (!mStopDecodeThread &&
936 mState != DECODER_STATE_SHUTDOWN &&
937 mState != DECODER_STATE_SEEKING)
939 mState = DECODER_STATE_COMPLETED;
940 ScheduleStateMachine();
943 LOG(PR_LOG_DEBUG, ("%p Exiting DecodeLoop", mDecoder.get()));
946 bool MediaDecoderStateMachine::IsPlaying()
948 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
950 return !mPlayStartTime.IsNull();
953 // If we have already written enough frames to the AudioStream, start the
954 // playback.
955 static void
956 StartAudioStreamPlaybackIfNeeded(AudioStream* aStream)
958 // We want to have enough data in the buffer to start the stream.
959 if (static_cast<double>(aStream->GetWritten()) / aStream->GetRate() >=
960 static_cast<double>(AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS) / USECS_PER_S) {
961 aStream->Start();
965 static void WriteSilence(AudioStream* aStream, uint32_t aFrames)
967 uint32_t numSamples = aFrames * aStream->GetChannels();
968 nsAutoTArray<AudioDataValue, 1000> buf;
969 buf.SetLength(numSamples);
970 memset(buf.Elements(), 0, numSamples * sizeof(AudioDataValue));
971 aStream->Write(buf.Elements(), aFrames);
973 StartAudioStreamPlaybackIfNeeded(aStream);
976 void MediaDecoderStateMachine::AudioLoop()
978 NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
979 LOG(PR_LOG_DEBUG, ("%p Begun audio thread/loop", mDecoder.get()));
980 int64_t audioDuration = 0;
981 int64_t audioStartTime = -1;
982 uint32_t channels, rate;
983 double volume = -1;
984 bool setVolume;
985 double playbackRate = -1;
986 bool setPlaybackRate;
987 bool preservesPitch;
988 bool setPreservesPitch;
989 int32_t minWriteFrames = -1;
990 AudioChannelType audioChannelType;
993 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
994 mAudioCompleted = false;
995 audioStartTime = mAudioStartTime;
996 NS_ASSERTION(audioStartTime != -1, "Should have audio start time by now");
997 channels = mInfo.mAudioChannels;
998 rate = mInfo.mAudioRate;
1000 audioChannelType = mDecoder->GetAudioChannelType();
1001 volume = mVolume;
1002 preservesPitch = mPreservesPitch;
1003 playbackRate = mPlaybackRate;
1007 // AudioStream initialization can block for extended periods in unusual
1008 // circumstances, so we take care to drop the decoder monitor while
1009 // initializing.
1010 nsAutoPtr<AudioStream> audioStream(AudioStream::AllocateStream());
1011 audioStream->Init(channels, rate, audioChannelType);
1012 audioStream->SetVolume(volume);
1013 audioStream->SetPreservesPitch(preservesPitch);
1014 if (playbackRate != 1.0) {
1015 NS_ASSERTION(playbackRate != 0,
1016 "Don't set the playbackRate to 0 on an AudioStream.");
1017 audioStream->SetPlaybackRate(playbackRate);
1021 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1022 mAudioStream = audioStream;
1026 while (1) {
1027 // Wait while we're not playing, and we're not shutting down, or we're
1028 // playing and we've got no audio to play.
1030 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1031 NS_ASSERTION(mState != DECODER_STATE_DECODING_METADATA,
1032 "Should have meta data before audio started playing.");
1033 while (mState != DECODER_STATE_SHUTDOWN &&
1034 !mStopAudioThread &&
1035 (!IsPlaying() ||
1036 mState == DECODER_STATE_BUFFERING ||
1037 (mReader->AudioQueue().GetSize() == 0 &&
1038 !mReader->AudioQueue().AtEndOfStream())))
1040 if (!IsPlaying() && !mAudioStream->IsPaused()) {
1041 mAudioStream->Pause();
1043 mon.Wait();
1046 // If we're shutting down, break out and exit the audio thread.
1047 // Also break out if audio is being captured.
1048 if (mState == DECODER_STATE_SHUTDOWN ||
1049 mStopAudioThread ||
1050 mReader->AudioQueue().AtEndOfStream())
1052 break;
1055 // We only want to go to the expense of changing the volume if
1056 // the volume has changed.
1057 setVolume = volume != mVolume;
1058 volume = mVolume;
1060 // Same for the playbackRate.
1061 setPlaybackRate = playbackRate != mPlaybackRate;
1062 playbackRate = mPlaybackRate;
1064 // Same for the pitch preservation.
1065 setPreservesPitch = preservesPitch != mPreservesPitch;
1066 preservesPitch = mPreservesPitch;
1068 if (IsPlaying() && mAudioStream->IsPaused()) {
1069 mAudioStream->Resume();
1073 if (setVolume) {
1074 mAudioStream->SetVolume(volume);
1076 if (setPlaybackRate) {
1077 NS_ASSERTION(playbackRate != 0,
1078 "Don't set the playbackRate to 0 in the AudioStreams");
1079 mAudioStream->SetPlaybackRate(playbackRate);
1081 if (setPreservesPitch) {
1082 mAudioStream->SetPreservesPitch(preservesPitch);
1084 if (minWriteFrames == -1) {
1085 minWriteFrames = mAudioStream->GetMinWriteSize();
1087 NS_ASSERTION(mReader->AudioQueue().GetSize() > 0,
1088 "Should have data to play");
1089 // See if there's a gap in the audio. If there is, push silence into the
1090 // audio hardware, so we can play across the gap.
1091 const AudioData* s = mReader->AudioQueue().PeekFront();
1093 // Calculate the number of frames that have been pushed onto the audio
1094 // hardware.
1095 CheckedInt64 playedFrames = UsecsToFrames(audioStartTime, rate) +
1096 audioDuration;
1097 // Calculate the timestamp of the next chunk of audio in numbers of
1098 // samples.
1099 CheckedInt64 sampleTime = UsecsToFrames(s->mTime, rate);
1100 CheckedInt64 missingFrames = sampleTime - playedFrames;
1101 if (!missingFrames.isValid() || !sampleTime.isValid()) {
1102 NS_WARNING("Int overflow adding in AudioLoop()");
1103 break;
1106 int64_t framesWritten = 0;
1107 if (missingFrames.value() > 0) {
1108 // The next audio chunk begins some time after the end of the last chunk
1109 // we pushed to the audio hardware. We must push silence into the audio
1110 // hardware so that the next audio chunk begins playback at the correct
1111 // time.
1112 missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
1113 LOG(PR_LOG_DEBUG, ("%p Decoder playing %d frames of silence",
1114 mDecoder.get(), int32_t(missingFrames.value())));
1115 framesWritten = PlaySilence(static_cast<uint32_t>(missingFrames.value()),
1116 channels, playedFrames.value());
1117 } else {
1118 framesWritten = PlayFromAudioQueue(sampleTime.value(), channels);
1120 audioDuration += framesWritten;
1122 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1123 CheckedInt64 playedUsecs = FramesToUsecs(audioDuration, rate) + audioStartTime;
1124 if (!playedUsecs.isValid()) {
1125 NS_WARNING("Int overflow calculating audio end time");
1126 break;
1128 mAudioEndTime = playedUsecs.value();
1132 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1133 if (mReader->AudioQueue().AtEndOfStream() &&
1134 mState != DECODER_STATE_SHUTDOWN &&
1135 !mStopAudioThread)
1137 // If the media was too short to trigger the start of the audio stream,
1138 // start it now.
1139 mAudioStream->Start();
1140 // Last frame pushed to audio hardware, wait for the audio to finish,
1141 // before the audio thread terminates.
1142 bool seeking = false;
1144 int64_t unplayedFrames = audioDuration % minWriteFrames;
1145 if (minWriteFrames > 1 && unplayedFrames > 0) {
1146 // Sound is written by libsydneyaudio to the hardware in blocks of
1147 // frames of size minWriteFrames. So if the number of frames we've
1148 // written isn't an exact multiple of minWriteFrames, we'll have
1149 // left over audio data which hasn't yet been written to the hardware,
1150 // and so that audio will not start playing. Write silence to ensure
1151 // the last block gets pushed to hardware, so that playback starts.
1152 int64_t framesToWrite = minWriteFrames - unplayedFrames;
1153 if (framesToWrite < UINT32_MAX / channels) {
1154 // Write silence manually rather than using PlaySilence(), so that
1155 // the AudioAPI doesn't get a copy of the audio frames.
1156 ReentrantMonitorAutoExit exit(mDecoder->GetReentrantMonitor());
1157 WriteSilence(mAudioStream, framesToWrite);
1161 int64_t oldPosition = -1;
1162 int64_t position = GetMediaTime();
1163 while (oldPosition != position &&
1164 mAudioEndTime - position > 0 &&
1165 mState != DECODER_STATE_SEEKING &&
1166 mState != DECODER_STATE_SHUTDOWN)
1168 const int64_t DRAIN_BLOCK_USECS = 100000;
1169 Wait(std::min(mAudioEndTime - position, DRAIN_BLOCK_USECS));
1170 oldPosition = position;
1171 position = GetMediaTime();
1173 seeking = mState == DECODER_STATE_SEEKING;
1176 if (!seeking && !mAudioStream->IsPaused()) {
1178 ReentrantMonitorAutoExit exit(mDecoder->GetReentrantMonitor());
1179 mAudioStream->Drain();
1181 // Fire one last event for any extra frames that didn't fill a framebuffer.
1182 mEventManager.Drain(mAudioEndTime);
1186 LOG(PR_LOG_DEBUG, ("%p Reached audio stream end.", mDecoder.get()));
1188 // Must hold lock while shutting down and anulling the audio stream to prevent
1189 // state machine thread trying to use it while we're destroying it.
1190 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1191 mAudioStream->Shutdown();
1192 mAudioStream = nullptr;
1193 mEventManager.Clear();
1194 if (!mAudioCaptured) {
1195 mAudioCompleted = true;
1196 UpdateReadyState();
1197 // Kick the decode thread; it may be sleeping waiting for this to finish.
1198 mDecoder->GetReentrantMonitor().NotifyAll();
1202 LOG(PR_LOG_DEBUG, ("%p Audio stream finished playing, audio thread exit", mDecoder.get()));
1205 uint32_t MediaDecoderStateMachine::PlaySilence(uint32_t aFrames,
1206 uint32_t aChannels,
1207 uint64_t aFrameOffset)
1210 NS_ASSERTION(OnAudioThread(), "Only call on audio thread.");
1211 NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
1212 uint32_t maxFrames = SILENCE_BYTES_CHUNK / aChannels / sizeof(AudioDataValue);
1213 uint32_t frames = std::min(aFrames, maxFrames);
1214 WriteSilence(mAudioStream, frames);
1215 // Dispatch events to the DOM for the audio just written.
1216 mEventManager.QueueWrittenAudioData(nullptr, frames * aChannels,
1217 (aFrameOffset + frames) * aChannels);
1218 return frames;
1221 uint32_t MediaDecoderStateMachine::PlayFromAudioQueue(uint64_t aFrameOffset,
1222 uint32_t aChannels)
1224 NS_ASSERTION(OnAudioThread(), "Only call on audio thread.");
1225 NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
1226 nsAutoPtr<AudioData> audio(mReader->AudioQueue().PopFront());
1228 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1229 NS_WARN_IF_FALSE(IsPlaying(), "Should be playing");
1230 // Awaken the decode loop if it's waiting for space to free up in the
1231 // audio queue.
1232 mDecoder->GetReentrantMonitor().NotifyAll();
1234 int64_t offset = -1;
1235 uint32_t frames = 0;
1236 if (!PR_GetEnv("MOZ_QUIET")) {
1237 LOG(PR_LOG_DEBUG, ("%p Decoder playing %d frames of data to stream for AudioData at %lld",
1238 mDecoder.get(), audio->mFrames, audio->mTime));
1240 mAudioStream->Write(audio->mAudioData,
1241 audio->mFrames);
1243 StartAudioStreamPlaybackIfNeeded(mAudioStream);
1245 offset = audio->mOffset;
1246 frames = audio->mFrames;
1248 // Dispatch events to the DOM for the audio just written.
1249 mEventManager.QueueWrittenAudioData(audio->mAudioData.get(),
1250 audio->mFrames * aChannels,
1251 (aFrameOffset + frames) * aChannels);
1252 if (offset != -1) {
1253 mDecoder->UpdatePlaybackOffset(offset);
1255 return frames;
1258 nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
1260 MediaDecoderReader* cloneReader = nullptr;
1261 if (aCloneDonor) {
1262 cloneReader = static_cast<MediaDecoderStateMachine*>(aCloneDonor)->mReader;
1264 return mReader->Init(cloneReader);
1267 void MediaDecoderStateMachine::StopPlayback()
1269 LOG(PR_LOG_DEBUG, ("%p StopPlayback()", mDecoder.get()));
1271 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
1272 "Should be on state machine thread or the decoder thread.");
1273 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1275 mDecoder->NotifyPlaybackStopped();
1277 if (IsPlaying()) {
1278 mPlayDuration += DurationToUsecs(TimeStamp::Now() - mPlayStartTime);
1279 mPlayStartTime = TimeStamp();
1281 // Notify the audio thread, so that it notices that we've stopped playing,
1282 // so it can pause audio playback.
1283 mDecoder->GetReentrantMonitor().NotifyAll();
1284 NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()");
1287 void MediaDecoderStateMachine::StartPlayback()
1289 LOG(PR_LOG_DEBUG, ("%p StartPlayback()", mDecoder.get()));
1291 NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
1292 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1294 mDecoder->NotifyPlaybackStarted();
1295 mPlayStartTime = TimeStamp::Now();
1297 NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()");
1298 if (NS_FAILED(StartAudioThread())) {
1299 NS_WARNING("Failed to create audio thread");
1301 mDecoder->GetReentrantMonitor().NotifyAll();
1304 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
1306 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
1307 "Should be on state machine thread.");
1308 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1310 NS_ASSERTION(mStartTime >= 0, "Should have positive mStartTime");
1311 mCurrentFrameTime = aTime - mStartTime;
1312 NS_ASSERTION(mCurrentFrameTime >= 0, "CurrentTime should be positive!");
1313 if (aTime > mEndTime) {
1314 NS_ASSERTION(mCurrentFrameTime > GetDuration(),
1315 "CurrentTime must be after duration if aTime > endTime!");
1316 mEndTime = aTime;
1317 nsCOMPtr<nsIRunnable> event =
1318 NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
1319 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1323 void MediaDecoderStateMachine::UpdatePlaybackPosition(int64_t aTime)
1325 UpdatePlaybackPositionInternal(aTime);
1327 bool fragmentEnded = mFragmentEndTime >= 0 && GetMediaTime() >= mFragmentEndTime;
1328 if (!mPositionChangeQueued || fragmentEnded) {
1329 mPositionChangeQueued = true;
1330 nsCOMPtr<nsIRunnable> event =
1331 NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackPositionChanged);
1332 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1335 // Notify DOM of any queued up audioavailable events
1336 mEventManager.DispatchPendingEvents(GetMediaTime());
1338 mMetadataManager.DispatchMetadataIfNeeded(mDecoder, aTime);
1340 if (fragmentEnded) {
1341 StopPlayback();
1345 void MediaDecoderStateMachine::ClearPositionChangeFlag()
1347 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
1348 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1350 mPositionChangeQueued = false;
1353 MediaDecoderOwner::NextFrameStatus MediaDecoderStateMachine::GetNextFrameStatus()
1355 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1356 if (IsBuffering() || IsSeeking()) {
1357 return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING;
1358 } else if (HaveNextFrameData()) {
1359 return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
1361 return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
1364 void MediaDecoderStateMachine::SetVolume(double volume)
1366 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
1367 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1368 mVolume = volume;
1371 void MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured)
1373 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
1374 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1375 if (!mAudioCaptured && aCaptured && !mStopAudioThread) {
1376 // Make sure the state machine runs as soon as possible. That will
1377 // stop the audio thread.
1378 // If mStopAudioThread is true then we're already stopping the audio thread
1379 // and since we set mAudioCaptured to true, nothing can start it again.
1380 ScheduleStateMachine();
1382 mAudioCaptured = aCaptured;
1385 double MediaDecoderStateMachine::GetCurrentTime() const
1387 NS_ASSERTION(NS_IsMainThread() ||
1388 OnStateMachineThread() ||
1389 OnDecodeThread(),
1390 "Should be on main, decode, or state machine thread.");
1392 return static_cast<double>(mCurrentFrameTime) / static_cast<double>(USECS_PER_S);
1395 int64_t MediaDecoderStateMachine::GetDuration()
1397 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1399 if (mEndTime == -1 || mStartTime == -1)
1400 return -1;
1401 return mEndTime - mStartTime;
1404 void MediaDecoderStateMachine::SetDuration(int64_t aDuration)
1406 NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
1407 "Should be on main or decode thread.");
1408 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1410 if (aDuration == -1) {
1411 return;
1414 if (mStartTime != -1) {
1415 mEndTime = mStartTime + aDuration;
1416 } else {
1417 mStartTime = 0;
1418 mEndTime = aDuration;
1422 void MediaDecoderStateMachine::SetMediaEndTime(int64_t aEndTime)
1424 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread");
1425 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1427 mEndTime = aEndTime;
1430 void MediaDecoderStateMachine::SetFragmentEndTime(int64_t aEndTime)
1432 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1434 mFragmentEndTime = aEndTime < 0 ? aEndTime : aEndTime + mStartTime;
1437 void MediaDecoderStateMachine::SetTransportSeekable(bool aTransportSeekable)
1439 NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
1440 "Should be on main thread or the decoder thread.");
1441 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1443 mTransportSeekable = aTransportSeekable;
1446 void MediaDecoderStateMachine::SetMediaSeekable(bool aMediaSeekable)
1448 NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(),
1449 "Should be on main thread or the decoder thread.");
1451 mMediaSeekable = aMediaSeekable;
1454 void MediaDecoderStateMachine::Shutdown()
1456 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
1458 // Once we've entered the shutdown state here there's no going back.
1459 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1461 // Change state before issuing shutdown request to threads so those
1462 // threads can start exiting cleanly during the Shutdown call.
1463 LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN", mDecoder.get()));
1464 ScheduleStateMachine();
1465 mState = DECODER_STATE_SHUTDOWN;
1466 mDecoder->GetReentrantMonitor().NotifyAll();
1469 void MediaDecoderStateMachine::StartDecoding()
1471 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
1472 "Should be on state machine or decode thread.");
1473 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1474 if (mState != DECODER_STATE_DECODING) {
1475 mDecodeStartTime = TimeStamp::Now();
1477 mState = DECODER_STATE_DECODING;
1478 ScheduleStateMachine();
1481 void MediaDecoderStateMachine::Play()
1483 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
1484 // When asked to play, switch to decoding state only if
1485 // we are currently buffering. In other cases, we'll start playing anyway
1486 // when the state machine notices the decoder's state change to PLAYING.
1487 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1488 if (mState == DECODER_STATE_BUFFERING) {
1489 LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder.get()));
1490 mState = DECODER_STATE_DECODING;
1491 mDecodeStartTime = TimeStamp::Now();
1493 ScheduleStateMachine();
1496 void MediaDecoderStateMachine::ResetPlayback()
1498 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
1499 mVideoFrameEndTime = -1;
1500 mAudioStartTime = -1;
1501 mAudioEndTime = -1;
1502 mAudioCompleted = false;
1505 void MediaDecoderStateMachine::NotifyDataArrived(const char* aBuffer,
1506 uint32_t aLength,
1507 int64_t aOffset)
1509 NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
1510 mReader->NotifyDataArrived(aBuffer, aLength, aOffset);
1512 // While playing an unseekable stream of unknown duration, mEndTime is
1513 // updated (in AdvanceFrame()) as we play. But if data is being downloaded
1514 // faster than played, mEndTime won't reflect the end of playable data
1515 // since we haven't played the frame at the end of buffered data. So update
1516 // mEndTime here as new data is downloaded to prevent such a lag.
1517 nsTimeRanges buffered;
1518 if (mDecoder->IsInfinite() &&
1519 NS_SUCCEEDED(mDecoder->GetBuffered(&buffered)))
1521 uint32_t length = 0;
1522 buffered.GetLength(&length);
1523 if (length) {
1524 double end = 0;
1525 buffered.End(length - 1, &end);
1526 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1527 mEndTime = std::max<int64_t>(mEndTime, end * USECS_PER_S);
1532 void MediaDecoderStateMachine::Seek(double aTime)
1534 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
1535 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1537 // We need to be able to seek both at a transport level and at a media level
1538 // to seek.
1539 if (!mMediaSeekable) {
1540 return;
1542 // MediaDecoder::mPlayState should be SEEKING while we seek, and
1543 // in that case MediaDecoder shouldn't be calling us.
1544 NS_ASSERTION(mState != DECODER_STATE_SEEKING,
1545 "We shouldn't already be seeking");
1546 NS_ASSERTION(mState >= DECODER_STATE_DECODING,
1547 "We should have loaded metadata");
1548 double t = aTime * static_cast<double>(USECS_PER_S);
1549 if (t > INT64_MAX) {
1550 // Prevent integer overflow.
1551 return;
1554 mSeekTime = static_cast<int64_t>(t) + mStartTime;
1555 NS_ASSERTION(mSeekTime >= mStartTime && mSeekTime <= mEndTime,
1556 "Can only seek in range [0,duration]");
1558 // Bound the seek time to be inside the media range.
1559 NS_ASSERTION(mStartTime != -1, "Should know start time by now");
1560 NS_ASSERTION(mEndTime != -1, "Should know end time by now");
1561 mSeekTime = std::min(mSeekTime, mEndTime);
1562 mSeekTime = std::max(mStartTime, mSeekTime);
1563 mBasePosition = mSeekTime;
1564 LOG(PR_LOG_DEBUG, ("%p Changed state to SEEKING (to %f)", mDecoder.get(), aTime));
1565 mState = DECODER_STATE_SEEKING;
1566 if (mDecoder->GetDecodedStream()) {
1567 mDecoder->RecreateDecodedStream(mSeekTime - mStartTime);
1569 ScheduleStateMachine();
1572 void MediaDecoderStateMachine::StopDecodeThread()
1574 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
1575 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1576 if (mRequestedNewDecodeThread) {
1577 // We've requested that the decode be created, but it hasn't been yet.
1578 // Cancel that request.
1579 NS_ASSERTION(!mDecodeThread,
1580 "Shouldn't have a decode thread until after request processed");
1581 StateMachineTracker::Instance().CancelCreateDecodeThread(this);
1582 mRequestedNewDecodeThread = false;
1584 mStopDecodeThread = true;
1585 mDecoder->GetReentrantMonitor().NotifyAll();
1586 if (mDecodeThread) {
1587 LOG(PR_LOG_DEBUG, ("%p Shutdown decode thread", mDecoder.get()));
1589 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
1590 mDecodeThread->Shutdown();
1591 StateMachineTracker::Instance().NoteDecodeThreadDestroyed();
1593 mDecodeThread = nullptr;
1594 mDecodeThreadIdle = false;
1596 NS_ASSERTION(!mRequestedNewDecodeThread,
1597 "Any pending requests for decode threads must be canceled and unflagged");
1598 NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this),
1599 "Any pending requests for decode threads must be canceled");
1602 void MediaDecoderStateMachine::StopAudioThread()
1604 NS_ASSERTION(OnDecodeThread() ||
1605 OnStateMachineThread(), "Should be on decode thread or state machine thread");
1606 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1608 if (mStopAudioThread) {
1609 // Nothing to do, since the thread is already stopping
1610 return;
1613 mStopAudioThread = true;
1614 mDecoder->GetReentrantMonitor().NotifyAll();
1615 if (mAudioThread) {
1616 LOG(PR_LOG_DEBUG, ("%p Shutdown audio thread", mDecoder.get()));
1618 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
1619 mAudioThread->Shutdown();
1621 mAudioThread = nullptr;
1622 // Now that the audio thread is dead, try sending data to our MediaStream(s).
1623 // That may have been waiting for the audio thread to stop.
1624 SendStreamData();
1628 nsresult
1629 MediaDecoderStateMachine::ScheduleDecodeThread()
1631 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
1632 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1634 mStopDecodeThread = false;
1635 if (mState >= DECODER_STATE_COMPLETED) {
1636 return NS_OK;
1638 if (mDecodeThread) {
1639 NS_ASSERTION(!mRequestedNewDecodeThread,
1640 "Shouldn't have requested new decode thread when we have a decode thread");
1641 // We already have a decode thread...
1642 if (mDecodeThreadIdle) {
1643 // ... and it's not been shutdown yet, wake it up.
1644 nsCOMPtr<nsIRunnable> event =
1645 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeThreadRun);
1646 mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
1647 mDecodeThreadIdle = false;
1649 return NS_OK;
1650 } else if (!mRequestedNewDecodeThread) {
1651 // We don't already have a decode thread, request a new one.
1652 mRequestedNewDecodeThread = true;
1653 ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor());
1654 StateMachineTracker::Instance().RequestCreateDecodeThread(this);
1656 return NS_OK;
1659 nsresult
1660 MediaDecoderStateMachine::StartDecodeThread()
1662 NS_ASSERTION(StateMachineTracker::Instance().GetDecodeThreadCount() <
1663 StateMachineTracker::MAX_DECODE_THREADS,
1664 "Should not have reached decode thread limit");
1666 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
1667 NS_ASSERTION(!StateMachineTracker::Instance().IsQueued(this),
1668 "Should not already have a pending request for a new decode thread.");
1669 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
1670 NS_ASSERTION(!mDecodeThread, "Should not have decode thread yet");
1671 NS_ASSERTION(mRequestedNewDecodeThread, "Should have requested this...");
1673 mRequestedNewDecodeThread = false;
1675 nsresult rv = NS_NewNamedThread("Media Decode",
1676 getter_AddRefs(mDecodeThread),
1677 nullptr,
1678 MEDIA_THREAD_STACK_SIZE);
1679 if (NS_FAILED(rv)) {
1680 // Give up, report error to media element.
1681 nsCOMPtr<nsIRunnable> event =
1682 NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
1683 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1684 return rv;
1687 nsCOMPtr<nsIRunnable> event =
1688 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeThreadRun);
1689 mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL);
1690 mDecodeThreadIdle = false;
1692 return NS_OK;
1695 nsresult
1696 MediaDecoderStateMachine::StartAudioThread()
1698 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
1699 "Should be on state machine or decode thread.");
1700 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1701 if (mAudioCaptured) {
1702 NS_ASSERTION(mStopAudioThread, "mStopAudioThread must always be true if audio is captured");
1703 return NS_OK;
1706 mStopAudioThread = false;
1707 if (HasAudio() && !mAudioThread) {
1708 nsresult rv = NS_NewNamedThread("Media Audio",
1709 getter_AddRefs(mAudioThread),
1710 nullptr,
1711 MEDIA_THREAD_STACK_SIZE);
1712 if (NS_FAILED(rv)) {
1713 LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN because failed to create audio thread", mDecoder.get()));
1714 mState = DECODER_STATE_SHUTDOWN;
1715 return rv;
1718 nsCOMPtr<nsIRunnable> event =
1719 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::AudioLoop);
1720 mAudioThread->Dispatch(event, NS_DISPATCH_NORMAL);
1722 return NS_OK;
1725 int64_t MediaDecoderStateMachine::AudioDecodedUsecs() const
1727 NS_ASSERTION(HasAudio(),
1728 "Should only call AudioDecodedUsecs() when we have audio");
1729 // The amount of audio we have decoded is the amount of audio data we've
1730 // already decoded and pushed to the hardware, plus the amount of audio
1731 // data waiting to be pushed to the hardware.
1732 int64_t pushed = (mAudioEndTime != -1) ? (mAudioEndTime - GetMediaTime()) : 0;
1733 return pushed + mReader->AudioQueue().Duration();
1736 bool MediaDecoderStateMachine::HasLowDecodedData(int64_t aAudioUsecs) const
1738 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1739 // We consider ourselves low on decoded data if we're low on audio,
1740 // provided we've not decoded to the end of the audio stream, or
1741 // if we're only playing video and we're low on video frames, provided
1742 // we've not decoded to the end of the video stream.
1743 return ((HasAudio() &&
1744 !mReader->AudioQueue().IsFinished() &&
1745 AudioDecodedUsecs() < aAudioUsecs)
1747 (!HasAudio() &&
1748 HasVideo() &&
1749 !mReader->VideoQueue().IsFinished() &&
1750 static_cast<uint32_t>(mReader->VideoQueue().GetSize()) < LOW_VIDEO_FRAMES));
1753 bool MediaDecoderStateMachine::HasLowUndecodedData() const
1755 return GetUndecodedData() < mLowDataThresholdUsecs;
1758 int64_t MediaDecoderStateMachine::GetUndecodedData() const
1760 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1761 NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
1762 "Must have loaded metadata for GetBuffered() to work");
1763 nsTimeRanges buffered;
1765 nsresult res = mDecoder->GetBuffered(&buffered);
1766 NS_ENSURE_SUCCESS(res, 0);
1767 double currentTime = GetCurrentTime();
1769 nsIDOMTimeRanges* r = static_cast<nsIDOMTimeRanges*>(&buffered);
1770 uint32_t length = 0;
1771 res = r->GetLength(&length);
1772 NS_ENSURE_SUCCESS(res, 0);
1774 for (uint32_t index = 0; index < length; ++index) {
1775 double start, end;
1776 res = r->Start(index, &start);
1777 NS_ENSURE_SUCCESS(res, 0);
1779 res = r->End(index, &end);
1780 NS_ENSURE_SUCCESS(res, 0);
1782 if (start <= currentTime && end >= currentTime) {
1783 return static_cast<int64_t>((end - currentTime) * USECS_PER_S);
1786 return 0;
1789 void MediaDecoderStateMachine::SetFrameBufferLength(uint32_t aLength)
1791 NS_ASSERTION(aLength >= 512 && aLength <= 16384,
1792 "The length must be between 512 and 16384");
1793 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1794 mEventManager.SetSignalBufferLength(aLength);
1797 nsresult MediaDecoderStateMachine::DecodeMetadata()
1799 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
1800 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1801 NS_ASSERTION(mState == DECODER_STATE_DECODING_METADATA,
1802 "Only call when in metadata decoding state");
1804 LOG(PR_LOG_DEBUG, ("%p Decoding Media Headers", mDecoder.get()));
1805 nsresult res;
1806 VideoInfo info;
1807 MetadataTags* tags;
1809 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
1810 res = mReader->ReadMetadata(&info, &tags);
1812 mInfo = info;
1814 if (NS_FAILED(res) || (!info.mHasVideo && !info.mHasAudio)) {
1815 // Dispatch the event to call DecodeError synchronously. This ensures
1816 // we're in shutdown state by the time we exit the decode thread.
1817 // If we just moved to shutdown state here on the decode thread, we may
1818 // cause the state machine to shutdown/free memory without closing its
1819 // media stream properly, and we'll get callbacks from the media stream
1820 // causing a crash. Note the state machine shutdown joins this decode
1821 // thread during shutdown (and other state machines can run on the state
1822 // machine thread while the join is waiting), so it's safe to do this
1823 // synchronously.
1824 nsCOMPtr<nsIRunnable> event =
1825 NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
1826 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
1827 NS_DispatchToMainThread(event, NS_DISPATCH_SYNC);
1828 return NS_ERROR_FAILURE;
1830 mDecoder->StartProgressUpdates();
1831 mGotDurationFromMetaData = (GetDuration() != -1);
1833 VideoData* videoData = FindStartTime();
1834 if (videoData) {
1835 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
1836 RenderVideoFrame(videoData, TimeStamp::Now());
1839 if (mState == DECODER_STATE_SHUTDOWN) {
1840 return NS_ERROR_FAILURE;
1843 NS_ASSERTION(mStartTime != -1, "Must have start time");
1844 MOZ_ASSERT((!HasVideo() && !HasAudio()) ||
1845 !(mMediaSeekable && mTransportSeekable) || mEndTime != -1,
1846 "Active seekable media should have end time");
1847 MOZ_ASSERT(!(mMediaSeekable && mTransportSeekable) ||
1848 GetDuration() != -1, "Seekable media should have duration");
1849 LOG(PR_LOG_DEBUG, ("%p Media goes from %lld to %lld (duration %lld)"
1850 " transportSeekable=%d, mediaSeekable=%d",
1851 mDecoder.get(), mStartTime, mEndTime, GetDuration(),
1852 mTransportSeekable, mMediaSeekable));
1854 // Inform the element that we've loaded the metadata and the first frame,
1855 // setting the default framebuffer size for audioavailable events. Also,
1856 // if there is audio, let the MozAudioAvailable event manager know about
1857 // the metadata.
1858 if (HasAudio()) {
1859 mEventManager.Init(mInfo.mAudioChannels, mInfo.mAudioRate);
1860 // Set the buffer length at the decoder level to be able, to be able
1861 // to retrive the value via media element method. The RequestFrameBufferLength
1862 // will call the MediaDecoderStateMachine::SetFrameBufferLength().
1863 uint32_t frameBufferLength = mInfo.mAudioChannels * FRAMEBUFFER_LENGTH_PER_CHANNEL;
1864 mDecoder->RequestFrameBufferLength(frameBufferLength);
1867 nsCOMPtr<nsIRunnable> metadataLoadedEvent =
1868 new AudioMetadataEventRunner(mDecoder,
1869 mInfo.mAudioChannels,
1870 mInfo.mAudioRate,
1871 HasAudio(),
1872 HasVideo(),
1873 tags);
1874 NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
1876 if (mState == DECODER_STATE_DECODING_METADATA) {
1877 LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder.get()));
1878 StartDecoding();
1881 if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED) &&
1882 mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
1883 !IsPlaying())
1885 StartPlayback();
1888 return NS_OK;
1891 void MediaDecoderStateMachine::DecodeSeek()
1893 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
1894 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
1895 NS_ASSERTION(mState == DECODER_STATE_SEEKING,
1896 "Only call when in seeking state");
1898 mDidThrottleAudioDecoding = false;
1899 mDidThrottleVideoDecoding = false;
1901 // During the seek, don't have a lock on the decoder state,
1902 // otherwise long seek operations can block the main thread.
1903 // The events dispatched to the main thread are SYNC calls.
1904 // These calls are made outside of the decode monitor lock so
1905 // it is safe for the main thread to makes calls that acquire
1906 // the lock since it won't deadlock. We check the state when
1907 // acquiring the lock again in case shutdown has occurred
1908 // during the time when we didn't have the lock.
1909 int64_t seekTime = mSeekTime;
1910 mDecoder->StopProgressUpdates();
1912 bool currentTimeChanged = false;
1913 int64_t mediaTime = GetMediaTime();
1914 if (mediaTime != seekTime) {
1915 currentTimeChanged = true;
1916 // Stop playback now to ensure that while we're outside the monitor
1917 // dispatching SeekingStarted, playback doesn't advance and mess with
1918 // mCurrentFrameTime that we've setting to seekTime here.
1919 StopPlayback();
1920 UpdatePlaybackPositionInternal(seekTime);
1923 // SeekingStarted will do a UpdateReadyStateForData which will
1924 // inform the element and its users that we have no frames
1925 // to display
1927 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
1928 nsCOMPtr<nsIRunnable> startEvent =
1929 NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStarted);
1930 NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC);
1933 if (currentTimeChanged) {
1934 // The seek target is different than the current playback position,
1935 // we'll need to seek the playback position, so shutdown our decode
1936 // and audio threads.
1937 StopAudioThread();
1938 ResetPlayback();
1939 nsresult res;
1941 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
1942 // Now perform the seek. We must not hold the state machine monitor
1943 // while we seek, since the seek reads, which could block on I/O.
1944 res = mReader->Seek(seekTime,
1945 mStartTime,
1946 mEndTime,
1947 mediaTime);
1949 if (NS_SUCCEEDED(res)) {
1950 AudioData* audio = HasAudio() ? mReader->AudioQueue().PeekFront() : nullptr;
1951 NS_ASSERTION(!audio || (audio->mTime <= seekTime &&
1952 seekTime <= audio->mTime + audio->mDuration),
1953 "Seek target should lie inside the first audio block after seek");
1954 int64_t startTime = (audio && audio->mTime < seekTime) ? audio->mTime : seekTime;
1955 mAudioStartTime = startTime;
1956 mPlayDuration = startTime - mStartTime;
1957 if (HasVideo()) {
1958 VideoData* video = mReader->VideoQueue().PeekFront();
1959 if (video) {
1960 NS_ASSERTION(video->mTime <= seekTime && seekTime <= video->mEndTime,
1961 "Seek target should lie inside the first frame after seek");
1963 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
1964 RenderVideoFrame(video, TimeStamp::Now());
1966 nsCOMPtr<nsIRunnable> event =
1967 NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate);
1968 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1973 mDecoder->StartProgressUpdates();
1974 if (mState == DECODER_STATE_SHUTDOWN)
1975 return;
1977 // Try to decode another frame to detect if we're at the end...
1978 LOG(PR_LOG_DEBUG, ("%p Seek completed, mCurrentFrameTime=%lld\n",
1979 mDecoder.get(), mCurrentFrameTime));
1981 // Change state to DECODING or COMPLETED now. SeekingStopped will
1982 // call MediaDecoderStateMachine::Seek to reset our state to SEEKING
1983 // if we need to seek again.
1985 nsCOMPtr<nsIRunnable> stopEvent;
1986 bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
1987 if (GetMediaTime() == mEndTime && !isLiveStream) {
1988 // Seeked to end of media, move to COMPLETED state. Note we don't do
1989 // this if we're playing a live stream, since the end of media will advance
1990 // once we download more data!
1991 LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to COMPLETED",
1992 mDecoder.get(), seekTime));
1993 stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStoppedAtEnd);
1994 mState = DECODER_STATE_COMPLETED;
1995 } else {
1996 LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to DECODING",
1997 mDecoder.get(), seekTime));
1998 stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStopped);
1999 StartDecoding();
2002 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
2003 NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
2006 // Reset quick buffering status. This ensures that if we began the
2007 // seek while quick-buffering, we won't bypass quick buffering mode
2008 // if we need to buffer after the seek.
2009 mQuickBuffering = false;
2011 ScheduleStateMachine();
2014 // Runnable to dispose of the decoder and state machine on the main thread.
2015 class nsDecoderDisposeEvent : public nsRunnable {
2016 public:
2017 nsDecoderDisposeEvent(already_AddRefed<MediaDecoder> aDecoder,
2018 already_AddRefed<MediaDecoderStateMachine> aStateMachine)
2019 : mDecoder(aDecoder), mStateMachine(aStateMachine) {}
2020 NS_IMETHOD Run() {
2021 NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
2022 mStateMachine->ReleaseDecoder();
2023 mDecoder->ReleaseStateMachine();
2024 mStateMachine = nullptr;
2025 mDecoder = nullptr;
2026 return NS_OK;
2028 private:
2029 nsRefPtr<MediaDecoder> mDecoder;
2030 nsCOMPtr<MediaDecoderStateMachine> mStateMachine;
2033 // Runnable which dispatches an event to the main thread to dispose of the
2034 // decoder and state machine. This runs on the state machine thread after
2035 // the state machine has shutdown, and all events for that state machine have
2036 // finished running.
2037 class nsDispatchDisposeEvent : public nsRunnable {
2038 public:
2039 nsDispatchDisposeEvent(MediaDecoder* aDecoder,
2040 MediaDecoderStateMachine* aStateMachine)
2041 : mDecoder(aDecoder), mStateMachine(aStateMachine) {}
2042 NS_IMETHOD Run() {
2043 NS_DispatchToMainThread(new nsDecoderDisposeEvent(mDecoder.forget(),
2044 mStateMachine.forget()));
2045 return NS_OK;
2047 private:
2048 nsRefPtr<MediaDecoder> mDecoder;
2049 nsCOMPtr<MediaDecoderStateMachine> mStateMachine;
2052 nsresult MediaDecoderStateMachine::RunStateMachine()
2054 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2056 MediaResource* resource = mDecoder->GetResource();
2057 NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
2059 switch (mState) {
2060 case DECODER_STATE_SHUTDOWN: {
2061 if (IsPlaying()) {
2062 StopPlayback();
2064 StopAudioThread();
2065 // If mAudioThread is non-null after StopAudioThread completes, we are
2066 // running in a nested event loop waiting for Shutdown() on
2067 // mAudioThread to complete. Return to the event loop and let it
2068 // finish processing before continuing with shutdown.
2069 if (mAudioThread) {
2070 MOZ_ASSERT(mStopAudioThread);
2071 return NS_OK;
2073 StopDecodeThread();
2074 // Now that those threads are stopped, there's no possibility of
2075 // mPendingWakeDecoder being needed again. Revoke it.
2076 mPendingWakeDecoder = nullptr;
2077 NS_ASSERTION(mState == DECODER_STATE_SHUTDOWN,
2078 "How did we escape from the shutdown state?");
2079 // We must daisy-chain these events to destroy the decoder. We must
2080 // destroy the decoder on the main thread, but we can't destroy the
2081 // decoder while this thread holds the decoder monitor. We can't
2082 // dispatch an event to the main thread to destroy the decoder from
2083 // here, as the event may run before the dispatch returns, and we
2084 // hold the decoder monitor here. We also want to guarantee that the
2085 // state machine is destroyed on the main thread, and so the
2086 // event runner running this function (which holds a reference to the
2087 // state machine) needs to finish and be released in order to allow
2088 // that. So we dispatch an event to run after this event runner has
2089 // finished and released its monitor/references. That event then will
2090 // dispatch an event to the main thread to release the decoder and
2091 // state machine.
2092 NS_DispatchToCurrentThread(new nsDispatchDisposeEvent(mDecoder, this));
2093 return NS_OK;
2096 case DECODER_STATE_DECODING_METADATA: {
2097 // Ensure we have a decode thread to decode metadata.
2098 return ScheduleDecodeThread();
2101 case DECODER_STATE_DECODING: {
2102 if (mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING &&
2103 IsPlaying())
2105 // We're playing, but the element/decoder is in paused state. Stop
2106 // playing! Note we do this before StopDecodeThread() below because
2107 // that blocks this state machine's execution, and can cause a
2108 // perceptible delay between the pause command, and playback actually
2109 // pausing.
2110 StopPlayback();
2113 if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
2114 !IsPlaying()) {
2115 // We are playing, but the state machine does not know it yet. Tell it
2116 // that it is, so that the clock can be properly queried.
2117 StartPlayback();
2120 if (IsPausedAndDecoderWaiting()) {
2121 // The decode buffers are full, and playback is paused. Shutdown the
2122 // decode thread.
2123 StopDecodeThread();
2124 return NS_OK;
2127 // We're playing and/or our decode buffers aren't full. Ensure we have
2128 // an active decode thread.
2129 if (NS_FAILED(ScheduleDecodeThread())) {
2130 NS_WARNING("Failed to start media decode thread!");
2131 return NS_ERROR_FAILURE;
2134 AdvanceFrame();
2135 NS_ASSERTION(mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING ||
2136 IsStateMachineScheduled() ||
2137 mPlaybackRate == 0.0, "Must have timer scheduled");
2138 return NS_OK;
2141 case DECODER_STATE_BUFFERING: {
2142 if (IsPausedAndDecoderWaiting()) {
2143 // The decode buffers are full, and playback is paused. Shutdown the
2144 // decode thread.
2145 StopDecodeThread();
2146 return NS_OK;
2149 TimeStamp now = TimeStamp::Now();
2150 NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
2152 // We will remain in the buffering state if we've not decoded enough
2153 // data to begin playback, or if we've not downloaded a reasonable
2154 // amount of data inside our buffering time.
2155 TimeDuration elapsed = now - mBufferingStart;
2156 bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
2157 if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
2158 elapsed < TimeDuration::FromSeconds(mBufferingWait * mPlaybackRate) &&
2159 (mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
2160 : (GetUndecodedData() < mBufferingWait * mPlaybackRate * USECS_PER_S)) &&
2161 !resource->IsDataCachedToEndOfResource(mDecoder->mDecoderPosition) &&
2162 !resource->IsSuspended())
2164 LOG(PR_LOG_DEBUG,
2165 ("%p Buffering: %.3lfs/%ds, timeout in %.3lfs %s",
2166 mDecoder.get(),
2167 GetUndecodedData() / static_cast<double>(USECS_PER_S),
2168 mBufferingWait,
2169 mBufferingWait - elapsed.ToSeconds(),
2170 (mQuickBuffering ? "(quick exit)" : "")));
2171 ScheduleStateMachine(USECS_PER_S);
2172 return NS_OK;
2173 } else {
2174 LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder.get()));
2175 LOG(PR_LOG_DEBUG, ("%p Buffered for %.3lfs",
2176 mDecoder.get(),
2177 (now - mBufferingStart).ToSeconds()));
2178 StartDecoding();
2181 // Notify to allow blocked decoder thread to continue
2182 mDecoder->GetReentrantMonitor().NotifyAll();
2183 UpdateReadyState();
2184 if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
2185 !IsPlaying())
2187 StartPlayback();
2189 NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
2190 return NS_OK;
2193 case DECODER_STATE_SEEKING: {
2194 // Ensure we have a decode thread to perform the seek.
2195 return ScheduleDecodeThread();
2198 case DECODER_STATE_COMPLETED: {
2199 StopDecodeThread();
2201 if (mState != DECODER_STATE_COMPLETED) {
2202 // While we're waiting for the decode thread to shutdown, we can
2203 // change state, for example to seeking or shutdown state.
2204 // Whatever changed our state should have scheduled another state
2205 // machine run.
2206 NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
2207 return NS_OK;
2210 // Play the remaining media. We want to run AdvanceFrame() at least
2211 // once to ensure the current playback position is advanced to the
2212 // end of the media, and so that we update the readyState.
2213 if (mState == DECODER_STATE_COMPLETED &&
2214 (mReader->VideoQueue().GetSize() > 0 ||
2215 (HasAudio() && !mAudioCompleted)))
2217 AdvanceFrame();
2218 NS_ASSERTION(mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING ||
2219 mPlaybackRate == 0 ||
2220 IsStateMachineScheduled(),
2221 "Must have timer scheduled");
2222 return NS_OK;
2225 // StopPlayback in order to reset the IsPlaying() state so audio
2226 // is restarted correctly.
2227 StopPlayback();
2229 if (mState != DECODER_STATE_COMPLETED) {
2230 // While we're presenting a frame we can change state. Whatever changed
2231 // our state should have scheduled another state machine run.
2232 NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
2233 return NS_OK;
2236 StopAudioThread();
2237 if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING) {
2238 int64_t videoTime = HasVideo() ? mVideoFrameEndTime : 0;
2239 int64_t clockTime = std::max(mEndTime, std::max(videoTime, GetAudioClock()));
2240 UpdatePlaybackPosition(clockTime);
2241 nsCOMPtr<nsIRunnable> event =
2242 NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
2243 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
2245 return NS_OK;
2249 return NS_OK;
2252 void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
2253 TimeStamp aTarget)
2255 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
2256 "Should be on state machine or decode thread.");
2257 mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn();
2259 if (aData->mDuplicate) {
2260 return;
2263 if (!PR_GetEnv("MOZ_QUIET")) {
2264 LOG(PR_LOG_DEBUG, ("%p Decoder playing video frame %lld",
2265 mDecoder.get(), aData->mTime));
2268 VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
2269 if (container) {
2270 container->SetCurrentFrame(aData->mDisplay, aData->mImage, aTarget);
2274 int64_t
2275 MediaDecoderStateMachine::GetAudioClock()
2277 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
2278 // We must hold the decoder monitor while using the audio stream off the
2279 // audio thread to ensure that it doesn't get destroyed on the audio thread
2280 // while we're using it.
2281 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2282 if (!HasAudio() || mAudioCaptured)
2283 return -1;
2284 if (!mAudioStream) {
2285 // Audio thread hasn't played any data yet.
2286 return mAudioStartTime;
2288 int64_t t = mAudioStream->GetPosition();
2289 return (t == -1) ? -1 : t + mAudioStartTime;
2292 int64_t MediaDecoderStateMachine::GetVideoStreamPosition()
2294 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2296 if (!IsPlaying()) {
2297 return mPlayDuration + mStartTime;
2300 // The playbackRate has been just been changed, reset the playstartTime.
2301 if (mResetPlayStartTime) {
2302 mPlayStartTime = TimeStamp::Now();
2303 mResetPlayStartTime = false;
2306 int64_t pos = DurationToUsecs(TimeStamp::Now() - mPlayStartTime) + mPlayDuration;
2307 pos -= mBasePosition;
2308 NS_ASSERTION(pos >= 0, "Video stream position should be positive.");
2309 return mBasePosition + pos * mPlaybackRate + mStartTime;
2312 int64_t MediaDecoderStateMachine::GetClock() {
2313 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
2314 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2316 // Determine the clock time. If we've got audio, and we've not reached
2317 // the end of the audio, use the audio clock. However if we've finished
2318 // audio, or don't have audio, use the system clock.
2319 int64_t clock_time = -1;
2320 if (!IsPlaying()) {
2321 clock_time = mPlayDuration + mStartTime;
2322 } else {
2323 int64_t audio_time = GetAudioClock();
2324 if (HasAudio() && !mAudioCompleted && audio_time != -1) {
2325 clock_time = audio_time;
2326 // Resync against the audio clock, while we're trusting the
2327 // audio clock. This ensures no "drift", particularly on Linux.
2328 mPlayDuration = clock_time - mStartTime;
2329 mPlayStartTime = TimeStamp::Now();
2330 } else {
2331 // Audio is disabled on this system. Sync to the system clock.
2332 clock_time = GetVideoStreamPosition();
2333 // Ensure the clock can never go backwards.
2334 NS_ASSERTION(mCurrentFrameTime <= clock_time || mPlaybackRate <= 0,
2335 "Clock should go forwards if the playback rate is > 0.");
2338 return clock_time;
2341 void MediaDecoderStateMachine::AdvanceFrame()
2343 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
2344 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2345 NS_ASSERTION(!HasAudio() || mAudioStartTime != -1,
2346 "Should know audio start time if we have audio.");
2348 if (mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING) {
2349 return;
2352 // If playbackRate is 0.0, we should stop the progress, but not be in paused
2353 // state, per spec.
2354 if (mPlaybackRate == 0.0) {
2355 return;
2358 int64_t clock_time = GetClock();
2359 // Skip frames up to the frame at the playback position, and figure out
2360 // the time remaining until it's time to display the next frame.
2361 int64_t remainingTime = AUDIO_DURATION_USECS;
2362 NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
2363 nsAutoPtr<VideoData> currentFrame;
2364 #ifdef PR_LOGGING
2365 int32_t droppedFrames = 0;
2366 #endif
2367 if (mReader->VideoQueue().GetSize() > 0) {
2368 VideoData* frame = mReader->VideoQueue().PeekFront();
2369 while (mRealTime || clock_time >= frame->mTime) {
2370 mVideoFrameEndTime = frame->mEndTime;
2371 currentFrame = frame;
2372 LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld", mDecoder.get(), frame->mTime));
2373 #ifdef PR_LOGGING
2374 if (droppedFrames++) {
2375 LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld (%d so far)",
2376 mDecoder.get(), frame->mTime, droppedFrames - 1));
2378 #endif
2379 mReader->VideoQueue().PopFront();
2380 // Notify the decode thread that the video queue's buffers may have
2381 // free'd up space for more frames.
2382 mDecoder->GetReentrantMonitor().NotifyAll();
2383 mDecoder->UpdatePlaybackOffset(frame->mOffset);
2384 if (mReader->VideoQueue().GetSize() == 0)
2385 break;
2386 frame = mReader->VideoQueue().PeekFront();
2388 // Current frame has already been presented, wait until it's time to
2389 // present the next frame.
2390 if (frame && !currentFrame) {
2391 int64_t now = IsPlaying() ? clock_time : mPlayDuration;
2393 remainingTime = frame->mTime - now;
2397 // Check to see if we don't have enough data to play up to the next frame.
2398 // If we don't, switch to buffering mode.
2399 MediaResource* resource = mDecoder->GetResource();
2400 if (mState == DECODER_STATE_DECODING &&
2401 mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
2402 HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
2403 !resource->IsDataCachedToEndOfResource(mDecoder->mDecoderPosition) &&
2404 !resource->IsSuspended() &&
2405 (JustExitedQuickBuffering() || HasLowUndecodedData()))
2407 if (currentFrame) {
2408 mReader->VideoQueue().PushFront(currentFrame.forget());
2410 StartBuffering();
2411 ScheduleStateMachine();
2412 return;
2415 // We've got enough data to keep playing until at least the next frame.
2416 // Start playing now if need be.
2417 if (!IsPlaying() && ((mFragmentEndTime >= 0 && clock_time < mFragmentEndTime) || mFragmentEndTime < 0)) {
2418 StartPlayback();
2421 if (currentFrame) {
2422 // Decode one frame and display it.
2423 TimeStamp presTime = mPlayStartTime - UsecsToDuration(mPlayDuration) +
2424 UsecsToDuration(currentFrame->mTime - mStartTime);
2425 NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time");
2427 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
2428 // If we have video, we want to increment the clock in steps of the frame
2429 // duration.
2430 RenderVideoFrame(currentFrame, presTime);
2432 // If we're no longer playing after dropping and reacquiring the lock,
2433 // playback must've been stopped on the decode thread (by a seek, for
2434 // example). In that case, the current frame is probably out of date.
2435 if (!IsPlaying()) {
2436 ScheduleStateMachine();
2437 return;
2439 mDecoder->GetFrameStatistics().NotifyPresentedFrame();
2440 remainingTime = currentFrame->mEndTime - clock_time;
2441 currentFrame = nullptr;
2444 // Cap the current time to the larger of the audio and video end time.
2445 // This ensures that if we're running off the system clock, we don't
2446 // advance the clock to after the media end time.
2447 if (mVideoFrameEndTime != -1 || mAudioEndTime != -1) {
2448 // These will be non -1 if we've displayed a video frame, or played an audio frame.
2449 clock_time = std::min(clock_time, std::max(mVideoFrameEndTime, mAudioEndTime));
2450 if (clock_time > GetMediaTime()) {
2451 // Only update the playback position if the clock time is greater
2452 // than the previous playback position. The audio clock can
2453 // sometimes report a time less than its previously reported in
2454 // some situations, and we need to gracefully handle that.
2455 UpdatePlaybackPosition(clock_time);
2459 // If the number of audio/video frames queued has changed, either by
2460 // this function popping and playing a video frame, or by the audio
2461 // thread popping and playing an audio frame, we may need to update our
2462 // ready state. Post an update to do so.
2463 UpdateReadyState();
2465 ScheduleStateMachine(remainingTime);
2468 void MediaDecoderStateMachine::Wait(int64_t aUsecs) {
2469 NS_ASSERTION(OnAudioThread(), "Only call on the audio thread");
2470 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2471 TimeStamp end = TimeStamp::Now() + UsecsToDuration(std::max<int64_t>(USECS_PER_MS, aUsecs));
2472 TimeStamp now;
2473 while ((now = TimeStamp::Now()) < end &&
2474 mState != DECODER_STATE_SHUTDOWN &&
2475 mState != DECODER_STATE_SEEKING &&
2476 !mStopAudioThread &&
2477 IsPlaying())
2479 int64_t ms = static_cast<int64_t>(NS_round((end - now).ToSeconds() * 1000));
2480 if (ms == 0 || ms > UINT32_MAX) {
2481 break;
2483 mDecoder->GetReentrantMonitor().Wait(PR_MillisecondsToInterval(static_cast<uint32_t>(ms)));
2487 VideoData* MediaDecoderStateMachine::FindStartTime()
2489 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
2490 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2491 int64_t startTime = 0;
2492 mStartTime = 0;
2493 VideoData* v = nullptr;
2495 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
2496 v = mReader->FindStartTime(startTime);
2498 if (startTime != 0) {
2499 mStartTime = startTime;
2500 if (mGotDurationFromMetaData) {
2501 NS_ASSERTION(mEndTime != -1,
2502 "We should have mEndTime as supplied duration here");
2503 // We were specified a duration from a Content-Duration HTTP header.
2504 // Adjust mEndTime so that mEndTime-mStartTime matches the specified
2505 // duration.
2506 mEndTime = mStartTime + mEndTime;
2509 // Set the audio start time to be start of media. If this lies before the
2510 // first actual audio frame we have, we'll inject silence during playback
2511 // to ensure the audio starts at the correct time.
2512 mAudioStartTime = mStartTime;
2513 LOG(PR_LOG_DEBUG, ("%p Media start time is %lld", mDecoder.get(), mStartTime));
2514 return v;
2517 void MediaDecoderStateMachine::UpdateReadyState() {
2518 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2520 MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus();
2521 if (nextFrameStatus == mLastFrameStatus) {
2522 return;
2524 mLastFrameStatus = nextFrameStatus;
2526 nsCOMPtr<nsIRunnable> event;
2527 switch (nextFrameStatus) {
2528 case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING:
2529 event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailableBuffering);
2530 break;
2531 case MediaDecoderOwner::NEXT_FRAME_AVAILABLE:
2532 event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameAvailable);
2533 break;
2534 case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE:
2535 event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailable);
2536 break;
2537 default:
2538 PR_NOT_REACHED("unhandled frame state");
2541 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
2544 bool MediaDecoderStateMachine::JustExitedQuickBuffering()
2546 return !mDecodeStartTime.IsNull() &&
2547 mQuickBuffering &&
2548 (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS);
2551 void MediaDecoderStateMachine::StartBuffering()
2553 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2555 if (IsPlaying()) {
2556 StopPlayback();
2559 TimeDuration decodeDuration = TimeStamp::Now() - mDecodeStartTime;
2560 // Go into quick buffering mode provided we've not just left buffering using
2561 // a "quick exit". This stops us flip-flopping between playing and buffering
2562 // when the download speed is similar to the decode speed.
2563 mQuickBuffering =
2564 !JustExitedQuickBuffering() &&
2565 decodeDuration < UsecsToDuration(QUICK_BUFFER_THRESHOLD_USECS);
2566 mBufferingStart = TimeStamp::Now();
2568 // We need to tell the element that buffering has started.
2569 // We can't just directly send an asynchronous runnable that
2570 // eventually fires the "waiting" event. The problem is that
2571 // there might be pending main-thread events, such as "data
2572 // received" notifications, that mean we're not actually still
2573 // buffering by the time this runnable executes. So instead
2574 // we just trigger UpdateReadyStateForData; when it runs, it
2575 // will check the current state and decide whether to tell
2576 // the element we're buffering or not.
2577 UpdateReadyState();
2578 mState = DECODER_STATE_BUFFERING;
2579 LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING to BUFFERING, decoded for %.3lfs",
2580 mDecoder.get(), decodeDuration.ToSeconds()));
2581 #ifdef PR_LOGGING
2582 MediaDecoder::Statistics stats = mDecoder->GetStatistics();
2583 #endif
2584 LOG(PR_LOG_DEBUG, ("%p Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
2585 mDecoder.get(),
2586 stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
2587 stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"));
2590 nsresult MediaDecoderStateMachine::GetBuffered(nsTimeRanges* aBuffered) {
2591 MediaResource* resource = mDecoder->GetResource();
2592 NS_ENSURE_TRUE(resource, NS_ERROR_FAILURE);
2593 resource->Pin();
2594 nsresult res = mReader->GetBuffered(aBuffered, mStartTime);
2595 resource->Unpin();
2596 return res;
2599 bool MediaDecoderStateMachine::IsPausedAndDecoderWaiting() {
2600 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2601 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
2603 return
2604 mDecodeThreadWaiting &&
2605 mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING &&
2606 (mState == DECODER_STATE_DECODING || mState == DECODER_STATE_BUFFERING);
2609 nsresult MediaDecoderStateMachine::Run()
2611 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
2612 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
2614 return CallRunStateMachine();
2617 nsresult MediaDecoderStateMachine::CallRunStateMachine()
2619 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2620 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread.");
2621 // This will be set to true by ScheduleStateMachine() if it's called
2622 // while we're in RunStateMachine().
2623 mRunAgain = false;
2625 // Set to true whenever we dispatch an event to run this state machine.
2626 // This flag prevents us from dispatching
2627 mDispatchedRunEvent = false;
2629 // If audio is being captured, stop the audio thread if it's running
2630 if (mAudioCaptured) {
2631 StopAudioThread();
2634 mTimeout = TimeStamp();
2636 mIsRunning = true;
2637 nsresult res = RunStateMachine();
2638 mIsRunning = false;
2640 if (mRunAgain && !mDispatchedRunEvent) {
2641 mDispatchedRunEvent = true;
2642 return NS_DispatchToCurrentThread(this);
2645 return res;
2648 static void TimeoutExpired(nsITimer *aTimer, void *aClosure) {
2649 MediaDecoderStateMachine *machine =
2650 static_cast<MediaDecoderStateMachine*>(aClosure);
2651 NS_ASSERTION(machine, "Must have been passed state machine");
2652 machine->TimeoutExpired();
2655 void MediaDecoderStateMachine::TimeoutExpired()
2657 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
2658 NS_ASSERTION(OnStateMachineThread(), "Must be on state machine thread");
2659 if (mIsRunning) {
2660 mRunAgain = true;
2661 } else if (!mDispatchedRunEvent) {
2662 // We don't have an event dispatched to run the state machine, so we
2663 // can just run it from here.
2664 CallRunStateMachine();
2666 // Otherwise, an event has already been dispatched to run the state machine
2667 // as soon as possible. Nothing else needed to do, the state machine is
2668 // going to run anyway.
2671 void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
2672 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
2673 mon.NotifyAll();
2674 ScheduleStateMachine();
2677 nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
2678 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2679 NS_ABORT_IF_FALSE(GetStateMachineThread(),
2680 "Must have a state machine thread to schedule");
2682 if (mState == DECODER_STATE_SHUTDOWN) {
2683 return NS_ERROR_FAILURE;
2685 aUsecs = std::max<int64_t>(aUsecs, 0);
2687 TimeStamp timeout = TimeStamp::Now() + UsecsToDuration(aUsecs);
2688 if (!mTimeout.IsNull()) {
2689 if (timeout >= mTimeout) {
2690 // We've already scheduled a timer set to expire at or before this time,
2691 // or have an event dispatched to run the state machine.
2692 return NS_OK;
2694 if (mTimer) {
2695 // We've been asked to schedule a timer to run before an existing timer.
2696 // Cancel the existing timer.
2697 mTimer->Cancel();
2701 uint32_t ms = static_cast<uint32_t>((aUsecs / USECS_PER_MS) & 0xFFFFFFFF);
2702 if (mRealTime && ms > 40)
2703 ms = 40;
2704 if (ms == 0) {
2705 if (mIsRunning) {
2706 // We're currently running this state machine on the state machine
2707 // thread. Signal it to run again once it finishes its current cycle.
2708 mRunAgain = true;
2709 return NS_OK;
2710 } else if (!mDispatchedRunEvent) {
2711 // We're not currently running this state machine on the state machine
2712 // thread. Dispatch an event to run one cycle of the state machine.
2713 mDispatchedRunEvent = true;
2714 return GetStateMachineThread()->Dispatch(this, NS_DISPATCH_NORMAL);
2716 // We're not currently running this state machine on the state machine
2717 // thread, but something has already dispatched an event to run it again,
2718 // so just exit; it's going to run real soon.
2719 return NS_OK;
2722 mTimeout = timeout;
2724 nsresult res;
2725 if (!mTimer) {
2726 mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
2727 if (NS_FAILED(res)) return res;
2728 mTimer->SetTarget(GetStateMachineThread());
2731 res = mTimer->InitWithFuncCallback(mozilla::TimeoutExpired,
2732 this,
2734 nsITimer::TYPE_ONE_SHOT);
2735 return res;
2738 bool MediaDecoderStateMachine::OnStateMachineThread() const
2740 return IsCurrentThread(GetStateMachineThread());
2743 nsIThread* MediaDecoderStateMachine::GetStateMachineThread()
2745 return StateMachineTracker::Instance().GetGlobalStateMachineThread();
2748 void MediaDecoderStateMachine::NotifyAudioAvailableListener()
2750 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2751 mEventManager.NotifyAudioAvailableListener();
2754 void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate)
2756 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
2757 NS_ASSERTION(aPlaybackRate != 0,
2758 "PlaybackRate == 0 should be handled before this function.");
2759 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
2761 if (mPlaybackRate == aPlaybackRate) {
2762 return;
2765 // Get position of the last time we changed the rate.
2766 if (!HasAudio()) {
2767 // mBasePosition is a position in the video stream, not an absolute time.
2768 if (mState == DECODER_STATE_SEEKING) {
2769 mBasePosition = mSeekTime;
2770 } else {
2771 mBasePosition = GetVideoStreamPosition();
2773 mPlayDuration = mBasePosition - mStartTime;
2774 mResetPlayStartTime = true;
2775 mPlayStartTime = TimeStamp::Now();
2778 mPlaybackRate = aPlaybackRate;
2781 void MediaDecoderStateMachine::SetPreservesPitch(bool aPreservesPitch)
2783 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
2784 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
2786 mPreservesPitch = aPreservesPitch;
2788 return;
2791 bool MediaDecoderStateMachine::IsShutdown()
2793 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2794 return GetState() == DECODER_STATE_SHUTDOWN;
2797 void MediaDecoderStateMachine::QueueMetadata(int64_t aPublishTime,
2798 int aChannels,
2799 int aRate,
2800 bool aHasAudio,
2801 bool aHasVideo,
2802 MetadataTags* aTags)
2804 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
2805 mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
2806 TimedMetadata* metadata = new TimedMetadata;
2807 metadata->mPublishTime = aPublishTime;
2808 metadata->mChannels = aChannels;
2809 metadata->mRate = aRate;
2810 metadata->mHasAudio = aHasAudio;
2811 metadata->mTags = aTags;
2812 mMetadataManager.QueueMetadata(metadata);
2815 } // namespace mozilla