|
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/. */ |
|
5 |
|
6 #ifdef XP_WIN |
|
7 // Include Windows headers required for enabling high precision timers. |
|
8 #include "windows.h" |
|
9 #include "mmsystem.h" |
|
10 #endif |
|
11 |
|
12 #include "mozilla/DebugOnly.h" |
|
13 #include <stdint.h> |
|
14 |
|
15 #include "MediaDecoderStateMachine.h" |
|
16 #include "AudioStream.h" |
|
17 #include "nsTArray.h" |
|
18 #include "MediaDecoder.h" |
|
19 #include "MediaDecoderReader.h" |
|
20 #include "mozilla/mozalloc.h" |
|
21 #include "VideoUtils.h" |
|
22 #include "mozilla/dom/TimeRanges.h" |
|
23 #include "nsDeque.h" |
|
24 #include "AudioSegment.h" |
|
25 #include "VideoSegment.h" |
|
26 #include "ImageContainer.h" |
|
27 #include "nsComponentManagerUtils.h" |
|
28 #include "nsITimer.h" |
|
29 #include "nsContentUtils.h" |
|
30 #include "MediaShutdownManager.h" |
|
31 #include "SharedThreadPool.h" |
|
32 #include "MediaTaskQueue.h" |
|
33 #include "nsIEventTarget.h" |
|
34 #include "prenv.h" |
|
35 #include "mozilla/Preferences.h" |
|
36 #include "gfx2DGlue.h" |
|
37 |
|
38 #include <algorithm> |
|
39 |
|
40 namespace mozilla { |
|
41 |
|
42 using namespace mozilla::layers; |
|
43 using namespace mozilla::dom; |
|
44 using namespace mozilla::gfx; |
|
45 |
|
46 // avoid redefined macro in unified build |
|
47 #undef DECODER_LOG |
|
48 #undef VERBOSE_LOG |
|
49 |
|
50 #ifdef PR_LOGGING |
|
51 extern PRLogModuleInfo* gMediaDecoderLog; |
|
52 #define DECODER_LOG(type, msg, ...) \ |
|
53 PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, mDecoder.get(), ##__VA_ARGS__)) |
|
54 #define VERBOSE_LOG(msg, ...) \ |
|
55 PR_BEGIN_MACRO \ |
|
56 if (!PR_GetEnv("MOZ_QUIET")) { \ |
|
57 DECODER_LOG(PR_LOG_DEBUG, msg, ##__VA_ARGS__); \ |
|
58 } \ |
|
59 PR_END_MACRO |
|
60 #else |
|
61 #define DECODER_LOG(type, msg, ...) |
|
62 #define VERBOSE_LOG(msg, ...) |
|
63 #endif |
|
64 |
|
65 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to |
|
66 // GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime |
|
67 // implementation. With unified builds, putting this in headers is not enough. |
|
68 #ifdef GetCurrentTime |
|
69 #undef GetCurrentTime |
|
70 #endif |
|
71 |
|
72 // Wait this number of seconds when buffering, then leave and play |
|
73 // as best as we can if the required amount of data hasn't been |
|
74 // retrieved. |
|
75 static const uint32_t BUFFERING_WAIT_S = 30; |
|
76 |
|
77 // If audio queue has less than this many usecs of decoded audio, we won't risk |
|
78 // trying to decode the video, we'll skip decoding video up to the next |
|
79 // keyframe. We may increase this value for an individual decoder if we |
|
80 // encounter video frames which take a long time to decode. |
|
81 static const uint32_t LOW_AUDIO_USECS = 300000; |
|
82 |
|
83 // If more than this many usecs of decoded audio is queued, we'll hold off |
|
84 // decoding more audio. If we increase the low audio threshold (see |
|
85 // LOW_AUDIO_USECS above) we'll also increase this value to ensure it's not |
|
86 // less than the low audio threshold. |
|
87 const int64_t AMPLE_AUDIO_USECS = 1000000; |
|
88 |
|
89 // When we're only playing audio and we don't have a video stream, we divide |
|
90 // AMPLE_AUDIO_USECS and LOW_AUDIO_USECS by the following value. This reduces |
|
91 // the amount of decoded audio we buffer, reducing our memory usage. We only |
|
92 // need to decode far ahead when we're decoding video using software decoding, |
|
93 // as otherwise a long video decode could cause an audio underrun. |
|
94 const int64_t NO_VIDEO_AMPLE_AUDIO_DIVISOR = 8; |
|
95 |
|
96 // Maximum number of bytes we'll allocate and write at once to the audio |
|
97 // hardware when the audio stream contains missing frames and we're |
|
98 // writing silence in order to fill the gap. We limit our silence-writes |
|
99 // to 32KB in order to avoid allocating an impossibly large chunk of |
|
100 // memory if we encounter a large chunk of silence. |
|
101 const uint32_t SILENCE_BYTES_CHUNK = 32 * 1024; |
|
102 |
|
103 // If we have fewer than LOW_VIDEO_FRAMES decoded frames, and |
|
104 // we're not "prerolling video", we'll skip the video up to the next keyframe |
|
105 // which is at or after the current playback position. |
|
106 static const uint32_t LOW_VIDEO_FRAMES = 1; |
|
107 |
|
108 // Arbitrary "frame duration" when playing only audio. |
|
109 static const int AUDIO_DURATION_USECS = 40000; |
|
110 |
|
111 // If we increase our "low audio threshold" (see LOW_AUDIO_USECS above), we |
|
112 // use this as a factor in all our calculations. Increasing this will cause |
|
113 // us to be more likely to increase our low audio threshold, and to |
|
114 // increase it by more. |
|
115 static const int THRESHOLD_FACTOR = 2; |
|
116 |
|
117 // If we have less than this much undecoded data available, we'll consider |
|
118 // ourselves to be running low on undecoded data. We determine how much |
|
119 // undecoded data we have remaining using the reader's GetBuffered() |
|
120 // implementation. |
|
121 static const int64_t LOW_DATA_THRESHOLD_USECS = 5000000; |
|
122 |
|
123 // LOW_DATA_THRESHOLD_USECS needs to be greater than AMPLE_AUDIO_USECS, otherwise |
|
124 // the skip-to-keyframe logic can activate when we're running low on data. |
|
125 static_assert(LOW_DATA_THRESHOLD_USECS > AMPLE_AUDIO_USECS, |
|
126 "LOW_DATA_THRESHOLD_USECS is too small"); |
|
127 |
|
128 // Amount of excess usecs of data to add in to the "should we buffer" calculation. |
|
129 static const uint32_t EXHAUSTED_DATA_MARGIN_USECS = 60000; |
|
130 |
|
131 // If we enter buffering within QUICK_BUFFER_THRESHOLD_USECS seconds of starting |
|
132 // decoding, we'll enter "quick buffering" mode, which exits a lot sooner than |
|
133 // normal buffering mode. This exists so that if the decode-ahead exhausts the |
|
134 // downloaded data while decode/playback is just starting up (for example |
|
135 // after a seek while the media is still playing, or when playing a media |
|
136 // as soon as it's load started), we won't necessarily stop for 30s and wait |
|
137 // for buffering. We may actually be able to playback in this case, so exit |
|
138 // buffering early and try to play. If it turns out we can't play, we'll fall |
|
139 // back to buffering normally. |
|
140 static const uint32_t QUICK_BUFFER_THRESHOLD_USECS = 2000000; |
|
141 |
|
142 // If we're quick buffering, we'll remain in buffering mode while we have less than |
|
143 // QUICK_BUFFERING_LOW_DATA_USECS of decoded data available. |
|
144 static const uint32_t QUICK_BUFFERING_LOW_DATA_USECS = 1000000; |
|
145 |
|
146 // If QUICK_BUFFERING_LOW_DATA_USECS is > AMPLE_AUDIO_USECS, we won't exit |
|
147 // quick buffering in a timely fashion, as the decode pauses when it |
|
148 // reaches AMPLE_AUDIO_USECS decoded data, and thus we'll never reach |
|
149 // QUICK_BUFFERING_LOW_DATA_USECS. |
|
150 static_assert(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS, |
|
151 "QUICK_BUFFERING_LOW_DATA_USECS is too large"); |
|
152 |
|
153 // This value has been chosen empirically. |
|
154 static const uint32_t AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS = 200000; |
|
155 |
|
156 // The amount of instability we tollerate in calls to |
|
157 // MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration |
|
158 // less than this are ignored, as they're assumed to be the result of |
|
159 // instability in the duration estimation. |
|
160 static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2; |
|
161 |
|
162 static TimeDuration UsecsToDuration(int64_t aUsecs) { |
|
163 return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS); |
|
164 } |
|
165 |
|
166 static int64_t DurationToUsecs(TimeDuration aDuration) { |
|
167 return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S); |
|
168 } |
|
169 |
|
170 MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder, |
|
171 MediaDecoderReader* aReader, |
|
172 bool aRealTime) : |
|
173 mDecoder(aDecoder), |
|
174 mState(DECODER_STATE_DECODING_METADATA), |
|
175 mInRunningStateMachine(false), |
|
176 mSyncPointInMediaStream(-1), |
|
177 mSyncPointInDecodedStream(-1), |
|
178 mResetPlayStartTime(false), |
|
179 mPlayDuration(0), |
|
180 mStartTime(-1), |
|
181 mEndTime(-1), |
|
182 mFragmentEndTime(-1), |
|
183 mReader(aReader), |
|
184 mCurrentFrameTime(0), |
|
185 mAudioStartTime(-1), |
|
186 mAudioEndTime(-1), |
|
187 mVideoFrameEndTime(-1), |
|
188 mVolume(1.0), |
|
189 mPlaybackRate(1.0), |
|
190 mPreservesPitch(true), |
|
191 mBasePosition(0), |
|
192 mAmpleVideoFrames(2), |
|
193 mLowAudioThresholdUsecs(LOW_AUDIO_USECS), |
|
194 mAmpleAudioThresholdUsecs(AMPLE_AUDIO_USECS), |
|
195 mDispatchedAudioDecodeTask(false), |
|
196 mDispatchedVideoDecodeTask(false), |
|
197 mIsReaderIdle(false), |
|
198 mAudioCaptured(false), |
|
199 mTransportSeekable(true), |
|
200 mMediaSeekable(true), |
|
201 mPositionChangeQueued(false), |
|
202 mAudioCompleted(false), |
|
203 mGotDurationFromMetaData(false), |
|
204 mDispatchedEventToDecode(false), |
|
205 mStopAudioThread(true), |
|
206 mQuickBuffering(false), |
|
207 mMinimizePreroll(false), |
|
208 mDecodeThreadWaiting(false), |
|
209 mRealTime(aRealTime), |
|
210 mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED), |
|
211 mTimerId(0) |
|
212 { |
|
213 MOZ_COUNT_CTOR(MediaDecoderStateMachine); |
|
214 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
215 |
|
216 // Only enable realtime mode when "media.realtime_decoder.enabled" is true. |
|
217 if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false) |
|
218 mRealTime = false; |
|
219 |
|
220 mAmpleVideoFrames = |
|
221 std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3); |
|
222 |
|
223 mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT_S; |
|
224 mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS; |
|
225 |
|
226 mVideoPrerollFrames = mRealTime ? 0 : mAmpleVideoFrames / 2; |
|
227 mAudioPrerollUsecs = mRealTime ? 0 : LOW_AUDIO_USECS * 2; |
|
228 |
|
229 #ifdef XP_WIN |
|
230 // Ensure high precision timers are enabled on Windows, otherwise the state |
|
231 // machine thread isn't woken up at reliable intervals to set the next frame, |
|
232 // and we drop frames while painting. Note that multiple calls to this |
|
233 // function per-process is OK, provided each call is matched by a corresponding |
|
234 // timeEndPeriod() call. |
|
235 timeBeginPeriod(1); |
|
236 #endif |
|
237 } |
|
238 |
|
239 MediaDecoderStateMachine::~MediaDecoderStateMachine() |
|
240 { |
|
241 MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread."); |
|
242 MOZ_COUNT_DTOR(MediaDecoderStateMachine); |
|
243 NS_ASSERTION(!mPendingWakeDecoder.get(), |
|
244 "WakeDecoder should have been revoked already"); |
|
245 |
|
246 MOZ_ASSERT(!mDecodeTaskQueue, "Should be released in SHUTDOWN"); |
|
247 // No need to cancel the timer here for we've done that in SHUTDOWN. |
|
248 MOZ_ASSERT(!mTimer, "Should be released in SHUTDOWN"); |
|
249 mReader = nullptr; |
|
250 |
|
251 #ifdef XP_WIN |
|
252 timeEndPeriod(1); |
|
253 #endif |
|
254 } |
|
255 |
|
256 bool MediaDecoderStateMachine::HasFutureAudio() { |
|
257 AssertCurrentThreadInMonitor(); |
|
258 NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio"); |
|
259 // We've got audio ready to play if: |
|
260 // 1. We've not completed playback of audio, and |
|
261 // 2. we either have more than the threshold of decoded audio available, or |
|
262 // we've completely decoded all audio (but not finished playing it yet |
|
263 // as per 1). |
|
264 return !mAudioCompleted && |
|
265 (AudioDecodedUsecs() > LOW_AUDIO_USECS * mPlaybackRate || AudioQueue().IsFinished()); |
|
266 } |
|
267 |
|
268 bool MediaDecoderStateMachine::HaveNextFrameData() { |
|
269 AssertCurrentThreadInMonitor(); |
|
270 return (!HasAudio() || HasFutureAudio()) && |
|
271 (!HasVideo() || VideoQueue().GetSize() > 0); |
|
272 } |
|
273 |
|
274 int64_t MediaDecoderStateMachine::GetDecodedAudioDuration() { |
|
275 NS_ASSERTION(OnDecodeThread() || OnStateMachineThread(), |
|
276 "Should be on decode thread or state machine thread"); |
|
277 AssertCurrentThreadInMonitor(); |
|
278 int64_t audioDecoded = AudioQueue().Duration(); |
|
279 if (mAudioEndTime != -1) { |
|
280 audioDecoded += mAudioEndTime - GetMediaTime(); |
|
281 } |
|
282 return audioDecoded; |
|
283 } |
|
284 |
|
285 void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio, |
|
286 DecodedStreamData* aStream, |
|
287 AudioSegment* aOutput) |
|
288 { |
|
289 NS_ASSERTION(OnDecodeThread() || |
|
290 OnStateMachineThread(), "Should be on decode thread or state machine thread"); |
|
291 AssertCurrentThreadInMonitor(); |
|
292 |
|
293 if (aAudio->mTime <= aStream->mLastAudioPacketTime) { |
|
294 // ignore packet that we've already processed |
|
295 return; |
|
296 } |
|
297 aStream->mLastAudioPacketTime = aAudio->mTime; |
|
298 aStream->mLastAudioPacketEndTime = aAudio->GetEndTime(); |
|
299 |
|
300 // This logic has to mimic AudioLoop closely to make sure we write |
|
301 // the exact same silences |
|
302 CheckedInt64 audioWrittenOffset = UsecsToFrames(mInfo.mAudio.mRate, |
|
303 aStream->mInitialTime + mStartTime) + aStream->mAudioFramesWritten; |
|
304 CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudio.mRate, aAudio->mTime); |
|
305 if (!audioWrittenOffset.isValid() || !frameOffset.isValid()) |
|
306 return; |
|
307 if (audioWrittenOffset.value() < frameOffset.value()) { |
|
308 // Write silence to catch up |
|
309 VERBOSE_LOG("writing %d frames of silence to MediaStream", |
|
310 int32_t(frameOffset.value() - audioWrittenOffset.value())); |
|
311 AudioSegment silence; |
|
312 silence.InsertNullDataAtStart(frameOffset.value() - audioWrittenOffset.value()); |
|
313 aStream->mAudioFramesWritten += silence.GetDuration(); |
|
314 aOutput->AppendFrom(&silence); |
|
315 } |
|
316 |
|
317 int64_t offset; |
|
318 if (aStream->mAudioFramesWritten == 0) { |
|
319 NS_ASSERTION(frameOffset.value() <= audioWrittenOffset.value(), |
|
320 "Otherwise we'd have taken the write-silence path"); |
|
321 // We're starting in the middle of a packet. Split the packet. |
|
322 offset = audioWrittenOffset.value() - frameOffset.value(); |
|
323 } else { |
|
324 // Write the entire packet. |
|
325 offset = 0; |
|
326 } |
|
327 |
|
328 if (offset >= aAudio->mFrames) |
|
329 return; |
|
330 |
|
331 aAudio->EnsureAudioBuffer(); |
|
332 nsRefPtr<SharedBuffer> buffer = aAudio->mAudioBuffer; |
|
333 AudioDataValue* bufferData = static_cast<AudioDataValue*>(buffer->Data()); |
|
334 nsAutoTArray<const AudioDataValue*,2> channels; |
|
335 for (uint32_t i = 0; i < aAudio->mChannels; ++i) { |
|
336 channels.AppendElement(bufferData + i*aAudio->mFrames + offset); |
|
337 } |
|
338 aOutput->AppendFrames(buffer.forget(), channels, aAudio->mFrames); |
|
339 VERBOSE_LOG("writing %d frames of data to MediaStream for AudioData at %lld", |
|
340 aAudio->mFrames - int32_t(offset), aAudio->mTime); |
|
341 aStream->mAudioFramesWritten += aAudio->mFrames - int32_t(offset); |
|
342 } |
|
343 |
|
344 static void WriteVideoToMediaStream(layers::Image* aImage, |
|
345 int64_t aDuration, |
|
346 const IntSize& aIntrinsicSize, |
|
347 VideoSegment* aOutput) |
|
348 { |
|
349 nsRefPtr<layers::Image> image = aImage; |
|
350 aOutput->AppendFrame(image.forget(), aDuration, aIntrinsicSize); |
|
351 } |
|
352 |
|
353 static const TrackID TRACK_AUDIO = 1; |
|
354 static const TrackID TRACK_VIDEO = 2; |
|
355 static const TrackRate RATE_VIDEO = USECS_PER_S; |
|
356 |
|
357 void MediaDecoderStateMachine::SendStreamData() |
|
358 { |
|
359 NS_ASSERTION(OnDecodeThread() || |
|
360 OnStateMachineThread(), "Should be on decode thread or state machine thread"); |
|
361 AssertCurrentThreadInMonitor(); |
|
362 |
|
363 DecodedStreamData* stream = mDecoder->GetDecodedStream(); |
|
364 if (!stream) |
|
365 return; |
|
366 |
|
367 if (mState == DECODER_STATE_DECODING_METADATA) |
|
368 return; |
|
369 |
|
370 // If there's still an audio thread alive, then we can't send any stream |
|
371 // data yet since both SendStreamData and the audio thread want to be in |
|
372 // charge of popping the audio queue. We're waiting for the audio thread |
|
373 // to die before sending anything to our stream. |
|
374 if (mAudioThread) |
|
375 return; |
|
376 |
|
377 int64_t minLastAudioPacketTime = INT64_MAX; |
|
378 bool finished = |
|
379 (!mInfo.HasAudio() || AudioQueue().IsFinished()) && |
|
380 (!mInfo.HasVideo() || VideoQueue().IsFinished()); |
|
381 if (mDecoder->IsSameOriginMedia()) { |
|
382 SourceMediaStream* mediaStream = stream->mStream; |
|
383 StreamTime endPosition = 0; |
|
384 |
|
385 if (!stream->mStreamInitialized) { |
|
386 if (mInfo.HasAudio()) { |
|
387 AudioSegment* audio = new AudioSegment(); |
|
388 mediaStream->AddTrack(TRACK_AUDIO, mInfo.mAudio.mRate, 0, audio); |
|
389 stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_AUDIO, |
|
390 GetStateMachineThread(), GetWakeDecoderRunnable()); |
|
391 } |
|
392 if (mInfo.HasVideo()) { |
|
393 VideoSegment* video = new VideoSegment(); |
|
394 mediaStream->AddTrack(TRACK_VIDEO, RATE_VIDEO, 0, video); |
|
395 stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_VIDEO, |
|
396 GetStateMachineThread(), GetWakeDecoderRunnable()); |
|
397 } |
|
398 stream->mStreamInitialized = true; |
|
399 } |
|
400 |
|
401 if (mInfo.HasAudio()) { |
|
402 nsAutoTArray<AudioData*,10> audio; |
|
403 // It's OK to hold references to the AudioData because while audio |
|
404 // is captured, only the decoder thread pops from the queue (see below). |
|
405 AudioQueue().GetElementsAfter(stream->mLastAudioPacketTime, &audio); |
|
406 AudioSegment output; |
|
407 for (uint32_t i = 0; i < audio.Length(); ++i) { |
|
408 SendStreamAudio(audio[i], stream, &output); |
|
409 } |
|
410 if (output.GetDuration() > 0) { |
|
411 mediaStream->AppendToTrack(TRACK_AUDIO, &output); |
|
412 } |
|
413 if (AudioQueue().IsFinished() && !stream->mHaveSentFinishAudio) { |
|
414 mediaStream->EndTrack(TRACK_AUDIO); |
|
415 stream->mHaveSentFinishAudio = true; |
|
416 } |
|
417 minLastAudioPacketTime = std::min(minLastAudioPacketTime, stream->mLastAudioPacketTime); |
|
418 endPosition = std::max(endPosition, |
|
419 TicksToTimeRoundDown(mInfo.mAudio.mRate, stream->mAudioFramesWritten)); |
|
420 } |
|
421 |
|
422 if (mInfo.HasVideo()) { |
|
423 nsAutoTArray<VideoData*,10> video; |
|
424 // It's OK to hold references to the VideoData only the decoder thread |
|
425 // pops from the queue. |
|
426 VideoQueue().GetElementsAfter(stream->mNextVideoTime, &video); |
|
427 VideoSegment output; |
|
428 for (uint32_t i = 0; i < video.Length(); ++i) { |
|
429 VideoData* v = video[i]; |
|
430 if (stream->mNextVideoTime < v->mTime) { |
|
431 VERBOSE_LOG("writing last video to MediaStream %p for %lldus", |
|
432 mediaStream, v->mTime - stream->mNextVideoTime); |
|
433 // Write last video frame to catch up. mLastVideoImage can be null here |
|
434 // which is fine, it just means there's no video. |
|
435 WriteVideoToMediaStream(stream->mLastVideoImage, |
|
436 v->mTime - stream->mNextVideoTime, stream->mLastVideoImageDisplaySize, |
|
437 &output); |
|
438 stream->mNextVideoTime = v->mTime; |
|
439 } |
|
440 if (stream->mNextVideoTime < v->GetEndTime()) { |
|
441 VERBOSE_LOG("writing video frame %lldus to MediaStream %p for %lldus", |
|
442 v->mTime, mediaStream, v->GetEndTime() - stream->mNextVideoTime); |
|
443 WriteVideoToMediaStream(v->mImage, |
|
444 v->GetEndTime() - stream->mNextVideoTime, v->mDisplay, |
|
445 &output); |
|
446 stream->mNextVideoTime = v->GetEndTime(); |
|
447 stream->mLastVideoImage = v->mImage; |
|
448 stream->mLastVideoImageDisplaySize = v->mDisplay; |
|
449 } else { |
|
450 VERBOSE_LOG("skipping writing video frame %lldus (end %lldus) to MediaStream", |
|
451 v->mTime, v->GetEndTime()); |
|
452 } |
|
453 } |
|
454 if (output.GetDuration() > 0) { |
|
455 mediaStream->AppendToTrack(TRACK_VIDEO, &output); |
|
456 } |
|
457 if (VideoQueue().IsFinished() && !stream->mHaveSentFinishVideo) { |
|
458 mediaStream->EndTrack(TRACK_VIDEO); |
|
459 stream->mHaveSentFinishVideo = true; |
|
460 } |
|
461 endPosition = std::max(endPosition, |
|
462 TicksToTimeRoundDown(RATE_VIDEO, stream->mNextVideoTime - stream->mInitialTime)); |
|
463 } |
|
464 |
|
465 if (!stream->mHaveSentFinish) { |
|
466 stream->mStream->AdvanceKnownTracksTime(endPosition); |
|
467 } |
|
468 |
|
469 if (finished && !stream->mHaveSentFinish) { |
|
470 stream->mHaveSentFinish = true; |
|
471 stream->mStream->Finish(); |
|
472 } |
|
473 } |
|
474 |
|
475 if (mAudioCaptured) { |
|
476 // Discard audio packets that are no longer needed. |
|
477 while (true) { |
|
478 const AudioData* a = AudioQueue().PeekFront(); |
|
479 // Packet times are not 100% reliable so this may discard packets that |
|
480 // actually contain data for mCurrentFrameTime. This means if someone might |
|
481 // create a new output stream and we actually don't have the audio for the |
|
482 // very start. That's OK, we'll play silence instead for a brief moment. |
|
483 // That's OK. Seeking to this time would have a similar issue for such |
|
484 // badly muxed resources. |
|
485 if (!a || a->GetEndTime() >= minLastAudioPacketTime) |
|
486 break; |
|
487 mAudioEndTime = std::max(mAudioEndTime, a->GetEndTime()); |
|
488 delete AudioQueue().PopFront(); |
|
489 } |
|
490 |
|
491 if (finished) { |
|
492 mAudioCompleted = true; |
|
493 UpdateReadyState(); |
|
494 } |
|
495 } |
|
496 } |
|
497 |
|
498 MediaDecoderStateMachine::WakeDecoderRunnable* |
|
499 MediaDecoderStateMachine::GetWakeDecoderRunnable() |
|
500 { |
|
501 AssertCurrentThreadInMonitor(); |
|
502 |
|
503 if (!mPendingWakeDecoder.get()) { |
|
504 mPendingWakeDecoder = new WakeDecoderRunnable(this); |
|
505 } |
|
506 return mPendingWakeDecoder.get(); |
|
507 } |
|
508 |
|
509 bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs) |
|
510 { |
|
511 AssertCurrentThreadInMonitor(); |
|
512 |
|
513 if (AudioQueue().GetSize() == 0 || |
|
514 GetDecodedAudioDuration() < aAmpleAudioUSecs) { |
|
515 return false; |
|
516 } |
|
517 if (!mAudioCaptured) { |
|
518 return true; |
|
519 } |
|
520 |
|
521 DecodedStreamData* stream = mDecoder->GetDecodedStream(); |
|
522 if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishAudio) { |
|
523 if (!stream->mStream->HaveEnoughBuffered(TRACK_AUDIO)) { |
|
524 return false; |
|
525 } |
|
526 stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_AUDIO, |
|
527 GetStateMachineThread(), GetWakeDecoderRunnable()); |
|
528 } |
|
529 |
|
530 return true; |
|
531 } |
|
532 |
|
533 bool MediaDecoderStateMachine::HaveEnoughDecodedVideo() |
|
534 { |
|
535 AssertCurrentThreadInMonitor(); |
|
536 |
|
537 if (static_cast<uint32_t>(VideoQueue().GetSize()) < mAmpleVideoFrames * mPlaybackRate) { |
|
538 return false; |
|
539 } |
|
540 |
|
541 DecodedStreamData* stream = mDecoder->GetDecodedStream(); |
|
542 if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishVideo) { |
|
543 if (!stream->mStream->HaveEnoughBuffered(TRACK_VIDEO)) { |
|
544 return false; |
|
545 } |
|
546 stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_VIDEO, |
|
547 GetStateMachineThread(), GetWakeDecoderRunnable()); |
|
548 } |
|
549 |
|
550 return true; |
|
551 } |
|
552 |
|
553 bool |
|
554 MediaDecoderStateMachine::NeedToDecodeVideo() |
|
555 { |
|
556 AssertCurrentThreadInMonitor(); |
|
557 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
558 "Should be on state machine or decode thread."); |
|
559 return mIsVideoDecoding && |
|
560 !mMinimizePreroll && |
|
561 !HaveEnoughDecodedVideo(); |
|
562 } |
|
563 |
|
564 void |
|
565 MediaDecoderStateMachine::DecodeVideo() |
|
566 { |
|
567 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
568 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); |
|
569 |
|
570 if (mState != DECODER_STATE_DECODING && mState != DECODER_STATE_BUFFERING) { |
|
571 mDispatchedVideoDecodeTask = false; |
|
572 return; |
|
573 } |
|
574 EnsureActive(); |
|
575 |
|
576 // We don't want to consider skipping to the next keyframe if we've |
|
577 // only just started up the decode loop, so wait until we've decoded |
|
578 // some frames before enabling the keyframe skip logic on video. |
|
579 if (mIsVideoPrerolling && |
|
580 (static_cast<uint32_t>(VideoQueue().GetSize()) |
|
581 >= mVideoPrerollFrames * mPlaybackRate)) |
|
582 { |
|
583 mIsVideoPrerolling = false; |
|
584 } |
|
585 |
|
586 // We'll skip the video decode to the nearest keyframe if we're low on |
|
587 // audio, or if we're low on video, provided we're not running low on |
|
588 // data to decode. If we're running low on downloaded data to decode, |
|
589 // we won't start keyframe skipping, as we'll be pausing playback to buffer |
|
590 // soon anyway and we'll want to be able to display frames immediately |
|
591 // after buffering finishes. |
|
592 if (mState == DECODER_STATE_DECODING && |
|
593 !mSkipToNextKeyFrame && |
|
594 mIsVideoDecoding && |
|
595 ((!mIsAudioPrerolling && mIsAudioDecoding && |
|
596 GetDecodedAudioDuration() < mLowAudioThresholdUsecs * mPlaybackRate) || |
|
597 (!mIsVideoPrerolling && mIsVideoDecoding && |
|
598 // don't skip frame when |clock time| <= |mVideoFrameEndTime| for |
|
599 // we are still in the safe range without underrunning video frames |
|
600 GetClock() > mVideoFrameEndTime && |
|
601 (static_cast<uint32_t>(VideoQueue().GetSize()) |
|
602 < LOW_VIDEO_FRAMES * mPlaybackRate))) && |
|
603 !HasLowUndecodedData()) |
|
604 { |
|
605 mSkipToNextKeyFrame = true; |
|
606 DECODER_LOG(PR_LOG_DEBUG, "Skipping video decode to the next keyframe"); |
|
607 } |
|
608 |
|
609 // Time the video decode, so that if it's slow, we can increase our low |
|
610 // audio threshold to reduce the chance of an audio underrun while we're |
|
611 // waiting for a video decode to complete. |
|
612 TimeDuration decodeTime; |
|
613 { |
|
614 int64_t currentTime = GetMediaTime(); |
|
615 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
616 TimeStamp start = TimeStamp::Now(); |
|
617 mIsVideoDecoding = mReader->DecodeVideoFrame(mSkipToNextKeyFrame, currentTime); |
|
618 decodeTime = TimeStamp::Now() - start; |
|
619 } |
|
620 if (!mIsVideoDecoding) { |
|
621 // Playback ended for this stream, close the sample queue. |
|
622 VideoQueue().Finish(); |
|
623 CheckIfDecodeComplete(); |
|
624 } |
|
625 |
|
626 if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > mLowAudioThresholdUsecs && |
|
627 !HasLowUndecodedData()) |
|
628 { |
|
629 mLowAudioThresholdUsecs = |
|
630 std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), AMPLE_AUDIO_USECS); |
|
631 mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs, |
|
632 mAmpleAudioThresholdUsecs); |
|
633 DECODER_LOG(PR_LOG_DEBUG, "Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld", |
|
634 mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs); |
|
635 } |
|
636 |
|
637 SendStreamData(); |
|
638 |
|
639 // The ready state can change when we've decoded data, so update the |
|
640 // ready state, so that DOM events can fire. |
|
641 UpdateReadyState(); |
|
642 |
|
643 mDispatchedVideoDecodeTask = false; |
|
644 DispatchDecodeTasksIfNeeded(); |
|
645 } |
|
646 |
|
647 bool |
|
648 MediaDecoderStateMachine::NeedToDecodeAudio() |
|
649 { |
|
650 AssertCurrentThreadInMonitor(); |
|
651 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
652 "Should be on state machine or decode thread."); |
|
653 return mIsAudioDecoding && |
|
654 !mMinimizePreroll && |
|
655 !HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate); |
|
656 } |
|
657 |
|
658 void |
|
659 MediaDecoderStateMachine::DecodeAudio() |
|
660 { |
|
661 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
662 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); |
|
663 |
|
664 if (mState != DECODER_STATE_DECODING && mState != DECODER_STATE_BUFFERING) { |
|
665 mDispatchedAudioDecodeTask = false; |
|
666 return; |
|
667 } |
|
668 EnsureActive(); |
|
669 |
|
670 // We don't want to consider skipping to the next keyframe if we've |
|
671 // only just started up the decode loop, so wait until we've decoded |
|
672 // some audio data before enabling the keyframe skip logic on audio. |
|
673 if (mIsAudioPrerolling && |
|
674 GetDecodedAudioDuration() >= mAudioPrerollUsecs * mPlaybackRate) { |
|
675 mIsAudioPrerolling = false; |
|
676 } |
|
677 |
|
678 { |
|
679 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
680 mIsAudioDecoding = mReader->DecodeAudioData(); |
|
681 } |
|
682 if (!mIsAudioDecoding) { |
|
683 // Playback ended for this stream, close the sample queue. |
|
684 AudioQueue().Finish(); |
|
685 CheckIfDecodeComplete(); |
|
686 } |
|
687 |
|
688 SendStreamData(); |
|
689 |
|
690 // Notify to ensure that the AudioLoop() is not waiting, in case it was |
|
691 // waiting for more audio to be decoded. |
|
692 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
693 |
|
694 // The ready state can change when we've decoded data, so update the |
|
695 // ready state, so that DOM events can fire. |
|
696 UpdateReadyState(); |
|
697 |
|
698 mDispatchedAudioDecodeTask = false; |
|
699 DispatchDecodeTasksIfNeeded(); |
|
700 } |
|
701 |
|
702 void |
|
703 MediaDecoderStateMachine::CheckIfDecodeComplete() |
|
704 { |
|
705 AssertCurrentThreadInMonitor(); |
|
706 if (mState == DECODER_STATE_SHUTDOWN || |
|
707 mState == DECODER_STATE_SEEKING || |
|
708 mState == DECODER_STATE_COMPLETED) { |
|
709 // Don't change our state if we've already been shutdown, or we're seeking, |
|
710 // since we don't want to abort the shutdown or seek processes. |
|
711 return; |
|
712 } |
|
713 MOZ_ASSERT(!AudioQueue().IsFinished() || !mIsAudioDecoding); |
|
714 MOZ_ASSERT(!VideoQueue().IsFinished() || !mIsVideoDecoding); |
|
715 if (!mIsVideoDecoding && !mIsAudioDecoding) { |
|
716 // We've finished decoding all active streams, |
|
717 // so move to COMPLETED state. |
|
718 mState = DECODER_STATE_COMPLETED; |
|
719 DispatchDecodeTasksIfNeeded(); |
|
720 ScheduleStateMachine(); |
|
721 } |
|
722 DECODER_LOG(PR_LOG_DEBUG, "CheckIfDecodeComplete %scompleted", |
|
723 ((mState == DECODER_STATE_COMPLETED) ? "" : "NOT ")); |
|
724 } |
|
725 |
|
726 bool MediaDecoderStateMachine::IsPlaying() |
|
727 { |
|
728 AssertCurrentThreadInMonitor(); |
|
729 |
|
730 return !mPlayStartTime.IsNull(); |
|
731 } |
|
732 |
|
733 // If we have already written enough frames to the AudioStream, start the |
|
734 // playback. |
|
735 static void |
|
736 StartAudioStreamPlaybackIfNeeded(AudioStream* aStream) |
|
737 { |
|
738 // We want to have enough data in the buffer to start the stream. |
|
739 if (static_cast<double>(aStream->GetWritten()) / aStream->GetRate() >= |
|
740 static_cast<double>(AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS) / USECS_PER_S) { |
|
741 aStream->Start(); |
|
742 } |
|
743 } |
|
744 |
|
745 static void WriteSilence(AudioStream* aStream, uint32_t aFrames) |
|
746 { |
|
747 uint32_t numSamples = aFrames * aStream->GetChannels(); |
|
748 nsAutoTArray<AudioDataValue, 1000> buf; |
|
749 buf.SetLength(numSamples); |
|
750 memset(buf.Elements(), 0, numSamples * sizeof(AudioDataValue)); |
|
751 aStream->Write(buf.Elements(), aFrames); |
|
752 |
|
753 StartAudioStreamPlaybackIfNeeded(aStream); |
|
754 } |
|
755 |
|
756 void MediaDecoderStateMachine::AudioLoop() |
|
757 { |
|
758 NS_ASSERTION(OnAudioThread(), "Should be on audio thread."); |
|
759 DECODER_LOG(PR_LOG_DEBUG, "Begun audio thread/loop"); |
|
760 int64_t audioDuration = 0; |
|
761 int64_t audioStartTime = -1; |
|
762 uint32_t channels, rate; |
|
763 double volume = -1; |
|
764 bool setVolume; |
|
765 double playbackRate = -1; |
|
766 bool setPlaybackRate; |
|
767 bool preservesPitch; |
|
768 bool setPreservesPitch; |
|
769 AudioChannel audioChannel; |
|
770 |
|
771 { |
|
772 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
773 mAudioCompleted = false; |
|
774 audioStartTime = mAudioStartTime; |
|
775 NS_ASSERTION(audioStartTime != -1, "Should have audio start time by now"); |
|
776 channels = mInfo.mAudio.mChannels; |
|
777 rate = mInfo.mAudio.mRate; |
|
778 |
|
779 audioChannel = mDecoder->GetAudioChannel(); |
|
780 volume = mVolume; |
|
781 preservesPitch = mPreservesPitch; |
|
782 playbackRate = mPlaybackRate; |
|
783 } |
|
784 |
|
785 { |
|
786 // AudioStream initialization can block for extended periods in unusual |
|
787 // circumstances, so we take care to drop the decoder monitor while |
|
788 // initializing. |
|
789 RefPtr<AudioStream> audioStream(new AudioStream()); |
|
790 audioStream->Init(channels, rate, audioChannel, AudioStream::HighLatency); |
|
791 audioStream->SetVolume(volume); |
|
792 if (audioStream->SetPreservesPitch(preservesPitch) != NS_OK) { |
|
793 NS_WARNING("Setting the pitch preservation failed at AudioLoop start."); |
|
794 } |
|
795 if (playbackRate != 1.0) { |
|
796 NS_ASSERTION(playbackRate != 0, |
|
797 "Don't set the playbackRate to 0 on an AudioStream."); |
|
798 if (audioStream->SetPlaybackRate(playbackRate) != NS_OK) { |
|
799 NS_WARNING("Setting the playback rate failed at AudioLoop start."); |
|
800 } |
|
801 } |
|
802 |
|
803 { |
|
804 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
805 mAudioStream = audioStream.forget(); |
|
806 } |
|
807 } |
|
808 |
|
809 while (1) { |
|
810 // Wait while we're not playing, and we're not shutting down, or we're |
|
811 // playing and we've got no audio to play. |
|
812 { |
|
813 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
814 NS_ASSERTION(mState != DECODER_STATE_DECODING_METADATA, |
|
815 "Should have meta data before audio started playing."); |
|
816 while (mState != DECODER_STATE_SHUTDOWN && |
|
817 !mStopAudioThread && |
|
818 (!IsPlaying() || |
|
819 mState == DECODER_STATE_BUFFERING || |
|
820 (AudioQueue().GetSize() == 0 && |
|
821 !AudioQueue().AtEndOfStream()))) |
|
822 { |
|
823 if (!IsPlaying() && !mAudioStream->IsPaused()) { |
|
824 mAudioStream->Pause(); |
|
825 } |
|
826 mon.Wait(); |
|
827 } |
|
828 |
|
829 // If we're shutting down, break out and exit the audio thread. |
|
830 // Also break out if audio is being captured. |
|
831 if (mState == DECODER_STATE_SHUTDOWN || |
|
832 mStopAudioThread || |
|
833 AudioQueue().AtEndOfStream()) |
|
834 { |
|
835 break; |
|
836 } |
|
837 |
|
838 // We only want to go to the expense of changing the volume if |
|
839 // the volume has changed. |
|
840 setVolume = volume != mVolume; |
|
841 volume = mVolume; |
|
842 |
|
843 // Same for the playbackRate. |
|
844 setPlaybackRate = playbackRate != mPlaybackRate; |
|
845 playbackRate = mPlaybackRate; |
|
846 |
|
847 // Same for the pitch preservation. |
|
848 setPreservesPitch = preservesPitch != mPreservesPitch; |
|
849 preservesPitch = mPreservesPitch; |
|
850 |
|
851 if (IsPlaying() && mAudioStream->IsPaused()) { |
|
852 mAudioStream->Resume(); |
|
853 } |
|
854 } |
|
855 |
|
856 if (setVolume) { |
|
857 mAudioStream->SetVolume(volume); |
|
858 } |
|
859 if (setPlaybackRate) { |
|
860 NS_ASSERTION(playbackRate != 0, |
|
861 "Don't set the playbackRate to 0 in the AudioStreams"); |
|
862 if (mAudioStream->SetPlaybackRate(playbackRate) != NS_OK) { |
|
863 NS_WARNING("Setting the playback rate failed in AudioLoop."); |
|
864 } |
|
865 } |
|
866 if (setPreservesPitch) { |
|
867 if (mAudioStream->SetPreservesPitch(preservesPitch) != NS_OK) { |
|
868 NS_WARNING("Setting the pitch preservation failed in AudioLoop."); |
|
869 } |
|
870 } |
|
871 NS_ASSERTION(AudioQueue().GetSize() > 0, |
|
872 "Should have data to play"); |
|
873 // See if there's a gap in the audio. If there is, push silence into the |
|
874 // audio hardware, so we can play across the gap. |
|
875 const AudioData* s = AudioQueue().PeekFront(); |
|
876 |
|
877 // Calculate the number of frames that have been pushed onto the audio |
|
878 // hardware. |
|
879 CheckedInt64 playedFrames = UsecsToFrames(audioStartTime, rate) + |
|
880 audioDuration; |
|
881 // Calculate the timestamp of the next chunk of audio in numbers of |
|
882 // samples. |
|
883 CheckedInt64 sampleTime = UsecsToFrames(s->mTime, rate); |
|
884 CheckedInt64 missingFrames = sampleTime - playedFrames; |
|
885 if (!missingFrames.isValid() || !sampleTime.isValid()) { |
|
886 NS_WARNING("Int overflow adding in AudioLoop()"); |
|
887 break; |
|
888 } |
|
889 |
|
890 int64_t framesWritten = 0; |
|
891 if (missingFrames.value() > 0) { |
|
892 // The next audio chunk begins some time after the end of the last chunk |
|
893 // we pushed to the audio hardware. We must push silence into the audio |
|
894 // hardware so that the next audio chunk begins playback at the correct |
|
895 // time. |
|
896 missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value()); |
|
897 VERBOSE_LOG("playing %d frames of silence", int32_t(missingFrames.value())); |
|
898 framesWritten = PlaySilence(static_cast<uint32_t>(missingFrames.value()), |
|
899 channels, playedFrames.value()); |
|
900 } else { |
|
901 framesWritten = PlayFromAudioQueue(sampleTime.value(), channels); |
|
902 } |
|
903 audioDuration += framesWritten; |
|
904 { |
|
905 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
906 CheckedInt64 playedUsecs = FramesToUsecs(audioDuration, rate) + audioStartTime; |
|
907 if (!playedUsecs.isValid()) { |
|
908 NS_WARNING("Int overflow calculating audio end time"); |
|
909 break; |
|
910 } |
|
911 mAudioEndTime = playedUsecs.value(); |
|
912 } |
|
913 } |
|
914 { |
|
915 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
916 if (AudioQueue().AtEndOfStream() && |
|
917 mState != DECODER_STATE_SHUTDOWN && |
|
918 !mStopAudioThread) |
|
919 { |
|
920 // If the media was too short to trigger the start of the audio stream, |
|
921 // start it now. |
|
922 mAudioStream->Start(); |
|
923 // Last frame pushed to audio hardware, wait for the audio to finish, |
|
924 // before the audio thread terminates. |
|
925 bool seeking = false; |
|
926 { |
|
927 int64_t oldPosition = -1; |
|
928 int64_t position = GetMediaTime(); |
|
929 while (oldPosition != position && |
|
930 mAudioEndTime - position > 0 && |
|
931 mState != DECODER_STATE_SEEKING && |
|
932 mState != DECODER_STATE_SHUTDOWN) |
|
933 { |
|
934 const int64_t DRAIN_BLOCK_USECS = 100000; |
|
935 Wait(std::min(mAudioEndTime - position, DRAIN_BLOCK_USECS)); |
|
936 oldPosition = position; |
|
937 position = GetMediaTime(); |
|
938 } |
|
939 seeking = mState == DECODER_STATE_SEEKING; |
|
940 } |
|
941 |
|
942 if (!seeking && !mAudioStream->IsPaused()) { |
|
943 { |
|
944 ReentrantMonitorAutoExit exit(mDecoder->GetReentrantMonitor()); |
|
945 mAudioStream->Drain(); |
|
946 } |
|
947 } |
|
948 } |
|
949 } |
|
950 DECODER_LOG(PR_LOG_DEBUG, "Reached audio stream end."); |
|
951 { |
|
952 // Must hold lock while shutting down and anulling the audio stream to prevent |
|
953 // state machine thread trying to use it while we're destroying it. |
|
954 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
955 mAudioStream->Shutdown(); |
|
956 mAudioStream = nullptr; |
|
957 if (!mAudioCaptured) { |
|
958 mAudioCompleted = true; |
|
959 UpdateReadyState(); |
|
960 // Kick the decode thread; it may be sleeping waiting for this to finish. |
|
961 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
962 } |
|
963 } |
|
964 |
|
965 DECODER_LOG(PR_LOG_DEBUG, "Audio stream finished playing, audio thread exit"); |
|
966 } |
|
967 |
|
968 uint32_t MediaDecoderStateMachine::PlaySilence(uint32_t aFrames, |
|
969 uint32_t aChannels, |
|
970 uint64_t aFrameOffset) |
|
971 |
|
972 { |
|
973 NS_ASSERTION(OnAudioThread(), "Only call on audio thread."); |
|
974 NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused"); |
|
975 uint32_t maxFrames = SILENCE_BYTES_CHUNK / aChannels / sizeof(AudioDataValue); |
|
976 uint32_t frames = std::min(aFrames, maxFrames); |
|
977 WriteSilence(mAudioStream, frames); |
|
978 return frames; |
|
979 } |
|
980 |
|
981 uint32_t MediaDecoderStateMachine::PlayFromAudioQueue(uint64_t aFrameOffset, |
|
982 uint32_t aChannels) |
|
983 { |
|
984 NS_ASSERTION(OnAudioThread(), "Only call on audio thread."); |
|
985 NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused"); |
|
986 nsAutoPtr<AudioData> audio(AudioQueue().PopFront()); |
|
987 { |
|
988 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
989 NS_WARN_IF_FALSE(IsPlaying(), "Should be playing"); |
|
990 // Awaken the decode loop if it's waiting for space to free up in the |
|
991 // audio queue. |
|
992 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
993 } |
|
994 int64_t offset = -1; |
|
995 uint32_t frames = 0; |
|
996 VERBOSE_LOG("playing %d frames of data to stream for AudioData at %lld", |
|
997 audio->mFrames, audio->mTime); |
|
998 mAudioStream->Write(audio->mAudioData, |
|
999 audio->mFrames); |
|
1000 |
|
1001 aChannels = mAudioStream->GetOutChannels(); |
|
1002 |
|
1003 StartAudioStreamPlaybackIfNeeded(mAudioStream); |
|
1004 |
|
1005 offset = audio->mOffset; |
|
1006 frames = audio->mFrames; |
|
1007 |
|
1008 if (offset != -1) { |
|
1009 mDecoder->UpdatePlaybackOffset(offset); |
|
1010 } |
|
1011 return frames; |
|
1012 } |
|
1013 |
|
1014 nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor) |
|
1015 { |
|
1016 MOZ_ASSERT(NS_IsMainThread()); |
|
1017 |
|
1018 RefPtr<SharedThreadPool> decodePool( |
|
1019 SharedThreadPool::Get(NS_LITERAL_CSTRING("Media Decode"), |
|
1020 Preferences::GetUint("media.num-decode-threads", 25))); |
|
1021 NS_ENSURE_TRUE(decodePool, NS_ERROR_FAILURE); |
|
1022 |
|
1023 RefPtr<SharedThreadPool> stateMachinePool( |
|
1024 SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1)); |
|
1025 NS_ENSURE_TRUE(stateMachinePool, NS_ERROR_FAILURE); |
|
1026 |
|
1027 mDecodeTaskQueue = new MediaTaskQueue(decodePool.forget()); |
|
1028 NS_ENSURE_TRUE(mDecodeTaskQueue, NS_ERROR_FAILURE); |
|
1029 |
|
1030 MediaDecoderReader* cloneReader = nullptr; |
|
1031 if (aCloneDonor) { |
|
1032 cloneReader = aCloneDonor->mReader; |
|
1033 } |
|
1034 |
|
1035 mStateMachineThreadPool = stateMachinePool; |
|
1036 |
|
1037 nsresult rv; |
|
1038 mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); |
|
1039 NS_ENSURE_SUCCESS(rv, rv); |
|
1040 rv = mTimer->SetTarget(GetStateMachineThread()); |
|
1041 NS_ENSURE_SUCCESS(rv, rv); |
|
1042 |
|
1043 return mReader->Init(cloneReader); |
|
1044 } |
|
1045 |
|
1046 void MediaDecoderStateMachine::StopPlayback() |
|
1047 { |
|
1048 DECODER_LOG(PR_LOG_DEBUG, "StopPlayback()"); |
|
1049 |
|
1050 AssertCurrentThreadInMonitor(); |
|
1051 |
|
1052 mDecoder->NotifyPlaybackStopped(); |
|
1053 |
|
1054 if (IsPlaying()) { |
|
1055 mPlayDuration = GetClock(); |
|
1056 mPlayStartTime = TimeStamp(); |
|
1057 } |
|
1058 // Notify the audio thread, so that it notices that we've stopped playing, |
|
1059 // so it can pause audio playback. |
|
1060 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
1061 NS_ASSERTION(!IsPlaying(), "Should report not playing at end of StopPlayback()"); |
|
1062 mDecoder->UpdateStreamBlockingForStateMachinePlaying(); |
|
1063 |
|
1064 DispatchDecodeTasksIfNeeded(); |
|
1065 } |
|
1066 |
|
1067 void MediaDecoderStateMachine::SetSyncPointForMediaStream() |
|
1068 { |
|
1069 AssertCurrentThreadInMonitor(); |
|
1070 |
|
1071 DecodedStreamData* stream = mDecoder->GetDecodedStream(); |
|
1072 if (!stream) { |
|
1073 return; |
|
1074 } |
|
1075 |
|
1076 mSyncPointInMediaStream = stream->GetLastOutputTime(); |
|
1077 mSyncPointInDecodedStream = mStartTime + mPlayDuration; |
|
1078 } |
|
1079 |
|
1080 int64_t MediaDecoderStateMachine::GetCurrentTimeViaMediaStreamSync() |
|
1081 { |
|
1082 AssertCurrentThreadInMonitor(); |
|
1083 NS_ASSERTION(mSyncPointInDecodedStream >= 0, "Should have set up sync point"); |
|
1084 DecodedStreamData* stream = mDecoder->GetDecodedStream(); |
|
1085 StreamTime streamDelta = stream->GetLastOutputTime() - mSyncPointInMediaStream; |
|
1086 return mSyncPointInDecodedStream + MediaTimeToMicroseconds(streamDelta); |
|
1087 } |
|
1088 |
|
1089 void MediaDecoderStateMachine::StartPlayback() |
|
1090 { |
|
1091 DECODER_LOG(PR_LOG_DEBUG, "StartPlayback()"); |
|
1092 |
|
1093 NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called"); |
|
1094 AssertCurrentThreadInMonitor(); |
|
1095 |
|
1096 mDecoder->NotifyPlaybackStarted(); |
|
1097 mPlayStartTime = TimeStamp::Now(); |
|
1098 |
|
1099 NS_ASSERTION(IsPlaying(), "Should report playing by end of StartPlayback()"); |
|
1100 if (NS_FAILED(StartAudioThread())) { |
|
1101 NS_WARNING("Failed to create audio thread"); |
|
1102 } |
|
1103 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
1104 mDecoder->UpdateStreamBlockingForStateMachinePlaying(); |
|
1105 } |
|
1106 |
|
1107 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime) |
|
1108 { |
|
1109 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
1110 "Should be on state machine thread."); |
|
1111 AssertCurrentThreadInMonitor(); |
|
1112 |
|
1113 NS_ASSERTION(mStartTime >= 0, "Should have positive mStartTime"); |
|
1114 mCurrentFrameTime = aTime - mStartTime; |
|
1115 NS_ASSERTION(mCurrentFrameTime >= 0, "CurrentTime should be positive!"); |
|
1116 if (aTime > mEndTime) { |
|
1117 NS_ASSERTION(mCurrentFrameTime > GetDuration(), |
|
1118 "CurrentTime must be after duration if aTime > endTime!"); |
|
1119 mEndTime = aTime; |
|
1120 nsCOMPtr<nsIRunnable> event = |
|
1121 NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged); |
|
1122 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
1123 } |
|
1124 } |
|
1125 |
|
1126 void MediaDecoderStateMachine::UpdatePlaybackPosition(int64_t aTime) |
|
1127 { |
|
1128 UpdatePlaybackPositionInternal(aTime); |
|
1129 |
|
1130 bool fragmentEnded = mFragmentEndTime >= 0 && GetMediaTime() >= mFragmentEndTime; |
|
1131 if (!mPositionChangeQueued || fragmentEnded) { |
|
1132 mPositionChangeQueued = true; |
|
1133 nsCOMPtr<nsIRunnable> event = |
|
1134 NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackPositionChanged); |
|
1135 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
1136 } |
|
1137 |
|
1138 mMetadataManager.DispatchMetadataIfNeeded(mDecoder, aTime); |
|
1139 |
|
1140 if (fragmentEnded) { |
|
1141 StopPlayback(); |
|
1142 } |
|
1143 } |
|
1144 |
|
1145 void MediaDecoderStateMachine::ClearPositionChangeFlag() |
|
1146 { |
|
1147 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
1148 AssertCurrentThreadInMonitor(); |
|
1149 |
|
1150 mPositionChangeQueued = false; |
|
1151 } |
|
1152 |
|
1153 MediaDecoderOwner::NextFrameStatus MediaDecoderStateMachine::GetNextFrameStatus() |
|
1154 { |
|
1155 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1156 if (IsBuffering() || IsSeeking()) { |
|
1157 return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING; |
|
1158 } else if (HaveNextFrameData()) { |
|
1159 return MediaDecoderOwner::NEXT_FRAME_AVAILABLE; |
|
1160 } |
|
1161 return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; |
|
1162 } |
|
1163 |
|
1164 void MediaDecoderStateMachine::SetVolume(double volume) |
|
1165 { |
|
1166 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
1167 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1168 mVolume = volume; |
|
1169 } |
|
1170 |
|
1171 void MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured) |
|
1172 { |
|
1173 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
1174 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1175 if (!mAudioCaptured && aCaptured && !mStopAudioThread) { |
|
1176 // Make sure the state machine runs as soon as possible. That will |
|
1177 // stop the audio thread. |
|
1178 // If mStopAudioThread is true then we're already stopping the audio thread |
|
1179 // and since we set mAudioCaptured to true, nothing can start it again. |
|
1180 ScheduleStateMachine(); |
|
1181 } |
|
1182 mAudioCaptured = aCaptured; |
|
1183 } |
|
1184 |
|
1185 double MediaDecoderStateMachine::GetCurrentTime() const |
|
1186 { |
|
1187 NS_ASSERTION(NS_IsMainThread() || |
|
1188 OnStateMachineThread() || |
|
1189 OnDecodeThread(), |
|
1190 "Should be on main, decode, or state machine thread."); |
|
1191 |
|
1192 return static_cast<double>(mCurrentFrameTime) / static_cast<double>(USECS_PER_S); |
|
1193 } |
|
1194 |
|
1195 int64_t MediaDecoderStateMachine::GetDuration() |
|
1196 { |
|
1197 AssertCurrentThreadInMonitor(); |
|
1198 |
|
1199 if (mEndTime == -1 || mStartTime == -1) |
|
1200 return -1; |
|
1201 return mEndTime - mStartTime; |
|
1202 } |
|
1203 |
|
1204 void MediaDecoderStateMachine::SetDuration(int64_t aDuration) |
|
1205 { |
|
1206 NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(), |
|
1207 "Should be on main or decode thread."); |
|
1208 AssertCurrentThreadInMonitor(); |
|
1209 |
|
1210 if (aDuration == -1) { |
|
1211 return; |
|
1212 } |
|
1213 |
|
1214 if (mStartTime != -1) { |
|
1215 mEndTime = mStartTime + aDuration; |
|
1216 } else { |
|
1217 mStartTime = 0; |
|
1218 mEndTime = aDuration; |
|
1219 } |
|
1220 } |
|
1221 |
|
1222 void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration) |
|
1223 { |
|
1224 AssertCurrentThreadInMonitor(); |
|
1225 int64_t duration = GetDuration(); |
|
1226 if (aDuration != duration && |
|
1227 abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) { |
|
1228 SetDuration(aDuration); |
|
1229 nsCOMPtr<nsIRunnable> event = |
|
1230 NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged); |
|
1231 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
1232 } |
|
1233 } |
|
1234 |
|
1235 void MediaDecoderStateMachine::SetMediaEndTime(int64_t aEndTime) |
|
1236 { |
|
1237 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread"); |
|
1238 AssertCurrentThreadInMonitor(); |
|
1239 |
|
1240 mEndTime = aEndTime; |
|
1241 } |
|
1242 |
|
1243 void MediaDecoderStateMachine::SetFragmentEndTime(int64_t aEndTime) |
|
1244 { |
|
1245 AssertCurrentThreadInMonitor(); |
|
1246 |
|
1247 mFragmentEndTime = aEndTime < 0 ? aEndTime : aEndTime + mStartTime; |
|
1248 } |
|
1249 |
|
1250 void MediaDecoderStateMachine::SetTransportSeekable(bool aTransportSeekable) |
|
1251 { |
|
1252 NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(), |
|
1253 "Should be on main thread or the decoder thread."); |
|
1254 AssertCurrentThreadInMonitor(); |
|
1255 |
|
1256 mTransportSeekable = aTransportSeekable; |
|
1257 } |
|
1258 |
|
1259 void MediaDecoderStateMachine::SetMediaSeekable(bool aMediaSeekable) |
|
1260 { |
|
1261 NS_ASSERTION(NS_IsMainThread() || OnDecodeThread(), |
|
1262 "Should be on main thread or the decoder thread."); |
|
1263 |
|
1264 mMediaSeekable = aMediaSeekable; |
|
1265 } |
|
1266 |
|
1267 bool MediaDecoderStateMachine::IsDormantNeeded() |
|
1268 { |
|
1269 return mReader->IsDormantNeeded(); |
|
1270 } |
|
1271 |
|
1272 void MediaDecoderStateMachine::SetDormant(bool aDormant) |
|
1273 { |
|
1274 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
1275 AssertCurrentThreadInMonitor(); |
|
1276 |
|
1277 if (!mReader) { |
|
1278 return; |
|
1279 } |
|
1280 |
|
1281 if (aDormant) { |
|
1282 ScheduleStateMachine(); |
|
1283 mState = DECODER_STATE_DORMANT; |
|
1284 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
1285 } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) { |
|
1286 ScheduleStateMachine(); |
|
1287 mStartTime = 0; |
|
1288 mCurrentFrameTime = 0; |
|
1289 mState = DECODER_STATE_DECODING_METADATA; |
|
1290 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
1291 } |
|
1292 } |
|
1293 |
|
1294 void MediaDecoderStateMachine::Shutdown() |
|
1295 { |
|
1296 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
1297 |
|
1298 // Once we've entered the shutdown state here there's no going back. |
|
1299 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1300 |
|
1301 // Change state before issuing shutdown request to threads so those |
|
1302 // threads can start exiting cleanly during the Shutdown call. |
|
1303 DECODER_LOG(PR_LOG_DEBUG, "Changed state to SHUTDOWN"); |
|
1304 ScheduleStateMachine(); |
|
1305 mState = DECODER_STATE_SHUTDOWN; |
|
1306 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
1307 } |
|
1308 |
|
1309 void MediaDecoderStateMachine::StartDecoding() |
|
1310 { |
|
1311 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
1312 "Should be on state machine or decode thread."); |
|
1313 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1314 if (mState == DECODER_STATE_DECODING) { |
|
1315 return; |
|
1316 } |
|
1317 mState = DECODER_STATE_DECODING; |
|
1318 |
|
1319 mDecodeStartTime = TimeStamp::Now(); |
|
1320 |
|
1321 // Reset our "stream finished decoding" flags, so we try to decode all |
|
1322 // streams that we have when we start decoding. |
|
1323 mIsVideoDecoding = HasVideo() && !VideoQueue().IsFinished(); |
|
1324 mIsAudioDecoding = HasAudio() && !AudioQueue().IsFinished(); |
|
1325 |
|
1326 CheckIfDecodeComplete(); |
|
1327 if (mState == DECODER_STATE_COMPLETED) { |
|
1328 return; |
|
1329 } |
|
1330 |
|
1331 // Reset other state to pristine values before starting decode. |
|
1332 mSkipToNextKeyFrame = false; |
|
1333 mIsAudioPrerolling = true; |
|
1334 mIsVideoPrerolling = true; |
|
1335 |
|
1336 // Ensure that we've got tasks enqueued to decode data if we need to. |
|
1337 DispatchDecodeTasksIfNeeded(); |
|
1338 |
|
1339 ScheduleStateMachine(); |
|
1340 } |
|
1341 |
|
1342 void MediaDecoderStateMachine::StartWaitForResources() |
|
1343 { |
|
1344 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
1345 "Should be on state machine or decode thread."); |
|
1346 AssertCurrentThreadInMonitor(); |
|
1347 mState = DECODER_STATE_WAIT_FOR_RESOURCES; |
|
1348 } |
|
1349 |
|
1350 void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged() |
|
1351 { |
|
1352 AssertCurrentThreadInMonitor(); |
|
1353 if (mState != DECODER_STATE_WAIT_FOR_RESOURCES || |
|
1354 mReader->IsWaitingMediaResources()) { |
|
1355 return; |
|
1356 } |
|
1357 // The reader is no longer waiting for resources (say a hardware decoder), |
|
1358 // we can now proceed to decode metadata. |
|
1359 mState = DECODER_STATE_DECODING_METADATA; |
|
1360 EnqueueDecodeMetadataTask(); |
|
1361 } |
|
1362 |
|
1363 void MediaDecoderStateMachine::Play() |
|
1364 { |
|
1365 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
1366 // When asked to play, switch to decoding state only if |
|
1367 // we are currently buffering. In other cases, we'll start playing anyway |
|
1368 // when the state machine notices the decoder's state change to PLAYING. |
|
1369 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1370 if (mState == DECODER_STATE_BUFFERING) { |
|
1371 DECODER_LOG(PR_LOG_DEBUG, "Changed state from BUFFERING to DECODING"); |
|
1372 mState = DECODER_STATE_DECODING; |
|
1373 mDecodeStartTime = TimeStamp::Now(); |
|
1374 } |
|
1375 // Once we start playing, we don't want to minimize our prerolling, as we |
|
1376 // assume the user is likely to want to keep playing in future. |
|
1377 mMinimizePreroll = false; |
|
1378 ScheduleStateMachine(); |
|
1379 } |
|
1380 |
|
1381 void MediaDecoderStateMachine::ResetPlayback() |
|
1382 { |
|
1383 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); |
|
1384 mVideoFrameEndTime = -1; |
|
1385 mAudioStartTime = -1; |
|
1386 mAudioEndTime = -1; |
|
1387 mAudioCompleted = false; |
|
1388 } |
|
1389 |
|
1390 void MediaDecoderStateMachine::NotifyDataArrived(const char* aBuffer, |
|
1391 uint32_t aLength, |
|
1392 int64_t aOffset) |
|
1393 { |
|
1394 NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); |
|
1395 mReader->NotifyDataArrived(aBuffer, aLength, aOffset); |
|
1396 |
|
1397 // While playing an unseekable stream of unknown duration, mEndTime is |
|
1398 // updated (in AdvanceFrame()) as we play. But if data is being downloaded |
|
1399 // faster than played, mEndTime won't reflect the end of playable data |
|
1400 // since we haven't played the frame at the end of buffered data. So update |
|
1401 // mEndTime here as new data is downloaded to prevent such a lag. |
|
1402 dom::TimeRanges buffered; |
|
1403 if (mDecoder->IsInfinite() && |
|
1404 NS_SUCCEEDED(mDecoder->GetBuffered(&buffered))) |
|
1405 { |
|
1406 uint32_t length = 0; |
|
1407 buffered.GetLength(&length); |
|
1408 if (length) { |
|
1409 double end = 0; |
|
1410 buffered.End(length - 1, &end); |
|
1411 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1412 mEndTime = std::max<int64_t>(mEndTime, end * USECS_PER_S); |
|
1413 } |
|
1414 } |
|
1415 } |
|
1416 |
|
1417 void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget) |
|
1418 { |
|
1419 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
1420 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1421 |
|
1422 // We need to be able to seek both at a transport level and at a media level |
|
1423 // to seek. |
|
1424 if (!mMediaSeekable) { |
|
1425 return; |
|
1426 } |
|
1427 // MediaDecoder::mPlayState should be SEEKING while we seek, and |
|
1428 // in that case MediaDecoder shouldn't be calling us. |
|
1429 NS_ASSERTION(mState != DECODER_STATE_SEEKING, |
|
1430 "We shouldn't already be seeking"); |
|
1431 NS_ASSERTION(mState >= DECODER_STATE_DECODING, |
|
1432 "We should have loaded metadata"); |
|
1433 |
|
1434 // Bound the seek time to be inside the media range. |
|
1435 NS_ASSERTION(mStartTime != -1, "Should know start time by now"); |
|
1436 NS_ASSERTION(mEndTime != -1, "Should know end time by now"); |
|
1437 int64_t seekTime = aTarget.mTime + mStartTime; |
|
1438 seekTime = std::min(seekTime, mEndTime); |
|
1439 seekTime = std::max(mStartTime, seekTime); |
|
1440 NS_ASSERTION(seekTime >= mStartTime && seekTime <= mEndTime, |
|
1441 "Can only seek in range [0,duration]"); |
|
1442 mSeekTarget = SeekTarget(seekTime, aTarget.mType); |
|
1443 |
|
1444 mBasePosition = seekTime - mStartTime; |
|
1445 DECODER_LOG(PR_LOG_DEBUG, "Changed state to SEEKING (to %lld)", mSeekTarget.mTime); |
|
1446 mState = DECODER_STATE_SEEKING; |
|
1447 if (mDecoder->GetDecodedStream()) { |
|
1448 mDecoder->RecreateDecodedStream(seekTime - mStartTime); |
|
1449 } |
|
1450 ScheduleStateMachine(); |
|
1451 } |
|
1452 |
|
1453 void MediaDecoderStateMachine::StopAudioThread() |
|
1454 { |
|
1455 NS_ASSERTION(OnDecodeThread() || |
|
1456 OnStateMachineThread(), "Should be on decode thread or state machine thread"); |
|
1457 AssertCurrentThreadInMonitor(); |
|
1458 |
|
1459 if (mStopAudioThread) { |
|
1460 // Nothing to do, since the thread is already stopping |
|
1461 return; |
|
1462 } |
|
1463 |
|
1464 mStopAudioThread = true; |
|
1465 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
1466 if (mAudioThread) { |
|
1467 DECODER_LOG(PR_LOG_DEBUG, "Shutdown audio thread"); |
|
1468 { |
|
1469 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
1470 mAudioThread->Shutdown(); |
|
1471 } |
|
1472 mAudioThread = nullptr; |
|
1473 // Now that the audio thread is dead, try sending data to our MediaStream(s). |
|
1474 // That may have been waiting for the audio thread to stop. |
|
1475 SendStreamData(); |
|
1476 } |
|
1477 } |
|
1478 |
|
1479 nsresult |
|
1480 MediaDecoderStateMachine::EnqueueDecodeMetadataTask() |
|
1481 { |
|
1482 AssertCurrentThreadInMonitor(); |
|
1483 |
|
1484 if (mState != DECODER_STATE_DECODING_METADATA) { |
|
1485 return NS_OK; |
|
1486 } |
|
1487 nsresult rv = mDecodeTaskQueue->Dispatch( |
|
1488 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeMetadata)); |
|
1489 NS_ENSURE_SUCCESS(rv, rv); |
|
1490 |
|
1491 return NS_OK; |
|
1492 } |
|
1493 |
|
1494 void |
|
1495 MediaDecoderStateMachine::EnsureActive() |
|
1496 { |
|
1497 AssertCurrentThreadInMonitor(); |
|
1498 MOZ_ASSERT(OnDecodeThread()); |
|
1499 if (!mIsReaderIdle) { |
|
1500 return; |
|
1501 } |
|
1502 mIsReaderIdle = false; |
|
1503 { |
|
1504 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
1505 SetReaderActive(); |
|
1506 } |
|
1507 } |
|
1508 |
|
1509 void |
|
1510 MediaDecoderStateMachine::SetReaderIdle() |
|
1511 { |
|
1512 #ifdef PR_LOGGING |
|
1513 { |
|
1514 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1515 DECODER_LOG(PR_LOG_DEBUG, "SetReaderIdle() audioQueue=%lld videoQueue=%lld", |
|
1516 GetDecodedAudioDuration(), |
|
1517 VideoQueue().Duration()); |
|
1518 } |
|
1519 #endif |
|
1520 MOZ_ASSERT(OnDecodeThread()); |
|
1521 mReader->SetIdle(); |
|
1522 } |
|
1523 |
|
1524 void |
|
1525 MediaDecoderStateMachine::SetReaderActive() |
|
1526 { |
|
1527 DECODER_LOG(PR_LOG_DEBUG, "SetReaderActive()"); |
|
1528 MOZ_ASSERT(OnDecodeThread()); |
|
1529 mReader->SetActive(); |
|
1530 } |
|
1531 |
|
1532 void |
|
1533 MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded() |
|
1534 { |
|
1535 AssertCurrentThreadInMonitor(); |
|
1536 |
|
1537 // NeedToDecodeAudio() can go from false to true while we hold the |
|
1538 // monitor, but it can't go from true to false. This can happen because |
|
1539 // NeedToDecodeAudio() takes into account the amount of decoded audio |
|
1540 // that's been written to the AudioStream but not played yet. So if we |
|
1541 // were calling NeedToDecodeAudio() twice and we thread-context switch |
|
1542 // between the calls, audio can play, which can affect the return value |
|
1543 // of NeedToDecodeAudio() giving inconsistent results. So we cache the |
|
1544 // value returned by NeedToDecodeAudio(), and make decisions |
|
1545 // based on the cached value. If NeedToDecodeAudio() has |
|
1546 // returned false, and then subsequently returns true and we're not |
|
1547 // playing, it will probably be OK since we don't need to consume data |
|
1548 // anyway. |
|
1549 |
|
1550 const bool needToDecodeAudio = NeedToDecodeAudio(); |
|
1551 const bool needToDecodeVideo = NeedToDecodeVideo(); |
|
1552 |
|
1553 // If we're in completed state, we should not need to decode anything else. |
|
1554 MOZ_ASSERT(mState != DECODER_STATE_COMPLETED || |
|
1555 (!needToDecodeAudio && !needToDecodeVideo)); |
|
1556 |
|
1557 bool needIdle = !mDecoder->IsLogicallyPlaying() && |
|
1558 mState != DECODER_STATE_SEEKING && |
|
1559 !needToDecodeAudio && |
|
1560 !needToDecodeVideo && |
|
1561 !IsPlaying(); |
|
1562 |
|
1563 if (needToDecodeAudio) { |
|
1564 EnsureAudioDecodeTaskQueued(); |
|
1565 } |
|
1566 if (needToDecodeVideo) { |
|
1567 EnsureVideoDecodeTaskQueued(); |
|
1568 } |
|
1569 |
|
1570 if (mIsReaderIdle == needIdle) { |
|
1571 return; |
|
1572 } |
|
1573 mIsReaderIdle = needIdle; |
|
1574 RefPtr<nsIRunnable> event; |
|
1575 if (mIsReaderIdle) { |
|
1576 event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderIdle); |
|
1577 } else { |
|
1578 event = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::SetReaderActive); |
|
1579 } |
|
1580 if (NS_FAILED(mDecodeTaskQueue->Dispatch(event.forget())) && |
|
1581 mState != DECODER_STATE_SHUTDOWN) { |
|
1582 NS_WARNING("Failed to dispatch event to set decoder idle state"); |
|
1583 } |
|
1584 } |
|
1585 |
|
1586 nsresult |
|
1587 MediaDecoderStateMachine::EnqueueDecodeSeekTask() |
|
1588 { |
|
1589 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
1590 "Should be on state machine or decode thread."); |
|
1591 AssertCurrentThreadInMonitor(); |
|
1592 |
|
1593 if (mState != DECODER_STATE_SEEKING) { |
|
1594 return NS_OK; |
|
1595 } |
|
1596 nsresult rv = mDecodeTaskQueue->Dispatch( |
|
1597 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeSeek)); |
|
1598 NS_ENSURE_SUCCESS(rv, rv); |
|
1599 |
|
1600 return NS_OK; |
|
1601 } |
|
1602 |
|
1603 nsresult |
|
1604 MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded() |
|
1605 { |
|
1606 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1607 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
1608 "Should be on state machine or decode thread."); |
|
1609 |
|
1610 if (NeedToDecodeAudio()) { |
|
1611 return EnsureAudioDecodeTaskQueued(); |
|
1612 } |
|
1613 |
|
1614 return NS_OK; |
|
1615 } |
|
1616 |
|
1617 nsresult |
|
1618 MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued() |
|
1619 { |
|
1620 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1621 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
1622 "Should be on state machine or decode thread."); |
|
1623 |
|
1624 if (mState >= DECODER_STATE_COMPLETED) { |
|
1625 return NS_OK; |
|
1626 } |
|
1627 |
|
1628 MOZ_ASSERT(mState > DECODER_STATE_DECODING_METADATA); |
|
1629 |
|
1630 if (mIsAudioDecoding && !mDispatchedAudioDecodeTask) { |
|
1631 nsresult rv = mDecodeTaskQueue->Dispatch( |
|
1632 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeAudio)); |
|
1633 if (NS_SUCCEEDED(rv)) { |
|
1634 mDispatchedAudioDecodeTask = true; |
|
1635 } else { |
|
1636 NS_WARNING("Failed to dispatch task to decode audio"); |
|
1637 } |
|
1638 } |
|
1639 |
|
1640 return NS_OK; |
|
1641 } |
|
1642 |
|
1643 nsresult |
|
1644 MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded() |
|
1645 { |
|
1646 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1647 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
1648 "Should be on state machine or decode thread."); |
|
1649 |
|
1650 if (NeedToDecodeVideo()) { |
|
1651 return EnsureVideoDecodeTaskQueued(); |
|
1652 } |
|
1653 |
|
1654 return NS_OK; |
|
1655 } |
|
1656 |
|
1657 nsresult |
|
1658 MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued() |
|
1659 { |
|
1660 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1661 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
1662 "Should be on state machine or decode thread."); |
|
1663 |
|
1664 if (mState >= DECODER_STATE_COMPLETED) { |
|
1665 return NS_OK; |
|
1666 } |
|
1667 |
|
1668 MOZ_ASSERT(mState > DECODER_STATE_DECODING_METADATA); |
|
1669 |
|
1670 if (mIsVideoDecoding && !mDispatchedVideoDecodeTask) { |
|
1671 nsresult rv = mDecodeTaskQueue->Dispatch( |
|
1672 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeVideo)); |
|
1673 if (NS_SUCCEEDED(rv)) { |
|
1674 mDispatchedVideoDecodeTask = true; |
|
1675 } else { |
|
1676 NS_WARNING("Failed to dispatch task to decode video"); |
|
1677 } |
|
1678 } |
|
1679 |
|
1680 return NS_OK; |
|
1681 } |
|
1682 |
|
1683 nsresult |
|
1684 MediaDecoderStateMachine::StartAudioThread() |
|
1685 { |
|
1686 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
1687 "Should be on state machine or decode thread."); |
|
1688 AssertCurrentThreadInMonitor(); |
|
1689 if (mAudioCaptured) { |
|
1690 NS_ASSERTION(mStopAudioThread, "mStopAudioThread must always be true if audio is captured"); |
|
1691 return NS_OK; |
|
1692 } |
|
1693 |
|
1694 mStopAudioThread = false; |
|
1695 if (HasAudio() && !mAudioThread) { |
|
1696 nsresult rv = NS_NewNamedThread("Media Audio", |
|
1697 getter_AddRefs(mAudioThread), |
|
1698 nullptr, |
|
1699 MEDIA_THREAD_STACK_SIZE); |
|
1700 if (NS_FAILED(rv)) { |
|
1701 DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because failed to create audio thread"); |
|
1702 mState = DECODER_STATE_SHUTDOWN; |
|
1703 return rv; |
|
1704 } |
|
1705 |
|
1706 nsCOMPtr<nsIRunnable> event = |
|
1707 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::AudioLoop); |
|
1708 mAudioThread->Dispatch(event, NS_DISPATCH_NORMAL); |
|
1709 } |
|
1710 return NS_OK; |
|
1711 } |
|
1712 |
|
1713 int64_t MediaDecoderStateMachine::AudioDecodedUsecs() |
|
1714 { |
|
1715 NS_ASSERTION(HasAudio(), |
|
1716 "Should only call AudioDecodedUsecs() when we have audio"); |
|
1717 // The amount of audio we have decoded is the amount of audio data we've |
|
1718 // already decoded and pushed to the hardware, plus the amount of audio |
|
1719 // data waiting to be pushed to the hardware. |
|
1720 int64_t pushed = (mAudioEndTime != -1) ? (mAudioEndTime - GetMediaTime()) : 0; |
|
1721 return pushed + AudioQueue().Duration(); |
|
1722 } |
|
1723 |
|
1724 bool MediaDecoderStateMachine::HasLowDecodedData(int64_t aAudioUsecs) |
|
1725 { |
|
1726 AssertCurrentThreadInMonitor(); |
|
1727 // We consider ourselves low on decoded data if we're low on audio, |
|
1728 // provided we've not decoded to the end of the audio stream, or |
|
1729 // if we're low on video frames, provided |
|
1730 // we've not decoded to the end of the video stream. |
|
1731 return ((HasAudio() && |
|
1732 !AudioQueue().IsFinished() && |
|
1733 AudioDecodedUsecs() < aAudioUsecs) |
|
1734 || |
|
1735 (HasVideo() && |
|
1736 !VideoQueue().IsFinished() && |
|
1737 static_cast<uint32_t>(VideoQueue().GetSize()) < LOW_VIDEO_FRAMES)); |
|
1738 } |
|
1739 |
|
1740 bool MediaDecoderStateMachine::HasLowUndecodedData() |
|
1741 { |
|
1742 return HasLowUndecodedData(mLowDataThresholdUsecs); |
|
1743 } |
|
1744 |
|
1745 bool MediaDecoderStateMachine::HasLowUndecodedData(double aUsecs) |
|
1746 { |
|
1747 AssertCurrentThreadInMonitor(); |
|
1748 NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA, |
|
1749 "Must have loaded metadata for GetBuffered() to work"); |
|
1750 |
|
1751 bool reliable; |
|
1752 double bytesPerSecond = mDecoder->ComputePlaybackRate(&reliable); |
|
1753 if (!reliable) { |
|
1754 // Default to assuming we have enough |
|
1755 return false; |
|
1756 } |
|
1757 |
|
1758 MediaResource* stream = mDecoder->GetResource(); |
|
1759 int64_t currentPos = stream->Tell(); |
|
1760 int64_t requiredPos = currentPos + int64_t((aUsecs/1000000.0)*bytesPerSecond); |
|
1761 int64_t length = stream->GetLength(); |
|
1762 if (length >= 0) { |
|
1763 requiredPos = std::min(requiredPos, length); |
|
1764 } |
|
1765 |
|
1766 return stream->GetCachedDataEnd(currentPos) < requiredPos; |
|
1767 } |
|
1768 |
|
1769 void |
|
1770 MediaDecoderStateMachine::DecodeError() |
|
1771 { |
|
1772 AssertCurrentThreadInMonitor(); |
|
1773 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); |
|
1774 |
|
1775 // Change state to shutdown before sending error report to MediaDecoder |
|
1776 // and the HTMLMediaElement, so that our pipeline can start exiting |
|
1777 // cleanly during the sync dispatch below. |
|
1778 DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN"); |
|
1779 ScheduleStateMachine(); |
|
1780 mState = DECODER_STATE_SHUTDOWN; |
|
1781 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
1782 |
|
1783 // Dispatch the event to call DecodeError synchronously. This ensures |
|
1784 // we're in shutdown state by the time we exit the decode thread. |
|
1785 // If we just moved to shutdown state here on the decode thread, we may |
|
1786 // cause the state machine to shutdown/free memory without closing its |
|
1787 // media stream properly, and we'll get callbacks from the media stream |
|
1788 // causing a crash. |
|
1789 { |
|
1790 nsCOMPtr<nsIRunnable> event = |
|
1791 NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError); |
|
1792 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
1793 NS_DispatchToMainThread(event, NS_DISPATCH_SYNC); |
|
1794 } |
|
1795 } |
|
1796 |
|
1797 void |
|
1798 MediaDecoderStateMachine::CallDecodeMetadata() |
|
1799 { |
|
1800 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1801 if (mState != DECODER_STATE_DECODING_METADATA) { |
|
1802 return; |
|
1803 } |
|
1804 if (NS_FAILED(DecodeMetadata())) { |
|
1805 DECODER_LOG(PR_LOG_WARNING, "Decode metadata failed, shutting down decoder"); |
|
1806 DecodeError(); |
|
1807 } |
|
1808 } |
|
1809 |
|
1810 nsresult MediaDecoderStateMachine::DecodeMetadata() |
|
1811 { |
|
1812 AssertCurrentThreadInMonitor(); |
|
1813 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); |
|
1814 DECODER_LOG(PR_LOG_DEBUG, "Decoding Media Headers"); |
|
1815 if (mState != DECODER_STATE_DECODING_METADATA) { |
|
1816 return NS_ERROR_FAILURE; |
|
1817 } |
|
1818 EnsureActive(); |
|
1819 |
|
1820 nsresult res; |
|
1821 MediaInfo info; |
|
1822 MetadataTags* tags; |
|
1823 { |
|
1824 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
1825 res = mReader->ReadMetadata(&info, &tags); |
|
1826 } |
|
1827 if (NS_SUCCEEDED(res) && |
|
1828 mState == DECODER_STATE_DECODING_METADATA && |
|
1829 mReader->IsWaitingMediaResources()) { |
|
1830 // change state to DECODER_STATE_WAIT_FOR_RESOURCES |
|
1831 StartWaitForResources(); |
|
1832 return NS_OK; |
|
1833 } |
|
1834 |
|
1835 mInfo = info; |
|
1836 |
|
1837 if (NS_FAILED(res) || (!info.HasValidMedia())) { |
|
1838 return NS_ERROR_FAILURE; |
|
1839 } |
|
1840 mDecoder->StartProgressUpdates(); |
|
1841 mGotDurationFromMetaData = (GetDuration() != -1); |
|
1842 |
|
1843 VideoData* videoData = FindStartTime(); |
|
1844 if (videoData) { |
|
1845 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
1846 RenderVideoFrame(videoData, TimeStamp::Now()); |
|
1847 } |
|
1848 |
|
1849 if (mState == DECODER_STATE_SHUTDOWN) { |
|
1850 return NS_ERROR_FAILURE; |
|
1851 } |
|
1852 |
|
1853 NS_ASSERTION(mStartTime != -1, "Must have start time"); |
|
1854 MOZ_ASSERT((!HasVideo() && !HasAudio()) || |
|
1855 !(mMediaSeekable && mTransportSeekable) || mEndTime != -1, |
|
1856 "Active seekable media should have end time"); |
|
1857 MOZ_ASSERT(!(mMediaSeekable && mTransportSeekable) || |
|
1858 GetDuration() != -1, "Seekable media should have duration"); |
|
1859 DECODER_LOG(PR_LOG_DEBUG, "Media goes from %lld to %lld (duration %lld) " |
|
1860 "transportSeekable=%d, mediaSeekable=%d", |
|
1861 mStartTime, mEndTime, GetDuration(), mTransportSeekable, mMediaSeekable); |
|
1862 |
|
1863 if (HasAudio() && !HasVideo()) { |
|
1864 // We're playing audio only. We don't need to worry about slow video |
|
1865 // decodes causing audio underruns, so don't buffer so much audio in |
|
1866 // order to reduce memory usage. |
|
1867 mAmpleAudioThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR; |
|
1868 mLowAudioThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR; |
|
1869 } |
|
1870 |
|
1871 // Inform the element that we've loaded the metadata and the first frame. |
|
1872 nsCOMPtr<nsIRunnable> metadataLoadedEvent = |
|
1873 new AudioMetadataEventRunner(mDecoder, |
|
1874 mInfo.mAudio.mChannels, |
|
1875 mInfo.mAudio.mRate, |
|
1876 HasAudio(), |
|
1877 HasVideo(), |
|
1878 tags); |
|
1879 NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL); |
|
1880 |
|
1881 if (HasAudio()) { |
|
1882 RefPtr<nsIRunnable> decodeTask( |
|
1883 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded)); |
|
1884 AudioQueue().AddPopListener(decodeTask, mDecodeTaskQueue); |
|
1885 } |
|
1886 if (HasVideo()) { |
|
1887 RefPtr<nsIRunnable> decodeTask( |
|
1888 NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchVideoDecodeTaskIfNeeded)); |
|
1889 VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue); |
|
1890 } |
|
1891 |
|
1892 if (mState == DECODER_STATE_DECODING_METADATA) { |
|
1893 DECODER_LOG(PR_LOG_DEBUG, "Changed state from DECODING_METADATA to DECODING"); |
|
1894 StartDecoding(); |
|
1895 } |
|
1896 |
|
1897 // For very short media FindStartTime() can decode the entire media. |
|
1898 // So we need to check if this has occurred, else our decode pipeline won't |
|
1899 // run (since it doesn't need to) and we won't detect end of stream. |
|
1900 CheckIfDecodeComplete(); |
|
1901 |
|
1902 if ((mState == DECODER_STATE_DECODING || mState == DECODER_STATE_COMPLETED) && |
|
1903 mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING && |
|
1904 !IsPlaying()) |
|
1905 { |
|
1906 StartPlayback(); |
|
1907 } |
|
1908 |
|
1909 return NS_OK; |
|
1910 } |
|
1911 |
|
1912 void MediaDecoderStateMachine::DecodeSeek() |
|
1913 { |
|
1914 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
1915 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); |
|
1916 if (mState != DECODER_STATE_SEEKING) { |
|
1917 return; |
|
1918 } |
|
1919 EnsureActive(); |
|
1920 |
|
1921 // During the seek, don't have a lock on the decoder state, |
|
1922 // otherwise long seek operations can block the main thread. |
|
1923 // The events dispatched to the main thread are SYNC calls. |
|
1924 // These calls are made outside of the decode monitor lock so |
|
1925 // it is safe for the main thread to makes calls that acquire |
|
1926 // the lock since it won't deadlock. We check the state when |
|
1927 // acquiring the lock again in case shutdown has occurred |
|
1928 // during the time when we didn't have the lock. |
|
1929 int64_t seekTime = mSeekTarget.mTime; |
|
1930 mDecoder->StopProgressUpdates(); |
|
1931 |
|
1932 bool currentTimeChanged = false; |
|
1933 const int64_t mediaTime = GetMediaTime(); |
|
1934 if (mediaTime != seekTime) { |
|
1935 currentTimeChanged = true; |
|
1936 // Stop playback now to ensure that while we're outside the monitor |
|
1937 // dispatching SeekingStarted, playback doesn't advance and mess with |
|
1938 // mCurrentFrameTime that we've setting to seekTime here. |
|
1939 StopPlayback(); |
|
1940 UpdatePlaybackPositionInternal(seekTime); |
|
1941 } |
|
1942 |
|
1943 // SeekingStarted will do a UpdateReadyStateForData which will |
|
1944 // inform the element and its users that we have no frames |
|
1945 // to display |
|
1946 { |
|
1947 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
1948 nsCOMPtr<nsIRunnable> startEvent = |
|
1949 NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStarted); |
|
1950 NS_DispatchToMainThread(startEvent, NS_DISPATCH_SYNC); |
|
1951 } |
|
1952 |
|
1953 int64_t newCurrentTime = seekTime; |
|
1954 if (currentTimeChanged) { |
|
1955 // The seek target is different than the current playback position, |
|
1956 // we'll need to seek the playback position, so shutdown our decode |
|
1957 // and audio threads. |
|
1958 StopAudioThread(); |
|
1959 ResetPlayback(); |
|
1960 nsresult res; |
|
1961 { |
|
1962 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
1963 // Now perform the seek. We must not hold the state machine monitor |
|
1964 // while we seek, since the seek reads, which could block on I/O. |
|
1965 res = mReader->Seek(seekTime, |
|
1966 mStartTime, |
|
1967 mEndTime, |
|
1968 mediaTime); |
|
1969 |
|
1970 if (NS_SUCCEEDED(res) && mSeekTarget.mType == SeekTarget::Accurate) { |
|
1971 res = mReader->DecodeToTarget(seekTime); |
|
1972 } |
|
1973 } |
|
1974 |
|
1975 if (NS_SUCCEEDED(res)) { |
|
1976 int64_t nextSampleStartTime = 0; |
|
1977 VideoData* video = nullptr; |
|
1978 { |
|
1979 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
1980 video = mReader->FindStartTime(nextSampleStartTime); |
|
1981 } |
|
1982 |
|
1983 // Setup timestamp state. |
|
1984 if (seekTime == mEndTime) { |
|
1985 newCurrentTime = mAudioStartTime = seekTime; |
|
1986 } else if (HasAudio()) { |
|
1987 AudioData* audio = AudioQueue().PeekFront(); |
|
1988 newCurrentTime = mAudioStartTime = audio ? audio->mTime : seekTime; |
|
1989 } else { |
|
1990 newCurrentTime = video ? video->mTime : seekTime; |
|
1991 } |
|
1992 mPlayDuration = newCurrentTime - mStartTime; |
|
1993 |
|
1994 if (HasVideo()) { |
|
1995 if (video) { |
|
1996 { |
|
1997 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
1998 RenderVideoFrame(video, TimeStamp::Now()); |
|
1999 } |
|
2000 nsCOMPtr<nsIRunnable> event = |
|
2001 NS_NewRunnableMethod(mDecoder, &MediaDecoder::Invalidate); |
|
2002 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
2003 } |
|
2004 } |
|
2005 } else { |
|
2006 DecodeError(); |
|
2007 } |
|
2008 } |
|
2009 mDecoder->StartProgressUpdates(); |
|
2010 if (mState == DECODER_STATE_DECODING_METADATA || |
|
2011 mState == DECODER_STATE_DORMANT || |
|
2012 mState == DECODER_STATE_SHUTDOWN) { |
|
2013 return; |
|
2014 } |
|
2015 |
|
2016 // Change state to DECODING or COMPLETED now. SeekingStopped will |
|
2017 // call MediaDecoderStateMachine::Seek to reset our state to SEEKING |
|
2018 // if we need to seek again. |
|
2019 |
|
2020 nsCOMPtr<nsIRunnable> stopEvent; |
|
2021 bool isLiveStream = mDecoder->GetResource()->GetLength() == -1; |
|
2022 if (GetMediaTime() == mEndTime && !isLiveStream) { |
|
2023 // Seeked to end of media, move to COMPLETED state. Note we don't do |
|
2024 // this if we're playing a live stream, since the end of media will advance |
|
2025 // once we download more data! |
|
2026 DECODER_LOG(PR_LOG_DEBUG, "Changed state from SEEKING (to %lld) to COMPLETED", seekTime); |
|
2027 stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStoppedAtEnd); |
|
2028 // Explicitly set our state so we don't decode further, and so |
|
2029 // we report playback ended to the media element. |
|
2030 mState = DECODER_STATE_COMPLETED; |
|
2031 mIsAudioDecoding = false; |
|
2032 mIsVideoDecoding = false; |
|
2033 DispatchDecodeTasksIfNeeded(); |
|
2034 } else { |
|
2035 DECODER_LOG(PR_LOG_DEBUG, "Changed state from SEEKING (to %lld) to DECODING", seekTime); |
|
2036 stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStopped); |
|
2037 StartDecoding(); |
|
2038 } |
|
2039 |
|
2040 if (newCurrentTime != mediaTime) { |
|
2041 UpdatePlaybackPositionInternal(newCurrentTime); |
|
2042 if (mDecoder->GetDecodedStream()) { |
|
2043 SetSyncPointForMediaStream(); |
|
2044 } |
|
2045 } |
|
2046 |
|
2047 // Try to decode another frame to detect if we're at the end... |
|
2048 DECODER_LOG(PR_LOG_DEBUG, "Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime); |
|
2049 |
|
2050 { |
|
2051 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
2052 NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC); |
|
2053 } |
|
2054 |
|
2055 // Reset quick buffering status. This ensures that if we began the |
|
2056 // seek while quick-buffering, we won't bypass quick buffering mode |
|
2057 // if we need to buffer after the seek. |
|
2058 mQuickBuffering = false; |
|
2059 |
|
2060 ScheduleStateMachine(); |
|
2061 } |
|
2062 |
|
2063 // Runnable to dispose of the decoder and state machine on the main thread. |
|
2064 class nsDecoderDisposeEvent : public nsRunnable { |
|
2065 public: |
|
2066 nsDecoderDisposeEvent(already_AddRefed<MediaDecoder> aDecoder, |
|
2067 already_AddRefed<MediaDecoderStateMachine> aStateMachine) |
|
2068 : mDecoder(aDecoder), mStateMachine(aStateMachine) {} |
|
2069 NS_IMETHOD Run() { |
|
2070 NS_ASSERTION(NS_IsMainThread(), "Must be on main thread."); |
|
2071 mStateMachine->ReleaseDecoder(); |
|
2072 mDecoder->ReleaseStateMachine(); |
|
2073 mStateMachine = nullptr; |
|
2074 mDecoder = nullptr; |
|
2075 return NS_OK; |
|
2076 } |
|
2077 private: |
|
2078 nsRefPtr<MediaDecoder> mDecoder; |
|
2079 nsRefPtr<MediaDecoderStateMachine> mStateMachine; |
|
2080 }; |
|
2081 |
|
2082 // Runnable which dispatches an event to the main thread to dispose of the |
|
2083 // decoder and state machine. This runs on the state machine thread after |
|
2084 // the state machine has shutdown, and all events for that state machine have |
|
2085 // finished running. |
|
2086 class nsDispatchDisposeEvent : public nsRunnable { |
|
2087 public: |
|
2088 nsDispatchDisposeEvent(MediaDecoder* aDecoder, |
|
2089 MediaDecoderStateMachine* aStateMachine) |
|
2090 : mDecoder(aDecoder), mStateMachine(aStateMachine) {} |
|
2091 NS_IMETHOD Run() { |
|
2092 NS_DispatchToMainThread(new nsDecoderDisposeEvent(mDecoder.forget(), |
|
2093 mStateMachine.forget())); |
|
2094 return NS_OK; |
|
2095 } |
|
2096 private: |
|
2097 nsRefPtr<MediaDecoder> mDecoder; |
|
2098 nsRefPtr<MediaDecoderStateMachine> mStateMachine; |
|
2099 }; |
|
2100 |
|
2101 nsresult MediaDecoderStateMachine::RunStateMachine() |
|
2102 { |
|
2103 AssertCurrentThreadInMonitor(); |
|
2104 |
|
2105 MediaResource* resource = mDecoder->GetResource(); |
|
2106 NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER); |
|
2107 |
|
2108 switch (mState) { |
|
2109 case DECODER_STATE_SHUTDOWN: { |
|
2110 if (IsPlaying()) { |
|
2111 StopPlayback(); |
|
2112 } |
|
2113 StopAudioThread(); |
|
2114 // If mAudioThread is non-null after StopAudioThread completes, we are |
|
2115 // running in a nested event loop waiting for Shutdown() on |
|
2116 // mAudioThread to complete. Return to the event loop and let it |
|
2117 // finish processing before continuing with shutdown. |
|
2118 if (mAudioThread) { |
|
2119 MOZ_ASSERT(mStopAudioThread); |
|
2120 return NS_OK; |
|
2121 } |
|
2122 |
|
2123 // The reader's listeners hold references to the state machine, |
|
2124 // creating a cycle which keeps the state machine and its shared |
|
2125 // thread pools alive. So break it here. |
|
2126 AudioQueue().ClearListeners(); |
|
2127 VideoQueue().ClearListeners(); |
|
2128 |
|
2129 { |
|
2130 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
2131 // Wait for the thread decoding to exit. |
|
2132 mDecodeTaskQueue->Shutdown(); |
|
2133 mDecodeTaskQueue = nullptr; |
|
2134 mReader->ReleaseMediaResources(); |
|
2135 } |
|
2136 // Now that those threads are stopped, there's no possibility of |
|
2137 // mPendingWakeDecoder being needed again. Revoke it. |
|
2138 mPendingWakeDecoder = nullptr; |
|
2139 |
|
2140 MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN, |
|
2141 "How did we escape from the shutdown state?"); |
|
2142 // We must daisy-chain these events to destroy the decoder. We must |
|
2143 // destroy the decoder on the main thread, but we can't destroy the |
|
2144 // decoder while this thread holds the decoder monitor. We can't |
|
2145 // dispatch an event to the main thread to destroy the decoder from |
|
2146 // here, as the event may run before the dispatch returns, and we |
|
2147 // hold the decoder monitor here. We also want to guarantee that the |
|
2148 // state machine is destroyed on the main thread, and so the |
|
2149 // event runner running this function (which holds a reference to the |
|
2150 // state machine) needs to finish and be released in order to allow |
|
2151 // that. So we dispatch an event to run after this event runner has |
|
2152 // finished and released its monitor/references. That event then will |
|
2153 // dispatch an event to the main thread to release the decoder and |
|
2154 // state machine. |
|
2155 GetStateMachineThread()->Dispatch( |
|
2156 new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL); |
|
2157 |
|
2158 mTimer->Cancel(); |
|
2159 mTimer = nullptr; |
|
2160 return NS_OK; |
|
2161 } |
|
2162 |
|
2163 case DECODER_STATE_DORMANT: { |
|
2164 if (IsPlaying()) { |
|
2165 StopPlayback(); |
|
2166 } |
|
2167 StopAudioThread(); |
|
2168 // Now that those threads are stopped, there's no possibility of |
|
2169 // mPendingWakeDecoder being needed again. Revoke it. |
|
2170 mPendingWakeDecoder = nullptr; |
|
2171 { |
|
2172 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
2173 // Wait for the thread decoding, if any, to exit. |
|
2174 mDecodeTaskQueue->AwaitIdle(); |
|
2175 mReader->ReleaseMediaResources(); |
|
2176 } |
|
2177 return NS_OK; |
|
2178 } |
|
2179 |
|
2180 case DECODER_STATE_WAIT_FOR_RESOURCES: { |
|
2181 return NS_OK; |
|
2182 } |
|
2183 |
|
2184 case DECODER_STATE_DECODING_METADATA: { |
|
2185 // Ensure we have a decode thread to decode metadata. |
|
2186 return EnqueueDecodeMetadataTask(); |
|
2187 } |
|
2188 |
|
2189 case DECODER_STATE_DECODING: { |
|
2190 if (mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING && |
|
2191 IsPlaying()) |
|
2192 { |
|
2193 // We're playing, but the element/decoder is in paused state. Stop |
|
2194 // playing! |
|
2195 StopPlayback(); |
|
2196 } |
|
2197 |
|
2198 if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING && |
|
2199 !IsPlaying()) { |
|
2200 // We are playing, but the state machine does not know it yet. Tell it |
|
2201 // that it is, so that the clock can be properly queried. |
|
2202 StartPlayback(); |
|
2203 } |
|
2204 |
|
2205 AdvanceFrame(); |
|
2206 NS_ASSERTION(mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING || |
|
2207 IsStateMachineScheduled() || |
|
2208 mPlaybackRate == 0.0, "Must have timer scheduled"); |
|
2209 return NS_OK; |
|
2210 } |
|
2211 |
|
2212 case DECODER_STATE_BUFFERING: { |
|
2213 TimeStamp now = TimeStamp::Now(); |
|
2214 NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time."); |
|
2215 |
|
2216 // We will remain in the buffering state if we've not decoded enough |
|
2217 // data to begin playback, or if we've not downloaded a reasonable |
|
2218 // amount of data inside our buffering time. |
|
2219 TimeDuration elapsed = now - mBufferingStart; |
|
2220 bool isLiveStream = resource->GetLength() == -1; |
|
2221 if ((isLiveStream || !mDecoder->CanPlayThrough()) && |
|
2222 elapsed < TimeDuration::FromSeconds(mBufferingWait * mPlaybackRate) && |
|
2223 (mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS) |
|
2224 : HasLowUndecodedData(mBufferingWait * USECS_PER_S)) && |
|
2225 !mDecoder->IsDataCachedToEndOfResource() && |
|
2226 !resource->IsSuspended()) |
|
2227 { |
|
2228 DECODER_LOG(PR_LOG_DEBUG, "Buffering: wait %ds, timeout in %.3lfs %s", |
|
2229 mBufferingWait, mBufferingWait - elapsed.ToSeconds(), |
|
2230 (mQuickBuffering ? "(quick exit)" : "")); |
|
2231 ScheduleStateMachine(USECS_PER_S); |
|
2232 return NS_OK; |
|
2233 } else { |
|
2234 DECODER_LOG(PR_LOG_DEBUG, "Changed state from BUFFERING to DECODING"); |
|
2235 DECODER_LOG(PR_LOG_DEBUG, "Buffered for %.3lfs", (now - mBufferingStart).ToSeconds()); |
|
2236 StartDecoding(); |
|
2237 } |
|
2238 |
|
2239 // Notify to allow blocked decoder thread to continue |
|
2240 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
2241 UpdateReadyState(); |
|
2242 if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING && |
|
2243 !IsPlaying()) |
|
2244 { |
|
2245 StartPlayback(); |
|
2246 } |
|
2247 NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled"); |
|
2248 return NS_OK; |
|
2249 } |
|
2250 |
|
2251 case DECODER_STATE_SEEKING: { |
|
2252 // Ensure we have a decode thread to perform the seek. |
|
2253 return EnqueueDecodeSeekTask(); |
|
2254 } |
|
2255 |
|
2256 case DECODER_STATE_COMPLETED: { |
|
2257 // Play the remaining media. We want to run AdvanceFrame() at least |
|
2258 // once to ensure the current playback position is advanced to the |
|
2259 // end of the media, and so that we update the readyState. |
|
2260 if (VideoQueue().GetSize() > 0 || |
|
2261 (HasAudio() && !mAudioCompleted) || |
|
2262 (mDecoder->GetDecodedStream() && !mDecoder->GetDecodedStream()->IsFinished())) |
|
2263 { |
|
2264 AdvanceFrame(); |
|
2265 NS_ASSERTION(mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING || |
|
2266 mPlaybackRate == 0 || |
|
2267 IsStateMachineScheduled(), |
|
2268 "Must have timer scheduled"); |
|
2269 return NS_OK; |
|
2270 } |
|
2271 |
|
2272 // StopPlayback in order to reset the IsPlaying() state so audio |
|
2273 // is restarted correctly. |
|
2274 StopPlayback(); |
|
2275 |
|
2276 if (mState != DECODER_STATE_COMPLETED) { |
|
2277 // While we're presenting a frame we can change state. Whatever changed |
|
2278 // our state should have scheduled another state machine run. |
|
2279 NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled"); |
|
2280 return NS_OK; |
|
2281 } |
|
2282 |
|
2283 StopAudioThread(); |
|
2284 // When we're decoding to a stream, the stream's main-thread finish signal |
|
2285 // will take care of calling MediaDecoder::PlaybackEnded. |
|
2286 if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING && |
|
2287 !mDecoder->GetDecodedStream()) { |
|
2288 int64_t videoTime = HasVideo() ? mVideoFrameEndTime : 0; |
|
2289 int64_t clockTime = std::max(mEndTime, std::max(videoTime, GetAudioClock())); |
|
2290 UpdatePlaybackPosition(clockTime); |
|
2291 |
|
2292 { |
|
2293 // Wait for the state change is completed in the main thread, |
|
2294 // otherwise we might see |mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING| |
|
2295 // in next loop and send |MediaDecoder::PlaybackEnded| again to trigger 'ended' |
|
2296 // event twice in the media element. |
|
2297 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
2298 nsCOMPtr<nsIRunnable> event = |
|
2299 NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded); |
|
2300 NS_DispatchToMainThread(event, NS_DISPATCH_SYNC); |
|
2301 } |
|
2302 } |
|
2303 return NS_OK; |
|
2304 } |
|
2305 } |
|
2306 |
|
2307 return NS_OK; |
|
2308 } |
|
2309 |
|
2310 void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData, |
|
2311 TimeStamp aTarget) |
|
2312 { |
|
2313 NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(), |
|
2314 "Should be on state machine or decode thread."); |
|
2315 mDecoder->GetReentrantMonitor().AssertNotCurrentThreadIn(); |
|
2316 |
|
2317 if (aData->mDuplicate) { |
|
2318 return; |
|
2319 } |
|
2320 |
|
2321 VERBOSE_LOG("playing video frame %lld", aData->mTime); |
|
2322 |
|
2323 VideoFrameContainer* container = mDecoder->GetVideoFrameContainer(); |
|
2324 if (container) { |
|
2325 container->SetCurrentFrame(ThebesIntSize(aData->mDisplay), aData->mImage, |
|
2326 aTarget); |
|
2327 } |
|
2328 } |
|
2329 |
|
2330 int64_t |
|
2331 MediaDecoderStateMachine::GetAudioClock() |
|
2332 { |
|
2333 // We must hold the decoder monitor while using the audio stream off the |
|
2334 // audio thread to ensure that it doesn't get destroyed on the audio thread |
|
2335 // while we're using it. |
|
2336 AssertCurrentThreadInMonitor(); |
|
2337 if (!HasAudio() || mAudioCaptured) |
|
2338 return -1; |
|
2339 if (!mAudioStream) { |
|
2340 // Audio thread hasn't played any data yet. |
|
2341 return mAudioStartTime; |
|
2342 } |
|
2343 int64_t t = mAudioStream->GetPosition(); |
|
2344 return (t == -1) ? -1 : t + mAudioStartTime; |
|
2345 } |
|
2346 |
|
2347 int64_t MediaDecoderStateMachine::GetVideoStreamPosition() |
|
2348 { |
|
2349 AssertCurrentThreadInMonitor(); |
|
2350 |
|
2351 if (!IsPlaying()) { |
|
2352 return mPlayDuration + mStartTime; |
|
2353 } |
|
2354 |
|
2355 // The playbackRate has been just been changed, reset the playstartTime. |
|
2356 if (mResetPlayStartTime) { |
|
2357 mPlayStartTime = TimeStamp::Now(); |
|
2358 mResetPlayStartTime = false; |
|
2359 } |
|
2360 |
|
2361 int64_t pos = DurationToUsecs(TimeStamp::Now() - mPlayStartTime) + mPlayDuration; |
|
2362 pos -= mBasePosition; |
|
2363 NS_ASSERTION(pos >= 0, "Video stream position should be positive."); |
|
2364 return mBasePosition + pos * mPlaybackRate + mStartTime; |
|
2365 } |
|
2366 |
|
2367 int64_t MediaDecoderStateMachine::GetClock() |
|
2368 { |
|
2369 AssertCurrentThreadInMonitor(); |
|
2370 |
|
2371 // Determine the clock time. If we've got audio, and we've not reached |
|
2372 // the end of the audio, use the audio clock. However if we've finished |
|
2373 // audio, or don't have audio, use the system clock. If our output is being |
|
2374 // fed to a MediaStream, use that stream as the source of the clock. |
|
2375 int64_t clock_time = -1; |
|
2376 DecodedStreamData* stream = mDecoder->GetDecodedStream(); |
|
2377 if (!IsPlaying()) { |
|
2378 clock_time = mPlayDuration + mStartTime; |
|
2379 } else if (stream) { |
|
2380 clock_time = GetCurrentTimeViaMediaStreamSync(); |
|
2381 } else { |
|
2382 int64_t audio_time = GetAudioClock(); |
|
2383 if (HasAudio() && !mAudioCompleted && audio_time != -1) { |
|
2384 clock_time = audio_time; |
|
2385 // Resync against the audio clock, while we're trusting the |
|
2386 // audio clock. This ensures no "drift", particularly on Linux. |
|
2387 mPlayDuration = clock_time - mStartTime; |
|
2388 mPlayStartTime = TimeStamp::Now(); |
|
2389 } else { |
|
2390 // Audio is disabled on this system. Sync to the system clock. |
|
2391 clock_time = GetVideoStreamPosition(); |
|
2392 // Ensure the clock can never go backwards. |
|
2393 NS_ASSERTION(mCurrentFrameTime <= clock_time || mPlaybackRate <= 0, |
|
2394 "Clock should go forwards if the playback rate is > 0."); |
|
2395 } |
|
2396 } |
|
2397 return clock_time; |
|
2398 } |
|
2399 |
|
2400 void MediaDecoderStateMachine::AdvanceFrame() |
|
2401 { |
|
2402 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread."); |
|
2403 AssertCurrentThreadInMonitor(); |
|
2404 NS_ASSERTION(!HasAudio() || mAudioStartTime != -1, |
|
2405 "Should know audio start time if we have audio."); |
|
2406 |
|
2407 if (mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING) { |
|
2408 return; |
|
2409 } |
|
2410 |
|
2411 // If playbackRate is 0.0, we should stop the progress, but not be in paused |
|
2412 // state, per spec. |
|
2413 if (mPlaybackRate == 0.0) { |
|
2414 return; |
|
2415 } |
|
2416 |
|
2417 int64_t clock_time = GetClock(); |
|
2418 // Skip frames up to the frame at the playback position, and figure out |
|
2419 // the time remaining until it's time to display the next frame. |
|
2420 int64_t remainingTime = AUDIO_DURATION_USECS; |
|
2421 NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time."); |
|
2422 nsAutoPtr<VideoData> currentFrame; |
|
2423 #ifdef PR_LOGGING |
|
2424 int32_t droppedFrames = 0; |
|
2425 #endif |
|
2426 if (VideoQueue().GetSize() > 0) { |
|
2427 VideoData* frame = VideoQueue().PeekFront(); |
|
2428 while (mRealTime || clock_time >= frame->mTime) { |
|
2429 mVideoFrameEndTime = frame->GetEndTime(); |
|
2430 currentFrame = frame; |
|
2431 #ifdef PR_LOGGING |
|
2432 VERBOSE_LOG("discarding video frame %lld", frame->mTime); |
|
2433 if (droppedFrames++) { |
|
2434 VERBOSE_LOG("discarding video frame %lld (%d so far)", frame->mTime, droppedFrames-1); |
|
2435 } |
|
2436 #endif |
|
2437 VideoQueue().PopFront(); |
|
2438 // Notify the decode thread that the video queue's buffers may have |
|
2439 // free'd up space for more frames. |
|
2440 mDecoder->GetReentrantMonitor().NotifyAll(); |
|
2441 mDecoder->UpdatePlaybackOffset(frame->mOffset); |
|
2442 if (VideoQueue().GetSize() == 0) |
|
2443 break; |
|
2444 frame = VideoQueue().PeekFront(); |
|
2445 } |
|
2446 // Current frame has already been presented, wait until it's time to |
|
2447 // present the next frame. |
|
2448 if (frame && !currentFrame) { |
|
2449 int64_t now = IsPlaying() ? clock_time : mPlayDuration; |
|
2450 |
|
2451 remainingTime = frame->mTime - now; |
|
2452 } |
|
2453 } |
|
2454 |
|
2455 // Check to see if we don't have enough data to play up to the next frame. |
|
2456 // If we don't, switch to buffering mode. |
|
2457 MediaResource* resource = mDecoder->GetResource(); |
|
2458 if (mState == DECODER_STATE_DECODING && |
|
2459 mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING && |
|
2460 HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) && |
|
2461 !mDecoder->IsDataCachedToEndOfResource() && |
|
2462 !resource->IsSuspended()) { |
|
2463 if (JustExitedQuickBuffering() || HasLowUndecodedData()) { |
|
2464 if (currentFrame) { |
|
2465 VideoQueue().PushFront(currentFrame.forget()); |
|
2466 } |
|
2467 StartBuffering(); |
|
2468 // Don't go straight back to the state machine loop since that might |
|
2469 // cause us to start decoding again and we could flip-flop between |
|
2470 // decoding and quick-buffering. |
|
2471 ScheduleStateMachine(USECS_PER_S); |
|
2472 return; |
|
2473 } |
|
2474 } |
|
2475 |
|
2476 // We've got enough data to keep playing until at least the next frame. |
|
2477 // Start playing now if need be. |
|
2478 if (!IsPlaying() && ((mFragmentEndTime >= 0 && clock_time < mFragmentEndTime) || mFragmentEndTime < 0)) { |
|
2479 StartPlayback(); |
|
2480 } |
|
2481 |
|
2482 if (currentFrame) { |
|
2483 // Decode one frame and display it. |
|
2484 TimeStamp presTime = mPlayStartTime - UsecsToDuration(mPlayDuration) + |
|
2485 UsecsToDuration(currentFrame->mTime - mStartTime); |
|
2486 NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time"); |
|
2487 { |
|
2488 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
2489 // If we have video, we want to increment the clock in steps of the frame |
|
2490 // duration. |
|
2491 RenderVideoFrame(currentFrame, presTime); |
|
2492 } |
|
2493 // If we're no longer playing after dropping and reacquiring the lock, |
|
2494 // playback must've been stopped on the decode thread (by a seek, for |
|
2495 // example). In that case, the current frame is probably out of date. |
|
2496 if (!IsPlaying()) { |
|
2497 ScheduleStateMachine(); |
|
2498 return; |
|
2499 } |
|
2500 MediaDecoder::FrameStatistics& frameStats = mDecoder->GetFrameStatistics(); |
|
2501 frameStats.NotifyPresentedFrame(); |
|
2502 remainingTime = currentFrame->GetEndTime() - clock_time; |
|
2503 currentFrame = nullptr; |
|
2504 } |
|
2505 |
|
2506 // Cap the current time to the larger of the audio and video end time. |
|
2507 // This ensures that if we're running off the system clock, we don't |
|
2508 // advance the clock to after the media end time. |
|
2509 if (mVideoFrameEndTime != -1 || mAudioEndTime != -1) { |
|
2510 // These will be non -1 if we've displayed a video frame, or played an audio frame. |
|
2511 clock_time = std::min(clock_time, std::max(mVideoFrameEndTime, mAudioEndTime)); |
|
2512 if (clock_time > GetMediaTime()) { |
|
2513 // Only update the playback position if the clock time is greater |
|
2514 // than the previous playback position. The audio clock can |
|
2515 // sometimes report a time less than its previously reported in |
|
2516 // some situations, and we need to gracefully handle that. |
|
2517 UpdatePlaybackPosition(clock_time); |
|
2518 } |
|
2519 } |
|
2520 |
|
2521 // If the number of audio/video frames queued has changed, either by |
|
2522 // this function popping and playing a video frame, or by the audio |
|
2523 // thread popping and playing an audio frame, we may need to update our |
|
2524 // ready state. Post an update to do so. |
|
2525 UpdateReadyState(); |
|
2526 |
|
2527 ScheduleStateMachine(remainingTime); |
|
2528 } |
|
2529 |
|
2530 void MediaDecoderStateMachine::Wait(int64_t aUsecs) { |
|
2531 NS_ASSERTION(OnAudioThread(), "Only call on the audio thread"); |
|
2532 AssertCurrentThreadInMonitor(); |
|
2533 TimeStamp end = TimeStamp::Now() + UsecsToDuration(std::max<int64_t>(USECS_PER_MS, aUsecs)); |
|
2534 TimeStamp now; |
|
2535 while ((now = TimeStamp::Now()) < end && |
|
2536 mState != DECODER_STATE_SHUTDOWN && |
|
2537 mState != DECODER_STATE_SEEKING && |
|
2538 !mStopAudioThread && |
|
2539 IsPlaying()) |
|
2540 { |
|
2541 int64_t ms = static_cast<int64_t>(NS_round((end - now).ToSeconds() * 1000)); |
|
2542 if (ms == 0 || ms > UINT32_MAX) { |
|
2543 break; |
|
2544 } |
|
2545 mDecoder->GetReentrantMonitor().Wait(PR_MillisecondsToInterval(static_cast<uint32_t>(ms))); |
|
2546 } |
|
2547 } |
|
2548 |
|
2549 VideoData* MediaDecoderStateMachine::FindStartTime() |
|
2550 { |
|
2551 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); |
|
2552 AssertCurrentThreadInMonitor(); |
|
2553 int64_t startTime = 0; |
|
2554 mStartTime = 0; |
|
2555 VideoData* v = nullptr; |
|
2556 { |
|
2557 ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); |
|
2558 v = mReader->FindStartTime(startTime); |
|
2559 } |
|
2560 if (startTime != 0) { |
|
2561 mStartTime = startTime; |
|
2562 if (mGotDurationFromMetaData) { |
|
2563 NS_ASSERTION(mEndTime != -1, |
|
2564 "We should have mEndTime as supplied duration here"); |
|
2565 // We were specified a duration from a Content-Duration HTTP header. |
|
2566 // Adjust mEndTime so that mEndTime-mStartTime matches the specified |
|
2567 // duration. |
|
2568 mEndTime = mStartTime + mEndTime; |
|
2569 } |
|
2570 } |
|
2571 // Set the audio start time to be start of media. If this lies before the |
|
2572 // first actual audio frame we have, we'll inject silence during playback |
|
2573 // to ensure the audio starts at the correct time. |
|
2574 mAudioStartTime = mStartTime; |
|
2575 DECODER_LOG(PR_LOG_DEBUG, "Media start time is %lld", mStartTime); |
|
2576 return v; |
|
2577 } |
|
2578 |
|
2579 void MediaDecoderStateMachine::UpdateReadyState() { |
|
2580 AssertCurrentThreadInMonitor(); |
|
2581 |
|
2582 MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus(); |
|
2583 if (nextFrameStatus == mLastFrameStatus) { |
|
2584 return; |
|
2585 } |
|
2586 mLastFrameStatus = nextFrameStatus; |
|
2587 |
|
2588 /* This is a bit tricky. MediaDecoder::UpdateReadyStateForData will run on |
|
2589 * the main thread and re-evaluate GetNextFrameStatus there, passing it to |
|
2590 * HTMLMediaElement::UpdateReadyStateForData. It doesn't use the value of |
|
2591 * GetNextFrameStatus we computed here, because what we're computing here |
|
2592 * could be stale by the time MediaDecoder::UpdateReadyStateForData runs. |
|
2593 * We only compute GetNextFrameStatus here to avoid posting runnables to the main |
|
2594 * thread unnecessarily. |
|
2595 */ |
|
2596 nsCOMPtr<nsIRunnable> event; |
|
2597 event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::UpdateReadyStateForData); |
|
2598 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
|
2599 } |
|
2600 |
|
2601 bool MediaDecoderStateMachine::JustExitedQuickBuffering() |
|
2602 { |
|
2603 return !mDecodeStartTime.IsNull() && |
|
2604 mQuickBuffering && |
|
2605 (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS); |
|
2606 } |
|
2607 |
|
2608 void MediaDecoderStateMachine::StartBuffering() |
|
2609 { |
|
2610 AssertCurrentThreadInMonitor(); |
|
2611 |
|
2612 if (mState != DECODER_STATE_DECODING) { |
|
2613 // We only move into BUFFERING state if we're actually decoding. |
|
2614 // If we're currently doing something else, we don't need to buffer, |
|
2615 // and more importantly, we shouldn't overwrite mState to interrupt |
|
2616 // the current operation, as that could leave us in an inconsistent |
|
2617 // state! |
|
2618 return; |
|
2619 } |
|
2620 |
|
2621 if (IsPlaying()) { |
|
2622 StopPlayback(); |
|
2623 } |
|
2624 |
|
2625 TimeDuration decodeDuration = TimeStamp::Now() - mDecodeStartTime; |
|
2626 // Go into quick buffering mode provided we've not just left buffering using |
|
2627 // a "quick exit". This stops us flip-flopping between playing and buffering |
|
2628 // when the download speed is similar to the decode speed. |
|
2629 mQuickBuffering = |
|
2630 !JustExitedQuickBuffering() && |
|
2631 decodeDuration < UsecsToDuration(QUICK_BUFFER_THRESHOLD_USECS); |
|
2632 mBufferingStart = TimeStamp::Now(); |
|
2633 |
|
2634 // We need to tell the element that buffering has started. |
|
2635 // We can't just directly send an asynchronous runnable that |
|
2636 // eventually fires the "waiting" event. The problem is that |
|
2637 // there might be pending main-thread events, such as "data |
|
2638 // received" notifications, that mean we're not actually still |
|
2639 // buffering by the time this runnable executes. So instead |
|
2640 // we just trigger UpdateReadyStateForData; when it runs, it |
|
2641 // will check the current state and decide whether to tell |
|
2642 // the element we're buffering or not. |
|
2643 UpdateReadyState(); |
|
2644 mState = DECODER_STATE_BUFFERING; |
|
2645 DECODER_LOG(PR_LOG_DEBUG, "Changed state from DECODING to BUFFERING, decoded for %.3lfs", |
|
2646 decodeDuration.ToSeconds()); |
|
2647 #ifdef PR_LOGGING |
|
2648 MediaDecoder::Statistics stats = mDecoder->GetStatistics(); |
|
2649 DECODER_LOG(PR_LOG_DEBUG, "Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s", |
|
2650 stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)", |
|
2651 stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"); |
|
2652 #endif |
|
2653 } |
|
2654 |
|
2655 nsresult MediaDecoderStateMachine::GetBuffered(dom::TimeRanges* aBuffered) { |
|
2656 MediaResource* resource = mDecoder->GetResource(); |
|
2657 NS_ENSURE_TRUE(resource, NS_ERROR_FAILURE); |
|
2658 resource->Pin(); |
|
2659 nsresult res = mReader->GetBuffered(aBuffered, mStartTime); |
|
2660 resource->Unpin(); |
|
2661 return res; |
|
2662 } |
|
2663 |
|
2664 nsresult MediaDecoderStateMachine::CallRunStateMachine() |
|
2665 { |
|
2666 AssertCurrentThreadInMonitor(); |
|
2667 NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread."); |
|
2668 |
|
2669 // If audio is being captured, stop the audio thread if it's running |
|
2670 if (mAudioCaptured) { |
|
2671 StopAudioThread(); |
|
2672 } |
|
2673 |
|
2674 MOZ_ASSERT(!mInRunningStateMachine, "State machine cycles must run in sequence!"); |
|
2675 mTimeout = TimeStamp(); |
|
2676 mInRunningStateMachine = true; |
|
2677 nsresult res = RunStateMachine(); |
|
2678 mInRunningStateMachine = false; |
|
2679 return res; |
|
2680 } |
|
2681 |
|
2682 nsresult MediaDecoderStateMachine::TimeoutExpired(int aTimerId) |
|
2683 { |
|
2684 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
2685 NS_ASSERTION(OnStateMachineThread(), "Must be on state machine thread"); |
|
2686 mTimer->Cancel(); |
|
2687 if (mTimerId == aTimerId) { |
|
2688 return CallRunStateMachine(); |
|
2689 } else { |
|
2690 return NS_OK; |
|
2691 } |
|
2692 } |
|
2693 |
|
2694 void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() { |
|
2695 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
2696 DispatchAudioDecodeTaskIfNeeded(); |
|
2697 DispatchVideoDecodeTaskIfNeeded(); |
|
2698 } |
|
2699 |
|
2700 class TimerEvent : public nsITimerCallback, public nsRunnable { |
|
2701 NS_DECL_THREADSAFE_ISUPPORTS |
|
2702 public: |
|
2703 TimerEvent(MediaDecoderStateMachine* aStateMachine, int aTimerId) |
|
2704 : mStateMachine(aStateMachine), mTimerId(aTimerId) {} |
|
2705 |
|
2706 NS_IMETHOD Run() MOZ_OVERRIDE { |
|
2707 return mStateMachine->TimeoutExpired(mTimerId); |
|
2708 } |
|
2709 |
|
2710 NS_IMETHOD Notify(nsITimer* aTimer) { |
|
2711 return mStateMachine->TimeoutExpired(mTimerId); |
|
2712 } |
|
2713 private: |
|
2714 const nsRefPtr<MediaDecoderStateMachine> mStateMachine; |
|
2715 int mTimerId; |
|
2716 }; |
|
2717 |
|
2718 NS_IMPL_ISUPPORTS(TimerEvent, nsITimerCallback, nsIRunnable); |
|
2719 |
|
2720 nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) { |
|
2721 AssertCurrentThreadInMonitor(); |
|
2722 NS_ABORT_IF_FALSE(GetStateMachineThread(), |
|
2723 "Must have a state machine thread to schedule"); |
|
2724 |
|
2725 if (mState == DECODER_STATE_SHUTDOWN) { |
|
2726 return NS_ERROR_FAILURE; |
|
2727 } |
|
2728 aUsecs = std::max<int64_t>(aUsecs, 0); |
|
2729 |
|
2730 TimeStamp timeout = TimeStamp::Now() + UsecsToDuration(aUsecs); |
|
2731 if (!mTimeout.IsNull() && timeout >= mTimeout) { |
|
2732 // We've already scheduled a timer set to expire at or before this time, |
|
2733 // or have an event dispatched to run the state machine. |
|
2734 return NS_OK; |
|
2735 } |
|
2736 |
|
2737 uint32_t ms = static_cast<uint32_t>((aUsecs / USECS_PER_MS) & 0xFFFFFFFF); |
|
2738 if (mRealTime && ms > 40) { |
|
2739 ms = 40; |
|
2740 } |
|
2741 |
|
2742 // Don't cancel the timer here for this function will be called from |
|
2743 // different threads. |
|
2744 |
|
2745 nsresult rv = NS_ERROR_FAILURE; |
|
2746 nsRefPtr<TimerEvent> event = new TimerEvent(this, mTimerId+1); |
|
2747 |
|
2748 if (ms == 0) { |
|
2749 // Dispatch a runnable to the state machine thread when delay is 0. |
|
2750 // It will has less latency than dispatching a runnable to the state |
|
2751 // machine thread which will then schedule a zero-delay timer. |
|
2752 rv = GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL); |
|
2753 } else if (OnStateMachineThread()) { |
|
2754 rv = mTimer->InitWithCallback(event, ms, nsITimer::TYPE_ONE_SHOT); |
|
2755 } else { |
|
2756 MOZ_ASSERT(false, "non-zero delay timer should be only scheduled in state machine thread"); |
|
2757 } |
|
2758 |
|
2759 if (NS_SUCCEEDED(rv)) { |
|
2760 mTimeout = timeout; |
|
2761 ++mTimerId; |
|
2762 } else { |
|
2763 NS_WARNING("Failed to schedule state machine"); |
|
2764 } |
|
2765 |
|
2766 return rv; |
|
2767 } |
|
2768 |
|
2769 bool MediaDecoderStateMachine::OnDecodeThread() const |
|
2770 { |
|
2771 return mDecodeTaskQueue->IsCurrentThreadIn(); |
|
2772 } |
|
2773 |
|
2774 bool MediaDecoderStateMachine::OnStateMachineThread() const |
|
2775 { |
|
2776 bool rv = false; |
|
2777 mStateMachineThreadPool->IsOnCurrentThread(&rv); |
|
2778 return rv; |
|
2779 } |
|
2780 |
|
2781 nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() |
|
2782 { |
|
2783 return mStateMachineThreadPool->GetEventTarget(); |
|
2784 } |
|
2785 |
|
2786 void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate) |
|
2787 { |
|
2788 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
2789 NS_ASSERTION(aPlaybackRate != 0, |
|
2790 "PlaybackRate == 0 should be handled before this function."); |
|
2791 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
2792 |
|
2793 if (mPlaybackRate == aPlaybackRate) { |
|
2794 return; |
|
2795 } |
|
2796 |
|
2797 // Get position of the last time we changed the rate. |
|
2798 if (!HasAudio()) { |
|
2799 // mBasePosition is a position in the video stream, not an absolute time. |
|
2800 if (mState == DECODER_STATE_SEEKING) { |
|
2801 mBasePosition = mSeekTarget.mTime - mStartTime; |
|
2802 } else { |
|
2803 mBasePosition = GetVideoStreamPosition(); |
|
2804 } |
|
2805 mPlayDuration = mBasePosition; |
|
2806 mResetPlayStartTime = true; |
|
2807 mPlayStartTime = TimeStamp::Now(); |
|
2808 } |
|
2809 |
|
2810 mPlaybackRate = aPlaybackRate; |
|
2811 } |
|
2812 |
|
2813 void MediaDecoderStateMachine::SetPreservesPitch(bool aPreservesPitch) |
|
2814 { |
|
2815 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); |
|
2816 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); |
|
2817 |
|
2818 mPreservesPitch = aPreservesPitch; |
|
2819 } |
|
2820 |
|
2821 void |
|
2822 MediaDecoderStateMachine::SetMinimizePrerollUntilPlaybackStarts() |
|
2823 { |
|
2824 AssertCurrentThreadInMonitor(); |
|
2825 mMinimizePreroll = true; |
|
2826 } |
|
2827 |
|
2828 bool MediaDecoderStateMachine::IsShutdown() |
|
2829 { |
|
2830 AssertCurrentThreadInMonitor(); |
|
2831 return GetState() == DECODER_STATE_SHUTDOWN; |
|
2832 } |
|
2833 |
|
2834 void MediaDecoderStateMachine::QueueMetadata(int64_t aPublishTime, |
|
2835 int aChannels, |
|
2836 int aRate, |
|
2837 bool aHasAudio, |
|
2838 bool aHasVideo, |
|
2839 MetadataTags* aTags) |
|
2840 { |
|
2841 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); |
|
2842 AssertCurrentThreadInMonitor(); |
|
2843 TimedMetadata* metadata = new TimedMetadata; |
|
2844 metadata->mPublishTime = aPublishTime; |
|
2845 metadata->mChannels = aChannels; |
|
2846 metadata->mRate = aRate; |
|
2847 metadata->mHasAudio = aHasAudio; |
|
2848 metadata->mHasVideo = aHasVideo; |
|
2849 metadata->mTags = aTags; |
|
2850 mMetadataManager.QueueMetadata(metadata); |
|
2851 } |
|
2852 |
|
2853 } // namespace mozilla |
|
2854 |
|
2855 // avoid redefined macro in unified build |
|
2856 #undef DECODER_LOG |
|
2857 #undef VERBOSE_LOG |