1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/MediaDecoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1803 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "MediaDecoder.h" 1.11 +#include "mozilla/FloatingPoint.h" 1.12 +#include "mozilla/MathAlgorithms.h" 1.13 +#include <limits> 1.14 +#include "nsIObserver.h" 1.15 +#include "nsTArray.h" 1.16 +#include "VideoUtils.h" 1.17 +#include "MediaDecoderStateMachine.h" 1.18 +#include "mozilla/dom/TimeRanges.h" 1.19 +#include "ImageContainer.h" 1.20 +#include "MediaResource.h" 1.21 +#include "nsError.h" 1.22 +#include "mozilla/Preferences.h" 1.23 +#include "mozilla/StaticPtr.h" 1.24 +#include "nsIMemoryReporter.h" 1.25 +#include "nsComponentManagerUtils.h" 1.26 +#include "nsITimer.h" 1.27 +#include <algorithm> 1.28 +#include "MediaShutdownManager.h" 1.29 +#include "AudioChannelService.h" 1.30 + 1.31 +#ifdef MOZ_WMF 1.32 +#include "WMFDecoder.h" 1.33 +#endif 1.34 + 1.35 +using namespace mozilla::layers; 1.36 +using namespace mozilla::dom; 1.37 + 1.38 +namespace mozilla { 1.39 + 1.40 +// Number of milliseconds between progress events as defined by spec 1.41 +static const uint32_t PROGRESS_MS = 350; 1.42 + 1.43 +// Number of milliseconds of no data before a stall event is fired as defined by spec 1.44 +static const uint32_t STALL_MS = 3000; 1.45 + 1.46 +// Number of estimated seconds worth of data we need to have buffered 1.47 +// ahead of the current playback position before we allow the media decoder 1.48 +// to report that it can play through the entire media without the decode 1.49 +// catching up with the download. Having this margin make the 1.50 +// MediaDecoder::CanPlayThrough() calculation more stable in the case of 1.51 +// fluctuating bitrates. 1.52 +static const int64_t CAN_PLAY_THROUGH_MARGIN = 1; 1.53 + 1.54 +// avoid redefined macro in unified build 1.55 +#undef DECODER_LOG 1.56 + 1.57 +#ifdef PR_LOGGING 1.58 +PRLogModuleInfo* gMediaDecoderLog; 1.59 +#define DECODER_LOG(type, msg, ...) \ 1.60 + PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, this, ##__VA_ARGS__)) 1.61 +#else 1.62 +#define DECODER_LOG(type, msg, ...) 1.63 +#endif 1.64 + 1.65 +class MediaMemoryTracker : public nsIMemoryReporter 1.66 +{ 1.67 + NS_DECL_THREADSAFE_ISUPPORTS 1.68 + NS_DECL_NSIMEMORYREPORTER 1.69 + 1.70 + MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf); 1.71 + 1.72 + MediaMemoryTracker(); 1.73 + virtual ~MediaMemoryTracker(); 1.74 + void InitMemoryReporter(); 1.75 + 1.76 + static StaticRefPtr<MediaMemoryTracker> sUniqueInstance; 1.77 + 1.78 + static MediaMemoryTracker* UniqueInstance() { 1.79 + if (!sUniqueInstance) { 1.80 + sUniqueInstance = new MediaMemoryTracker(); 1.81 + sUniqueInstance->InitMemoryReporter(); 1.82 + } 1.83 + return sUniqueInstance; 1.84 + } 1.85 + 1.86 + typedef nsTArray<MediaDecoder*> DecodersArray; 1.87 + static DecodersArray& Decoders() { 1.88 + return UniqueInstance()->mDecoders; 1.89 + } 1.90 + 1.91 + DecodersArray mDecoders; 1.92 + 1.93 +public: 1.94 + static void AddMediaDecoder(MediaDecoder* aDecoder) 1.95 + { 1.96 + Decoders().AppendElement(aDecoder); 1.97 + } 1.98 + 1.99 + static void RemoveMediaDecoder(MediaDecoder* aDecoder) 1.100 + { 1.101 + DecodersArray& decoders = Decoders(); 1.102 + decoders.RemoveElement(aDecoder); 1.103 + if (decoders.IsEmpty()) { 1.104 + sUniqueInstance = nullptr; 1.105 + } 1.106 + } 1.107 +}; 1.108 + 1.109 +StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance; 1.110 + 1.111 +NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter) 1.112 + 1.113 +NS_IMPL_ISUPPORTS(MediaDecoder, nsIObserver) 1.114 + 1.115 +void MediaDecoder::SetDormantIfNecessary(bool aDormant) 1.116 +{ 1.117 + MOZ_ASSERT(NS_IsMainThread()); 1.118 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.119 + 1.120 + if (!mDecoderStateMachine || !mDecoderStateMachine->IsDormantNeeded() || (mPlayState == PLAY_STATE_SHUTDOWN)) { 1.121 + return; 1.122 + } 1.123 + 1.124 + if (mIsDormant == aDormant) { 1.125 + // no change to dormant state 1.126 + return; 1.127 + } 1.128 + 1.129 + if(aDormant) { 1.130 + // enter dormant state 1.131 + StopProgress(); 1.132 + DestroyDecodedStream(); 1.133 + mDecoderStateMachine->SetDormant(true); 1.134 + 1.135 + mRequestedSeekTarget = SeekTarget(mCurrentTime, SeekTarget::Accurate); 1.136 + if (mPlayState == PLAY_STATE_PLAYING){ 1.137 + mNextState = PLAY_STATE_PLAYING; 1.138 + } else { 1.139 + mNextState = PLAY_STATE_PAUSED; 1.140 + } 1.141 + mNextState = mPlayState; 1.142 + mIsDormant = true; 1.143 + mIsExitingDormant = false; 1.144 + ChangeState(PLAY_STATE_LOADING); 1.145 + } else if ((aDormant != true) && (mPlayState == PLAY_STATE_LOADING)) { 1.146 + // exit dormant state 1.147 + // trigger to state machine. 1.148 + mDecoderStateMachine->SetDormant(false); 1.149 + mIsExitingDormant = true; 1.150 + } 1.151 +} 1.152 + 1.153 +void MediaDecoder::Pause() 1.154 +{ 1.155 + MOZ_ASSERT(NS_IsMainThread()); 1.156 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.157 + if ((mPlayState == PLAY_STATE_LOADING && mIsDormant) || mPlayState == PLAY_STATE_SEEKING || mPlayState == PLAY_STATE_ENDED) { 1.158 + mNextState = PLAY_STATE_PAUSED; 1.159 + return; 1.160 + } 1.161 + 1.162 + ChangeState(PLAY_STATE_PAUSED); 1.163 +} 1.164 + 1.165 +void MediaDecoder::SetVolume(double aVolume) 1.166 +{ 1.167 + MOZ_ASSERT(NS_IsMainThread()); 1.168 + mInitialVolume = aVolume; 1.169 + if (mDecoderStateMachine) { 1.170 + mDecoderStateMachine->SetVolume(aVolume); 1.171 + } 1.172 +} 1.173 + 1.174 +void MediaDecoder::SetAudioCaptured(bool aCaptured) 1.175 +{ 1.176 + MOZ_ASSERT(NS_IsMainThread()); 1.177 + mInitialAudioCaptured = aCaptured; 1.178 + if (mDecoderStateMachine) { 1.179 + mDecoderStateMachine->SetAudioCaptured(aCaptured); 1.180 + } 1.181 +} 1.182 + 1.183 +void MediaDecoder::ConnectDecodedStreamToOutputStream(OutputStreamData* aStream) 1.184 +{ 1.185 + NS_ASSERTION(!aStream->mPort, "Already connected?"); 1.186 + 1.187 + // The output stream must stay in sync with the decoded stream, so if 1.188 + // either stream is blocked, we block the other. 1.189 + aStream->mPort = aStream->mStream->AllocateInputPort(mDecodedStream->mStream, 1.190 + MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT); 1.191 + // Unblock the output stream now. While it's connected to mDecodedStream, 1.192 + // mDecodedStream is responsible for controlling blocking. 1.193 + aStream->mStream->ChangeExplicitBlockerCount(-1); 1.194 +} 1.195 + 1.196 +MediaDecoder::DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder, 1.197 + int64_t aInitialTime, 1.198 + SourceMediaStream* aStream) 1.199 + : mLastAudioPacketTime(-1), 1.200 + mLastAudioPacketEndTime(-1), 1.201 + mAudioFramesWritten(0), 1.202 + mInitialTime(aInitialTime), 1.203 + mNextVideoTime(aInitialTime), 1.204 + mDecoder(aDecoder), 1.205 + mStreamInitialized(false), 1.206 + mHaveSentFinish(false), 1.207 + mHaveSentFinishAudio(false), 1.208 + mHaveSentFinishVideo(false), 1.209 + mStream(aStream), 1.210 + mHaveBlockedForPlayState(false), 1.211 + mHaveBlockedForStateMachineNotPlaying(false) 1.212 +{ 1.213 + mListener = new DecodedStreamGraphListener(mStream, this); 1.214 + mStream->AddListener(mListener); 1.215 +} 1.216 + 1.217 +MediaDecoder::DecodedStreamData::~DecodedStreamData() 1.218 +{ 1.219 + mListener->Forget(); 1.220 + mStream->Destroy(); 1.221 +} 1.222 + 1.223 +MediaDecoder::DecodedStreamGraphListener::DecodedStreamGraphListener(MediaStream* aStream, 1.224 + DecodedStreamData* aData) 1.225 + : mData(aData), 1.226 + mMutex("MediaDecoder::DecodedStreamData::mMutex"), 1.227 + mStream(aStream), 1.228 + mLastOutputTime(aStream->GetCurrentTime()), 1.229 + mStreamFinishedOnMainThread(false) 1.230 +{ 1.231 +} 1.232 + 1.233 +void 1.234 +MediaDecoder::DecodedStreamGraphListener::NotifyOutput(MediaStreamGraph* aGraph, 1.235 + GraphTime aCurrentTime) 1.236 +{ 1.237 + MutexAutoLock lock(mMutex); 1.238 + if (mStream) { 1.239 + mLastOutputTime = mStream->GraphTimeToStreamTime(aCurrentTime); 1.240 + } 1.241 +} 1.242 + 1.243 +void 1.244 +MediaDecoder::DecodedStreamGraphListener::DoNotifyFinished() 1.245 +{ 1.246 + if (mData && mData->mDecoder) { 1.247 + if (mData->mDecoder->GetState() == PLAY_STATE_PLAYING) { 1.248 + nsCOMPtr<nsIRunnable> event = 1.249 + NS_NewRunnableMethod(mData->mDecoder, &MediaDecoder::PlaybackEnded); 1.250 + NS_DispatchToCurrentThread(event); 1.251 + } 1.252 + } 1.253 + 1.254 + MutexAutoLock lock(mMutex); 1.255 + mStreamFinishedOnMainThread = true; 1.256 +} 1.257 + 1.258 +void 1.259 +MediaDecoder::DecodedStreamGraphListener::NotifyFinished(MediaStreamGraph* aGraph) 1.260 +{ 1.261 + nsCOMPtr<nsIRunnable> event = 1.262 + NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished); 1.263 + aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget()); 1.264 +} 1.265 + 1.266 +void MediaDecoder::DestroyDecodedStream() 1.267 +{ 1.268 + MOZ_ASSERT(NS_IsMainThread()); 1.269 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.270 + 1.271 + // All streams are having their SourceMediaStream disconnected, so they 1.272 + // need to be explicitly blocked again. 1.273 + for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) { 1.274 + OutputStreamData& os = mOutputStreams[i]; 1.275 + // During cycle collection, nsDOMMediaStream can be destroyed and send 1.276 + // its Destroy message before this decoder is destroyed. So we have to 1.277 + // be careful not to send any messages after the Destroy(). 1.278 + if (os.mStream->IsDestroyed()) { 1.279 + // Probably the DOM MediaStream was GCed. Clean up. 1.280 + os.mPort->Destroy(); 1.281 + mOutputStreams.RemoveElementAt(i); 1.282 + continue; 1.283 + } 1.284 + os.mStream->ChangeExplicitBlockerCount(1); 1.285 + // Explicitly remove all existing ports. This is not strictly necessary but it's 1.286 + // good form. 1.287 + os.mPort->Destroy(); 1.288 + os.mPort = nullptr; 1.289 + } 1.290 + 1.291 + mDecodedStream = nullptr; 1.292 +} 1.293 + 1.294 +void MediaDecoder::UpdateStreamBlockingForStateMachinePlaying() 1.295 +{ 1.296 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.297 + if (!mDecodedStream) { 1.298 + return; 1.299 + } 1.300 + if (mDecoderStateMachine) { 1.301 + mDecoderStateMachine->SetSyncPointForMediaStream(); 1.302 + } 1.303 + bool blockForStateMachineNotPlaying = 1.304 + mDecoderStateMachine && !mDecoderStateMachine->IsPlaying() && 1.305 + mDecoderStateMachine->GetState() != MediaDecoderStateMachine::DECODER_STATE_COMPLETED; 1.306 + if (blockForStateMachineNotPlaying != mDecodedStream->mHaveBlockedForStateMachineNotPlaying) { 1.307 + mDecodedStream->mHaveBlockedForStateMachineNotPlaying = blockForStateMachineNotPlaying; 1.308 + int32_t delta = blockForStateMachineNotPlaying ? 1 : -1; 1.309 + if (NS_IsMainThread()) { 1.310 + mDecodedStream->mStream->ChangeExplicitBlockerCount(delta); 1.311 + } else { 1.312 + nsCOMPtr<nsIRunnable> runnable = 1.313 + NS_NewRunnableMethodWithArg<int32_t>(mDecodedStream->mStream.get(), 1.314 + &MediaStream::ChangeExplicitBlockerCount, delta); 1.315 + NS_DispatchToMainThread(runnable); 1.316 + } 1.317 + } 1.318 +} 1.319 + 1.320 +void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs) 1.321 +{ 1.322 + MOZ_ASSERT(NS_IsMainThread()); 1.323 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.324 + DECODER_LOG(PR_LOG_DEBUG, "RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs); 1.325 + 1.326 + DestroyDecodedStream(); 1.327 + 1.328 + mDecodedStream = new DecodedStreamData(this, aStartTimeUSecs, 1.329 + MediaStreamGraph::GetInstance()->CreateSourceStream(nullptr)); 1.330 + 1.331 + // Note that the delay between removing ports in DestroyDecodedStream 1.332 + // and adding new ones won't cause a glitch since all graph operations 1.333 + // between main-thread stable states take effect atomically. 1.334 + for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) { 1.335 + OutputStreamData& os = mOutputStreams[i]; 1.336 + if (os.mStream->IsDestroyed()) { 1.337 + // Probably the DOM MediaStream was GCed. Clean up. 1.338 + // No need to destroy the port; all ports have been destroyed here. 1.339 + mOutputStreams.RemoveElementAt(i); 1.340 + continue; 1.341 + } 1.342 + ConnectDecodedStreamToOutputStream(&os); 1.343 + } 1.344 + UpdateStreamBlockingForStateMachinePlaying(); 1.345 + 1.346 + mDecodedStream->mHaveBlockedForPlayState = mPlayState != PLAY_STATE_PLAYING; 1.347 + if (mDecodedStream->mHaveBlockedForPlayState) { 1.348 + mDecodedStream->mStream->ChangeExplicitBlockerCount(1); 1.349 + } 1.350 +} 1.351 + 1.352 +void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream, 1.353 + bool aFinishWhenEnded) 1.354 +{ 1.355 + MOZ_ASSERT(NS_IsMainThread()); 1.356 + DECODER_LOG(PR_LOG_DEBUG, "AddOutputStream aStream=%p!", aStream); 1.357 + 1.358 + { 1.359 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.360 + if (!mDecodedStream) { 1.361 + RecreateDecodedStream(mDecoderStateMachine ? 1.362 + int64_t(mDecoderStateMachine->GetCurrentTime()*USECS_PER_S) : 0); 1.363 + } 1.364 + OutputStreamData* os = mOutputStreams.AppendElement(); 1.365 + os->Init(aStream, aFinishWhenEnded); 1.366 + ConnectDecodedStreamToOutputStream(os); 1.367 + if (aFinishWhenEnded) { 1.368 + // Ensure that aStream finishes the moment mDecodedStream does. 1.369 + aStream->SetAutofinish(true); 1.370 + } 1.371 + } 1.372 + 1.373 + // This can be called before Load(), in which case our mDecoderStateMachine 1.374 + // won't have been created yet and we can rely on Load() to schedule it 1.375 + // once it is created. 1.376 + if (mDecoderStateMachine) { 1.377 + // Make sure the state machine thread runs so that any buffered data 1.378 + // is fed into our stream. 1.379 + ScheduleStateMachineThread(); 1.380 + } 1.381 +} 1.382 + 1.383 +double MediaDecoder::GetDuration() 1.384 +{ 1.385 + MOZ_ASSERT(NS_IsMainThread()); 1.386 + if (mInfiniteStream) { 1.387 + return std::numeric_limits<double>::infinity(); 1.388 + } 1.389 + if (mDuration >= 0) { 1.390 + return static_cast<double>(mDuration) / static_cast<double>(USECS_PER_S); 1.391 + } 1.392 + return std::numeric_limits<double>::quiet_NaN(); 1.393 +} 1.394 + 1.395 +int64_t MediaDecoder::GetMediaDuration() 1.396 +{ 1.397 + NS_ENSURE_TRUE(GetStateMachine(), -1); 1.398 + return GetStateMachine()->GetDuration(); 1.399 +} 1.400 + 1.401 +void MediaDecoder::SetInfinite(bool aInfinite) 1.402 +{ 1.403 + MOZ_ASSERT(NS_IsMainThread()); 1.404 + mInfiniteStream = aInfinite; 1.405 +} 1.406 + 1.407 +bool MediaDecoder::IsInfinite() 1.408 +{ 1.409 + MOZ_ASSERT(NS_IsMainThread()); 1.410 + return mInfiniteStream; 1.411 +} 1.412 + 1.413 +MediaDecoder::MediaDecoder() : 1.414 + mDecoderPosition(0), 1.415 + mPlaybackPosition(0), 1.416 + mCurrentTime(0.0), 1.417 + mInitialVolume(0.0), 1.418 + mInitialPlaybackRate(1.0), 1.419 + mInitialPreservesPitch(true), 1.420 + mDuration(-1), 1.421 + mTransportSeekable(true), 1.422 + mMediaSeekable(true), 1.423 + mSameOriginMedia(false), 1.424 + mReentrantMonitor("media.decoder"), 1.425 + mIsDormant(false), 1.426 + mIsExitingDormant(false), 1.427 + mPlayState(PLAY_STATE_PAUSED), 1.428 + mNextState(PLAY_STATE_PAUSED), 1.429 + mCalledResourceLoaded(false), 1.430 + mIgnoreProgressData(false), 1.431 + mInfiniteStream(false), 1.432 + mOwner(nullptr), 1.433 + mPinnedForSeek(false), 1.434 + mShuttingDown(false), 1.435 + mPausedForPlaybackRateNull(false), 1.436 + mMinimizePreroll(false) 1.437 +{ 1.438 + MOZ_COUNT_CTOR(MediaDecoder); 1.439 + MOZ_ASSERT(NS_IsMainThread()); 1.440 + MediaMemoryTracker::AddMediaDecoder(this); 1.441 +#ifdef PR_LOGGING 1.442 + if (!gMediaDecoderLog) { 1.443 + gMediaDecoderLog = PR_NewLogModule("MediaDecoder"); 1.444 + } 1.445 +#endif 1.446 + 1.447 + mAudioChannel = AudioChannelService::GetDefaultAudioChannel(); 1.448 +} 1.449 + 1.450 +bool MediaDecoder::Init(MediaDecoderOwner* aOwner) 1.451 +{ 1.452 + MOZ_ASSERT(NS_IsMainThread()); 1.453 + mOwner = aOwner; 1.454 + mVideoFrameContainer = aOwner->GetVideoFrameContainer(); 1.455 + MediaShutdownManager::Instance().Register(this); 1.456 + return true; 1.457 +} 1.458 + 1.459 +void MediaDecoder::Shutdown() 1.460 +{ 1.461 + MOZ_ASSERT(NS_IsMainThread()); 1.462 + 1.463 + if (mShuttingDown) 1.464 + return; 1.465 + 1.466 + mShuttingDown = true; 1.467 + 1.468 + { 1.469 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.470 + DestroyDecodedStream(); 1.471 + } 1.472 + 1.473 + // This changes the decoder state to SHUTDOWN and does other things 1.474 + // necessary to unblock the state machine thread if it's blocked, so 1.475 + // the asynchronous shutdown in nsDestroyStateMachine won't deadlock. 1.476 + if (mDecoderStateMachine) { 1.477 + mDecoderStateMachine->Shutdown(); 1.478 + } 1.479 + 1.480 + // Force any outstanding seek and byterange requests to complete 1.481 + // to prevent shutdown from deadlocking. 1.482 + if (mResource) { 1.483 + mResource->Close(); 1.484 + } 1.485 + 1.486 + ChangeState(PLAY_STATE_SHUTDOWN); 1.487 + 1.488 + StopProgress(); 1.489 + mOwner = nullptr; 1.490 + 1.491 + MediaShutdownManager::Instance().Unregister(this); 1.492 +} 1.493 + 1.494 +MediaDecoder::~MediaDecoder() 1.495 +{ 1.496 + MOZ_ASSERT(NS_IsMainThread()); 1.497 + MediaMemoryTracker::RemoveMediaDecoder(this); 1.498 + UnpinForSeek(); 1.499 + MOZ_COUNT_DTOR(MediaDecoder); 1.500 +} 1.501 + 1.502 +nsresult MediaDecoder::OpenResource(nsIStreamListener** aStreamListener) 1.503 +{ 1.504 + MOZ_ASSERT(NS_IsMainThread()); 1.505 + if (aStreamListener) { 1.506 + *aStreamListener = nullptr; 1.507 + } 1.508 + 1.509 + { 1.510 + // Hold the lock while we do this to set proper lock ordering 1.511 + // expectations for dynamic deadlock detectors: decoder lock(s) 1.512 + // should be grabbed before the cache lock 1.513 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.514 + 1.515 + nsresult rv = mResource->Open(aStreamListener); 1.516 + if (NS_FAILED(rv)) { 1.517 + DECODER_LOG(PR_LOG_WARNING, "Failed to open stream!"); 1.518 + return rv; 1.519 + } 1.520 + } 1.521 + return NS_OK; 1.522 +} 1.523 + 1.524 +nsresult MediaDecoder::Load(nsIStreamListener** aStreamListener, 1.525 + MediaDecoder* aCloneDonor) 1.526 +{ 1.527 + MOZ_ASSERT(NS_IsMainThread()); 1.528 + 1.529 + nsresult rv = OpenResource(aStreamListener); 1.530 + NS_ENSURE_SUCCESS(rv, rv); 1.531 + 1.532 + mDecoderStateMachine = CreateStateMachine(); 1.533 + if (!mDecoderStateMachine) { 1.534 + DECODER_LOG(PR_LOG_WARNING, "Failed to create state machine!"); 1.535 + return NS_ERROR_FAILURE; 1.536 + } 1.537 + 1.538 + return InitializeStateMachine(aCloneDonor); 1.539 +} 1.540 + 1.541 +nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor) 1.542 +{ 1.543 + MOZ_ASSERT(NS_IsMainThread()); 1.544 + NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!"); 1.545 + 1.546 + MediaDecoder* cloneDonor = static_cast<MediaDecoder*>(aCloneDonor); 1.547 + if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ? 1.548 + cloneDonor->mDecoderStateMachine : nullptr))) { 1.549 + DECODER_LOG(PR_LOG_WARNING, "Failed to init state machine!"); 1.550 + return NS_ERROR_FAILURE; 1.551 + } 1.552 + { 1.553 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.554 + mDecoderStateMachine->SetTransportSeekable(mTransportSeekable); 1.555 + mDecoderStateMachine->SetMediaSeekable(mMediaSeekable); 1.556 + mDecoderStateMachine->SetDuration(mDuration); 1.557 + mDecoderStateMachine->SetVolume(mInitialVolume); 1.558 + mDecoderStateMachine->SetAudioCaptured(mInitialAudioCaptured); 1.559 + SetPlaybackRate(mInitialPlaybackRate); 1.560 + mDecoderStateMachine->SetPreservesPitch(mInitialPreservesPitch); 1.561 + if (mMinimizePreroll) { 1.562 + mDecoderStateMachine->SetMinimizePrerollUntilPlaybackStarts(); 1.563 + } 1.564 + } 1.565 + 1.566 + ChangeState(PLAY_STATE_LOADING); 1.567 + 1.568 + return ScheduleStateMachineThread(); 1.569 +} 1.570 + 1.571 +void MediaDecoder::SetMinimizePrerollUntilPlaybackStarts() 1.572 +{ 1.573 + MOZ_ASSERT(NS_IsMainThread()); 1.574 + mMinimizePreroll = true; 1.575 +} 1.576 + 1.577 +nsresult MediaDecoder::ScheduleStateMachineThread() 1.578 +{ 1.579 + MOZ_ASSERT(NS_IsMainThread()); 1.580 + NS_ASSERTION(mDecoderStateMachine, 1.581 + "Must have state machine to start state machine thread"); 1.582 + NS_ENSURE_STATE(mDecoderStateMachine); 1.583 + 1.584 + if (mShuttingDown) 1.585 + return NS_OK; 1.586 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.587 + return mDecoderStateMachine->ScheduleStateMachine(); 1.588 +} 1.589 + 1.590 +nsresult MediaDecoder::Play() 1.591 +{ 1.592 + MOZ_ASSERT(NS_IsMainThread()); 1.593 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.594 + NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine."); 1.595 + nsresult res = ScheduleStateMachineThread(); 1.596 + NS_ENSURE_SUCCESS(res,res); 1.597 + if ((mPlayState == PLAY_STATE_LOADING && mIsDormant) || mPlayState == PLAY_STATE_SEEKING) { 1.598 + mNextState = PLAY_STATE_PLAYING; 1.599 + return NS_OK; 1.600 + } 1.601 + if (mPlayState == PLAY_STATE_ENDED) 1.602 + return Seek(0, SeekTarget::PrevSyncPoint); 1.603 + 1.604 + ChangeState(PLAY_STATE_PLAYING); 1.605 + return NS_OK; 1.606 +} 1.607 + 1.608 +nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType) 1.609 +{ 1.610 + MOZ_ASSERT(NS_IsMainThread()); 1.611 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.612 + 1.613 + NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value."); 1.614 + 1.615 + int64_t timeUsecs = 0; 1.616 + nsresult rv = SecondsToUsecs(aTime, timeUsecs); 1.617 + NS_ENSURE_SUCCESS(rv, rv); 1.618 + 1.619 + mRequestedSeekTarget = SeekTarget(timeUsecs, aSeekType); 1.620 + mCurrentTime = aTime; 1.621 + 1.622 + // If we are already in the seeking state, then setting mRequestedSeekTarget 1.623 + // above will result in the new seek occurring when the current seek 1.624 + // completes. 1.625 + if ((mPlayState != PLAY_STATE_LOADING || !mIsDormant) && mPlayState != PLAY_STATE_SEEKING) { 1.626 + bool paused = false; 1.627 + if (mOwner) { 1.628 + paused = mOwner->GetPaused(); 1.629 + } 1.630 + mNextState = paused ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING; 1.631 + PinForSeek(); 1.632 + ChangeState(PLAY_STATE_SEEKING); 1.633 + } 1.634 + 1.635 + return ScheduleStateMachineThread(); 1.636 +} 1.637 + 1.638 +bool MediaDecoder::IsLogicallyPlaying() 1.639 +{ 1.640 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.641 + return mPlayState == PLAY_STATE_PLAYING || 1.642 + mNextState == PLAY_STATE_PLAYING; 1.643 +} 1.644 + 1.645 +double MediaDecoder::GetCurrentTime() 1.646 +{ 1.647 + MOZ_ASSERT(NS_IsMainThread()); 1.648 + return mCurrentTime; 1.649 +} 1.650 + 1.651 +already_AddRefed<nsIPrincipal> MediaDecoder::GetCurrentPrincipal() 1.652 +{ 1.653 + MOZ_ASSERT(NS_IsMainThread()); 1.654 + return mResource ? mResource->GetCurrentPrincipal() : nullptr; 1.655 +} 1.656 + 1.657 +void MediaDecoder::QueueMetadata(int64_t aPublishTime, 1.658 + int aChannels, 1.659 + int aRate, 1.660 + bool aHasAudio, 1.661 + bool aHasVideo, 1.662 + MetadataTags* aTags) 1.663 +{ 1.664 + NS_ASSERTION(OnDecodeThread(), "Should be on decode thread."); 1.665 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.666 + mDecoderStateMachine->QueueMetadata(aPublishTime, aChannels, aRate, aHasAudio, aHasVideo, aTags); 1.667 +} 1.668 + 1.669 +bool 1.670 +MediaDecoder::IsDataCachedToEndOfResource() 1.671 +{ 1.672 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.673 + return (mResource && 1.674 + mResource->IsDataCachedToEndOfResource(mDecoderPosition)); 1.675 +} 1.676 + 1.677 +void MediaDecoder::MetadataLoaded(int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags) 1.678 +{ 1.679 + MOZ_ASSERT(NS_IsMainThread()); 1.680 + if (mShuttingDown) { 1.681 + return; 1.682 + } 1.683 + 1.684 + { 1.685 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.686 + if (mPlayState == PLAY_STATE_LOADING && mIsDormant && !mIsExitingDormant) { 1.687 + return; 1.688 + } else if (mPlayState == PLAY_STATE_LOADING && mIsDormant && mIsExitingDormant) { 1.689 + mIsDormant = false; 1.690 + mIsExitingDormant = false; 1.691 + } 1.692 + mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1; 1.693 + // Duration has changed so we should recompute playback rate 1.694 + UpdatePlaybackRate(); 1.695 + } 1.696 + 1.697 + if (mDuration == -1) { 1.698 + SetInfinite(true); 1.699 + } 1.700 + 1.701 + if (mOwner) { 1.702 + // Make sure the element and the frame (if any) are told about 1.703 + // our new size. 1.704 + Invalidate(); 1.705 + mOwner->MetadataLoaded(aChannels, aRate, aHasAudio, aHasVideo, aTags); 1.706 + } 1.707 + 1.708 + if (!mCalledResourceLoaded) { 1.709 + StartProgress(); 1.710 + } else if (mOwner) { 1.711 + // Resource was loaded during metadata loading, when progress 1.712 + // events are being ignored. Fire the final progress event. 1.713 + mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress")); 1.714 + } 1.715 + 1.716 + // Only inform the element of FirstFrameLoaded if not doing a load() in order 1.717 + // to fulfill a seek, otherwise we'll get multiple loadedfirstframe events. 1.718 + bool notifyResourceIsLoaded = !mCalledResourceLoaded && 1.719 + IsDataCachedToEndOfResource(); 1.720 + if (mOwner) { 1.721 + mOwner->FirstFrameLoaded(notifyResourceIsLoaded); 1.722 + } 1.723 + 1.724 + // This can run cache callbacks. 1.725 + mResource->EnsureCacheUpToDate(); 1.726 + 1.727 + // The element can run javascript via events 1.728 + // before reaching here, so only change the 1.729 + // state if we're still set to the original 1.730 + // loading state. 1.731 + if (mPlayState == PLAY_STATE_LOADING) { 1.732 + if (mRequestedSeekTarget.IsValid()) { 1.733 + ChangeState(PLAY_STATE_SEEKING); 1.734 + } 1.735 + else { 1.736 + ChangeState(mNextState); 1.737 + } 1.738 + } 1.739 + 1.740 + if (notifyResourceIsLoaded) { 1.741 + ResourceLoaded(); 1.742 + } 1.743 + 1.744 + // Run NotifySuspendedStatusChanged now to give us a chance to notice 1.745 + // that autoplay should run. 1.746 + NotifySuspendedStatusChanged(); 1.747 +} 1.748 + 1.749 +void MediaDecoder::ResourceLoaded() 1.750 +{ 1.751 + MOZ_ASSERT(NS_IsMainThread()); 1.752 + 1.753 + // Don't handle ResourceLoaded if we are shutting down, or if 1.754 + // we need to ignore progress data due to seeking (in the case 1.755 + // that the seek results in reaching end of file, we get a bogus call 1.756 + // to ResourceLoaded). 1.757 + if (mShuttingDown) 1.758 + return; 1.759 + 1.760 + { 1.761 + // If we are seeking or loading then the resource loaded notification we get 1.762 + // should be ignored, since it represents the end of the seek request. 1.763 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.764 + if (mIgnoreProgressData || mCalledResourceLoaded || mPlayState == PLAY_STATE_LOADING) 1.765 + return; 1.766 + 1.767 + Progress(false); 1.768 + 1.769 + mCalledResourceLoaded = true; 1.770 + StopProgress(); 1.771 + } 1.772 + 1.773 + // Ensure the final progress event gets fired 1.774 + if (mOwner) { 1.775 + mOwner->ResourceLoaded(); 1.776 + } 1.777 +} 1.778 + 1.779 +void MediaDecoder::ResetConnectionState() 1.780 +{ 1.781 + MOZ_ASSERT(NS_IsMainThread()); 1.782 + if (mShuttingDown) 1.783 + return; 1.784 + 1.785 + if (mOwner) { 1.786 + // Notify the media element that connection gets lost. 1.787 + mOwner->ResetConnectionState(); 1.788 + } 1.789 + 1.790 + // Since we have notified the media element the connection 1.791 + // lost event, the decoder will be reloaded when user tries 1.792 + // to play the Rtsp streaming next time. 1.793 + Shutdown(); 1.794 +} 1.795 + 1.796 +void MediaDecoder::NetworkError() 1.797 +{ 1.798 + MOZ_ASSERT(NS_IsMainThread()); 1.799 + if (mShuttingDown) 1.800 + return; 1.801 + 1.802 + if (mOwner) 1.803 + mOwner->NetworkError(); 1.804 + 1.805 + Shutdown(); 1.806 +} 1.807 + 1.808 +void MediaDecoder::DecodeError() 1.809 +{ 1.810 + MOZ_ASSERT(NS_IsMainThread()); 1.811 + if (mShuttingDown) 1.812 + return; 1.813 + 1.814 + if (mOwner) 1.815 + mOwner->DecodeError(); 1.816 + 1.817 + Shutdown(); 1.818 +} 1.819 + 1.820 +void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin) 1.821 +{ 1.822 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.823 + mSameOriginMedia = aSameOrigin; 1.824 +} 1.825 + 1.826 +bool MediaDecoder::IsSameOriginMedia() 1.827 +{ 1.828 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.829 + return mSameOriginMedia; 1.830 +} 1.831 + 1.832 +bool MediaDecoder::IsSeeking() const 1.833 +{ 1.834 + MOZ_ASSERT(NS_IsMainThread()); 1.835 + return mPlayState == PLAY_STATE_SEEKING; 1.836 +} 1.837 + 1.838 +bool MediaDecoder::IsEnded() const 1.839 +{ 1.840 + MOZ_ASSERT(NS_IsMainThread()); 1.841 + return mPlayState == PLAY_STATE_ENDED || mPlayState == PLAY_STATE_SHUTDOWN; 1.842 +} 1.843 + 1.844 +void MediaDecoder::PlaybackEnded() 1.845 +{ 1.846 + MOZ_ASSERT(NS_IsMainThread()); 1.847 + 1.848 + if (mShuttingDown || mPlayState == MediaDecoder::PLAY_STATE_SEEKING) 1.849 + return; 1.850 + 1.851 + { 1.852 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.853 + 1.854 + for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) { 1.855 + OutputStreamData& os = mOutputStreams[i]; 1.856 + if (os.mStream->IsDestroyed()) { 1.857 + // Probably the DOM MediaStream was GCed. Clean up. 1.858 + os.mPort->Destroy(); 1.859 + mOutputStreams.RemoveElementAt(i); 1.860 + continue; 1.861 + } 1.862 + if (os.mFinishWhenEnded) { 1.863 + // Shouldn't really be needed since mDecodedStream should already have 1.864 + // finished, but doesn't hurt. 1.865 + os.mStream->Finish(); 1.866 + os.mPort->Destroy(); 1.867 + // Not really needed but it keeps the invariant that a stream not 1.868 + // connected to mDecodedStream is explicity blocked. 1.869 + os.mStream->ChangeExplicitBlockerCount(1); 1.870 + mOutputStreams.RemoveElementAt(i); 1.871 + } 1.872 + } 1.873 + } 1.874 + 1.875 + PlaybackPositionChanged(); 1.876 + ChangeState(PLAY_STATE_ENDED); 1.877 + InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE); 1.878 + 1.879 + UpdateReadyStateForData(); 1.880 + if (mOwner) { 1.881 + mOwner->PlaybackEnded(); 1.882 + } 1.883 + 1.884 + // This must be called after |mOwner->PlaybackEnded()| call above, in order 1.885 + // to fire the required durationchange. 1.886 + if (IsInfinite()) { 1.887 + SetInfinite(false); 1.888 + } 1.889 +} 1.890 + 1.891 +NS_IMETHODIMP MediaDecoder::Observe(nsISupports *aSubjet, 1.892 + const char *aTopic, 1.893 + const char16_t *someData) 1.894 +{ 1.895 + MOZ_ASSERT(NS_IsMainThread()); 1.896 + if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { 1.897 + Shutdown(); 1.898 + } 1.899 + 1.900 + return NS_OK; 1.901 +} 1.902 + 1.903 +MediaDecoder::Statistics 1.904 +MediaDecoder::GetStatistics() 1.905 +{ 1.906 + MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread()); 1.907 + Statistics result; 1.908 + 1.909 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.910 + if (mResource) { 1.911 + result.mDownloadRate = 1.912 + mResource->GetDownloadRate(&result.mDownloadRateReliable); 1.913 + result.mDownloadPosition = 1.914 + mResource->GetCachedDataEnd(mDecoderPosition); 1.915 + result.mTotalBytes = mResource->GetLength(); 1.916 + result.mPlaybackRate = ComputePlaybackRate(&result.mPlaybackRateReliable); 1.917 + result.mDecoderPosition = mDecoderPosition; 1.918 + result.mPlaybackPosition = mPlaybackPosition; 1.919 + } 1.920 + else { 1.921 + result.mDownloadRate = 0; 1.922 + result.mDownloadRateReliable = true; 1.923 + result.mPlaybackRate = 0; 1.924 + result.mPlaybackRateReliable = true; 1.925 + result.mDecoderPosition = 0; 1.926 + result.mPlaybackPosition = 0; 1.927 + result.mDownloadPosition = 0; 1.928 + result.mTotalBytes = 0; 1.929 + } 1.930 + 1.931 + return result; 1.932 +} 1.933 + 1.934 +double MediaDecoder::ComputePlaybackRate(bool* aReliable) 1.935 +{ 1.936 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.937 + MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread() || OnDecodeThread()); 1.938 + 1.939 + int64_t length = mResource ? mResource->GetLength() : -1; 1.940 + if (mDuration >= 0 && length >= 0) { 1.941 + *aReliable = true; 1.942 + return length * static_cast<double>(USECS_PER_S) / mDuration; 1.943 + } 1.944 + return mPlaybackStatistics.GetRateAtLastStop(aReliable); 1.945 +} 1.946 + 1.947 +void MediaDecoder::UpdatePlaybackRate() 1.948 +{ 1.949 + MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread()); 1.950 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.951 + if (!mResource) 1.952 + return; 1.953 + bool reliable; 1.954 + uint32_t rate = uint32_t(ComputePlaybackRate(&reliable)); 1.955 + if (reliable) { 1.956 + // Avoid passing a zero rate 1.957 + rate = std::max(rate, 1u); 1.958 + } 1.959 + else { 1.960 + // Set a minimum rate of 10,000 bytes per second ... sometimes we just 1.961 + // don't have good data 1.962 + rate = std::max(rate, 10000u); 1.963 + } 1.964 + mResource->SetPlaybackRate(rate); 1.965 +} 1.966 + 1.967 +void MediaDecoder::NotifySuspendedStatusChanged() 1.968 +{ 1.969 + MOZ_ASSERT(NS_IsMainThread()); 1.970 + if (!mResource) 1.971 + return; 1.972 + bool suspended = mResource->IsSuspendedByCache(); 1.973 + if (mOwner) { 1.974 + mOwner->NotifySuspendedByCache(suspended); 1.975 + UpdateReadyStateForData(); 1.976 + } 1.977 +} 1.978 + 1.979 +void MediaDecoder::NotifyBytesDownloaded() 1.980 +{ 1.981 + MOZ_ASSERT(NS_IsMainThread()); 1.982 + { 1.983 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.984 + UpdatePlaybackRate(); 1.985 + } 1.986 + UpdateReadyStateForData(); 1.987 + Progress(false); 1.988 +} 1.989 + 1.990 +void MediaDecoder::NotifyDownloadEnded(nsresult aStatus) 1.991 +{ 1.992 + MOZ_ASSERT(NS_IsMainThread()); 1.993 + 1.994 + if (aStatus == NS_BINDING_ABORTED) { 1.995 + // Download has been cancelled by user. 1.996 + if (mOwner) { 1.997 + mOwner->LoadAborted(); 1.998 + } 1.999 + return; 1.1000 + } 1.1001 + 1.1002 + { 1.1003 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1004 + UpdatePlaybackRate(); 1.1005 + } 1.1006 + 1.1007 + if (NS_SUCCEEDED(aStatus)) { 1.1008 + ResourceLoaded(); 1.1009 + } 1.1010 + else if (aStatus != NS_BASE_STREAM_CLOSED) { 1.1011 + NetworkError(); 1.1012 + } 1.1013 + UpdateReadyStateForData(); 1.1014 +} 1.1015 + 1.1016 +void MediaDecoder::NotifyPrincipalChanged() 1.1017 +{ 1.1018 + if (mOwner) { 1.1019 + mOwner->NotifyDecoderPrincipalChanged(); 1.1020 + } 1.1021 +} 1.1022 + 1.1023 +void MediaDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) 1.1024 +{ 1.1025 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1026 + NS_ENSURE_TRUE_VOID(mDecoderStateMachine); 1.1027 + if (mIgnoreProgressData) { 1.1028 + return; 1.1029 + } 1.1030 + if (aOffset >= mDecoderPosition) { 1.1031 + mPlaybackStatistics.AddBytes(aBytes); 1.1032 + } 1.1033 + mDecoderPosition = aOffset + aBytes; 1.1034 +} 1.1035 + 1.1036 +void MediaDecoder::UpdateReadyStateForData() 1.1037 +{ 1.1038 + MOZ_ASSERT(NS_IsMainThread()); 1.1039 + if (!mOwner || mShuttingDown || !mDecoderStateMachine) 1.1040 + return; 1.1041 + MediaDecoderOwner::NextFrameStatus frameStatus = 1.1042 + mDecoderStateMachine->GetNextFrameStatus(); 1.1043 + mOwner->UpdateReadyStateForData(frameStatus); 1.1044 +} 1.1045 + 1.1046 +void MediaDecoder::SeekingStopped() 1.1047 +{ 1.1048 + MOZ_ASSERT(NS_IsMainThread()); 1.1049 + 1.1050 + if (mShuttingDown) 1.1051 + return; 1.1052 + 1.1053 + bool seekWasAborted = false; 1.1054 + { 1.1055 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1056 + 1.1057 + // An additional seek was requested while the current seek was 1.1058 + // in operation. 1.1059 + if (mRequestedSeekTarget.IsValid()) { 1.1060 + ChangeState(PLAY_STATE_SEEKING); 1.1061 + seekWasAborted = true; 1.1062 + } else { 1.1063 + UnpinForSeek(); 1.1064 + ChangeState(mNextState); 1.1065 + } 1.1066 + } 1.1067 + 1.1068 + PlaybackPositionChanged(); 1.1069 + 1.1070 + if (mOwner) { 1.1071 + UpdateReadyStateForData(); 1.1072 + if (!seekWasAborted) { 1.1073 + mOwner->SeekCompleted(); 1.1074 + } 1.1075 + } 1.1076 +} 1.1077 + 1.1078 +// This is called when seeking stopped *and* we're at the end of the 1.1079 +// media. 1.1080 +void MediaDecoder::SeekingStoppedAtEnd() 1.1081 +{ 1.1082 + MOZ_ASSERT(NS_IsMainThread()); 1.1083 + 1.1084 + if (mShuttingDown) 1.1085 + return; 1.1086 + 1.1087 + bool fireEnded = false; 1.1088 + bool seekWasAborted = false; 1.1089 + { 1.1090 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1091 + 1.1092 + // An additional seek was requested while the current seek was 1.1093 + // in operation. 1.1094 + if (mRequestedSeekTarget.IsValid()) { 1.1095 + ChangeState(PLAY_STATE_SEEKING); 1.1096 + seekWasAborted = true; 1.1097 + } else { 1.1098 + UnpinForSeek(); 1.1099 + fireEnded = true; 1.1100 + ChangeState(PLAY_STATE_ENDED); 1.1101 + } 1.1102 + } 1.1103 + 1.1104 + PlaybackPositionChanged(); 1.1105 + 1.1106 + if (mOwner) { 1.1107 + UpdateReadyStateForData(); 1.1108 + if (!seekWasAborted) { 1.1109 + mOwner->SeekCompleted(); 1.1110 + if (fireEnded) { 1.1111 + mOwner->PlaybackEnded(); 1.1112 + } 1.1113 + } 1.1114 + } 1.1115 +} 1.1116 + 1.1117 +void MediaDecoder::SeekingStarted() 1.1118 +{ 1.1119 + MOZ_ASSERT(NS_IsMainThread()); 1.1120 + if (mShuttingDown) 1.1121 + return; 1.1122 + 1.1123 + if (mOwner) { 1.1124 + UpdateReadyStateForData(); 1.1125 + mOwner->SeekStarted(); 1.1126 + } 1.1127 +} 1.1128 + 1.1129 +void MediaDecoder::ChangeState(PlayState aState) 1.1130 +{ 1.1131 + MOZ_ASSERT(NS_IsMainThread()); 1.1132 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1133 + 1.1134 + if (mNextState == aState) { 1.1135 + mNextState = PLAY_STATE_PAUSED; 1.1136 + } 1.1137 + 1.1138 + if ((mPlayState == PLAY_STATE_LOADING && mIsDormant && aState != PLAY_STATE_SHUTDOWN) || 1.1139 + mPlayState == PLAY_STATE_SHUTDOWN) { 1.1140 + GetReentrantMonitor().NotifyAll(); 1.1141 + return; 1.1142 + } 1.1143 + 1.1144 + if (mDecodedStream) { 1.1145 + bool blockForPlayState = aState != PLAY_STATE_PLAYING; 1.1146 + if (mDecodedStream->mHaveBlockedForPlayState != blockForPlayState) { 1.1147 + mDecodedStream->mStream->ChangeExplicitBlockerCount(blockForPlayState ? 1 : -1); 1.1148 + mDecodedStream->mHaveBlockedForPlayState = blockForPlayState; 1.1149 + } 1.1150 + } 1.1151 + mPlayState = aState; 1.1152 + 1.1153 + ApplyStateToStateMachine(mPlayState); 1.1154 + 1.1155 + if (aState!= PLAY_STATE_LOADING) { 1.1156 + mIsDormant = false; 1.1157 + mIsExitingDormant = false; 1.1158 + } 1.1159 + 1.1160 + GetReentrantMonitor().NotifyAll(); 1.1161 +} 1.1162 + 1.1163 +void MediaDecoder::ApplyStateToStateMachine(PlayState aState) 1.1164 +{ 1.1165 + MOZ_ASSERT(NS_IsMainThread()); 1.1166 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.1167 + 1.1168 + if (mDecoderStateMachine) { 1.1169 + switch (aState) { 1.1170 + case PLAY_STATE_PLAYING: 1.1171 + mDecoderStateMachine->Play(); 1.1172 + break; 1.1173 + case PLAY_STATE_SEEKING: 1.1174 + mDecoderStateMachine->Seek(mRequestedSeekTarget); 1.1175 + mRequestedSeekTarget.Reset(); 1.1176 + break; 1.1177 + default: 1.1178 + /* No action needed */ 1.1179 + break; 1.1180 + } 1.1181 + } 1.1182 +} 1.1183 + 1.1184 +void MediaDecoder::PlaybackPositionChanged() 1.1185 +{ 1.1186 + MOZ_ASSERT(NS_IsMainThread()); 1.1187 + if (mShuttingDown) 1.1188 + return; 1.1189 + 1.1190 + double lastTime = mCurrentTime; 1.1191 + 1.1192 + // Control the scope of the monitor so it is not 1.1193 + // held while the timeupdate and the invalidate is run. 1.1194 + { 1.1195 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1196 + if (mDecoderStateMachine) { 1.1197 + if (!IsSeeking()) { 1.1198 + // Only update the current playback position if we're not seeking. 1.1199 + // If we are seeking, the update could have been scheduled on the 1.1200 + // state machine thread while we were playing but after the seek 1.1201 + // algorithm set the current playback position on the main thread, 1.1202 + // and we don't want to override the seek algorithm and change the 1.1203 + // current time after the seek has started but before it has 1.1204 + // completed. 1.1205 + if (GetDecodedStream()) { 1.1206 + mCurrentTime = mDecoderStateMachine->GetCurrentTimeViaMediaStreamSync()/ 1.1207 + static_cast<double>(USECS_PER_S); 1.1208 + } else { 1.1209 + mCurrentTime = mDecoderStateMachine->GetCurrentTime(); 1.1210 + } 1.1211 + } 1.1212 + mDecoderStateMachine->ClearPositionChangeFlag(); 1.1213 + } 1.1214 + } 1.1215 + 1.1216 + // Invalidate the frame so any video data is displayed. 1.1217 + // Do this before the timeupdate event so that if that 1.1218 + // event runs JavaScript that queries the media size, the 1.1219 + // frame has reflowed and the size updated beforehand. 1.1220 + Invalidate(); 1.1221 + 1.1222 + if (mOwner && lastTime != mCurrentTime) { 1.1223 + FireTimeUpdate(); 1.1224 + } 1.1225 +} 1.1226 + 1.1227 +void MediaDecoder::DurationChanged() 1.1228 +{ 1.1229 + MOZ_ASSERT(NS_IsMainThread()); 1.1230 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1231 + int64_t oldDuration = mDuration; 1.1232 + mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1; 1.1233 + // Duration has changed so we should recompute playback rate 1.1234 + UpdatePlaybackRate(); 1.1235 + 1.1236 + SetInfinite(mDuration == -1); 1.1237 + 1.1238 + if (mOwner && oldDuration != mDuration && !IsInfinite()) { 1.1239 + DECODER_LOG(PR_LOG_DEBUG, "Duration changed to %lld", mDuration); 1.1240 + mOwner->DispatchEvent(NS_LITERAL_STRING("durationchange")); 1.1241 + } 1.1242 +} 1.1243 + 1.1244 +void MediaDecoder::SetDuration(double aDuration) 1.1245 +{ 1.1246 + MOZ_ASSERT(NS_IsMainThread()); 1.1247 + if (mozilla::IsInfinite(aDuration)) { 1.1248 + SetInfinite(true); 1.1249 + } else if (IsNaN(aDuration)) { 1.1250 + mDuration = -1; 1.1251 + SetInfinite(true); 1.1252 + } else { 1.1253 + mDuration = static_cast<int64_t>(NS_round(aDuration * static_cast<double>(USECS_PER_S))); 1.1254 + } 1.1255 + 1.1256 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1257 + if (mDecoderStateMachine) { 1.1258 + mDecoderStateMachine->SetDuration(mDuration); 1.1259 + } 1.1260 + 1.1261 + // Duration has changed so we should recompute playback rate 1.1262 + UpdatePlaybackRate(); 1.1263 +} 1.1264 + 1.1265 +void MediaDecoder::SetMediaDuration(int64_t aDuration) 1.1266 +{ 1.1267 + NS_ENSURE_TRUE_VOID(GetStateMachine()); 1.1268 + GetStateMachine()->SetDuration(aDuration); 1.1269 +} 1.1270 + 1.1271 +void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration) 1.1272 +{ 1.1273 + if (mPlayState <= PLAY_STATE_LOADING) { 1.1274 + return; 1.1275 + } 1.1276 + NS_ENSURE_TRUE_VOID(GetStateMachine()); 1.1277 + GetStateMachine()->UpdateEstimatedDuration(aDuration); 1.1278 +} 1.1279 + 1.1280 +void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) { 1.1281 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1282 + MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread()); 1.1283 + mMediaSeekable = aMediaSeekable; 1.1284 + if (mDecoderStateMachine) { 1.1285 + mDecoderStateMachine->SetMediaSeekable(aMediaSeekable); 1.1286 + } 1.1287 +} 1.1288 + 1.1289 +void MediaDecoder::SetTransportSeekable(bool aTransportSeekable) 1.1290 +{ 1.1291 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1292 + MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread()); 1.1293 + mTransportSeekable = aTransportSeekable; 1.1294 + if (mDecoderStateMachine) { 1.1295 + mDecoderStateMachine->SetTransportSeekable(aTransportSeekable); 1.1296 + } 1.1297 +} 1.1298 + 1.1299 +bool MediaDecoder::IsTransportSeekable() 1.1300 +{ 1.1301 + MOZ_ASSERT(NS_IsMainThread()); 1.1302 + return mTransportSeekable; 1.1303 +} 1.1304 + 1.1305 +bool MediaDecoder::IsMediaSeekable() 1.1306 +{ 1.1307 + NS_ENSURE_TRUE(GetStateMachine(), false); 1.1308 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1309 + MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread()); 1.1310 + return mMediaSeekable; 1.1311 +} 1.1312 + 1.1313 +nsresult MediaDecoder::GetSeekable(dom::TimeRanges* aSeekable) 1.1314 +{ 1.1315 + double initialTime = 0.0; 1.1316 + 1.1317 + // We can seek in buffered range if the media is seekable. Also, we can seek 1.1318 + // in unbuffered ranges if the transport level is seekable (local file or the 1.1319 + // server supports range requests, etc.) 1.1320 + if (!IsMediaSeekable()) { 1.1321 + return NS_OK; 1.1322 + } else if (!IsTransportSeekable()) { 1.1323 + return GetBuffered(aSeekable); 1.1324 + } else { 1.1325 + double end = IsInfinite() ? std::numeric_limits<double>::infinity() 1.1326 + : initialTime + GetDuration(); 1.1327 + aSeekable->Add(initialTime, end); 1.1328 + return NS_OK; 1.1329 + } 1.1330 +} 1.1331 + 1.1332 +void MediaDecoder::SetFragmentEndTime(double aTime) 1.1333 +{ 1.1334 + MOZ_ASSERT(NS_IsMainThread()); 1.1335 + if (mDecoderStateMachine) { 1.1336 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1337 + mDecoderStateMachine->SetFragmentEndTime(static_cast<int64_t>(aTime * USECS_PER_S)); 1.1338 + } 1.1339 +} 1.1340 + 1.1341 +void MediaDecoder::SetMediaEndTime(int64_t aTime) 1.1342 +{ 1.1343 + NS_ENSURE_TRUE_VOID(GetStateMachine()); 1.1344 + GetStateMachine()->SetMediaEndTime(aTime); 1.1345 +} 1.1346 + 1.1347 +void MediaDecoder::Suspend() 1.1348 +{ 1.1349 + MOZ_ASSERT(NS_IsMainThread()); 1.1350 + if (mResource) { 1.1351 + mResource->Suspend(true); 1.1352 + } 1.1353 +} 1.1354 + 1.1355 +void MediaDecoder::Resume(bool aForceBuffering) 1.1356 +{ 1.1357 + MOZ_ASSERT(NS_IsMainThread()); 1.1358 + if (mResource) { 1.1359 + mResource->Resume(); 1.1360 + } 1.1361 + if (aForceBuffering) { 1.1362 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1363 + if (mDecoderStateMachine) { 1.1364 + mDecoderStateMachine->StartBuffering(); 1.1365 + } 1.1366 + } 1.1367 +} 1.1368 + 1.1369 +void MediaDecoder::StopProgressUpdates() 1.1370 +{ 1.1371 + MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread()); 1.1372 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.1373 + mIgnoreProgressData = true; 1.1374 + if (mResource) { 1.1375 + mResource->SetReadMode(MediaCacheStream::MODE_METADATA); 1.1376 + } 1.1377 +} 1.1378 + 1.1379 +void MediaDecoder::StartProgressUpdates() 1.1380 +{ 1.1381 + MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread()); 1.1382 + GetReentrantMonitor().AssertCurrentThreadIn(); 1.1383 + mIgnoreProgressData = false; 1.1384 + if (mResource) { 1.1385 + mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK); 1.1386 + mDecoderPosition = mPlaybackPosition = mResource->Tell(); 1.1387 + } 1.1388 +} 1.1389 + 1.1390 +void MediaDecoder::MoveLoadsToBackground() 1.1391 +{ 1.1392 + MOZ_ASSERT(NS_IsMainThread()); 1.1393 + if (mResource) { 1.1394 + mResource->MoveLoadsToBackground(); 1.1395 + } 1.1396 +} 1.1397 + 1.1398 +void MediaDecoder::UpdatePlaybackOffset(int64_t aOffset) 1.1399 +{ 1.1400 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1401 + mPlaybackPosition = std::max(aOffset, mPlaybackPosition); 1.1402 +} 1.1403 + 1.1404 +bool MediaDecoder::OnStateMachineThread() const 1.1405 +{ 1.1406 + return mDecoderStateMachine->OnStateMachineThread(); 1.1407 +} 1.1408 + 1.1409 +void MediaDecoder::SetPlaybackRate(double aPlaybackRate) 1.1410 +{ 1.1411 + if (aPlaybackRate == 0) { 1.1412 + mPausedForPlaybackRateNull = true; 1.1413 + Pause(); 1.1414 + return; 1.1415 + } else if (mPausedForPlaybackRateNull) { 1.1416 + // If the playbackRate is no longer null, restart the playback, iff the 1.1417 + // media was playing. 1.1418 + if (mOwner && !mOwner->GetPaused()) { 1.1419 + Play(); 1.1420 + } 1.1421 + mPausedForPlaybackRateNull = false; 1.1422 + } 1.1423 + 1.1424 + if (mDecoderStateMachine) { 1.1425 + mDecoderStateMachine->SetPlaybackRate(aPlaybackRate); 1.1426 + } else { 1.1427 + mInitialPlaybackRate = aPlaybackRate; 1.1428 + } 1.1429 +} 1.1430 + 1.1431 +void MediaDecoder::SetPreservesPitch(bool aPreservesPitch) 1.1432 +{ 1.1433 + if (mDecoderStateMachine) { 1.1434 + mDecoderStateMachine->SetPreservesPitch(aPreservesPitch); 1.1435 + } else { 1.1436 + mInitialPreservesPitch = aPreservesPitch; 1.1437 + } 1.1438 +} 1.1439 + 1.1440 +bool MediaDecoder::OnDecodeThread() const { 1.1441 + NS_WARN_IF_FALSE(mDecoderStateMachine, "mDecoderStateMachine is null"); 1.1442 + return mDecoderStateMachine ? mDecoderStateMachine->OnDecodeThread() : false; 1.1443 +} 1.1444 + 1.1445 +ReentrantMonitor& MediaDecoder::GetReentrantMonitor() { 1.1446 + return mReentrantMonitor.GetReentrantMonitor(); 1.1447 +} 1.1448 + 1.1449 +ImageContainer* MediaDecoder::GetImageContainer() 1.1450 +{ 1.1451 + return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer() : nullptr; 1.1452 +} 1.1453 + 1.1454 +void MediaDecoder::InvalidateWithFlags(uint32_t aFlags) 1.1455 +{ 1.1456 + if (mVideoFrameContainer) { 1.1457 + mVideoFrameContainer->InvalidateWithFlags(aFlags); 1.1458 + } 1.1459 +} 1.1460 + 1.1461 +void MediaDecoder::Invalidate() 1.1462 +{ 1.1463 + if (mVideoFrameContainer) { 1.1464 + mVideoFrameContainer->Invalidate(); 1.1465 + } 1.1466 +} 1.1467 + 1.1468 +// Constructs the time ranges representing what segments of the media 1.1469 +// are buffered and playable. 1.1470 +nsresult MediaDecoder::GetBuffered(dom::TimeRanges* aBuffered) { 1.1471 + if (mDecoderStateMachine) { 1.1472 + return mDecoderStateMachine->GetBuffered(aBuffered); 1.1473 + } 1.1474 + return NS_ERROR_FAILURE; 1.1475 +} 1.1476 + 1.1477 +size_t MediaDecoder::SizeOfVideoQueue() { 1.1478 + if (mDecoderStateMachine) { 1.1479 + return mDecoderStateMachine->SizeOfVideoQueue(); 1.1480 + } 1.1481 + return 0; 1.1482 +} 1.1483 + 1.1484 +size_t MediaDecoder::SizeOfAudioQueue() { 1.1485 + if (mDecoderStateMachine) { 1.1486 + return mDecoderStateMachine->SizeOfAudioQueue(); 1.1487 + } 1.1488 + return 0; 1.1489 +} 1.1490 + 1.1491 +void MediaDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) { 1.1492 + if (mDecoderStateMachine) { 1.1493 + mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset); 1.1494 + } 1.1495 +} 1.1496 + 1.1497 +void MediaDecoder::UpdatePlaybackPosition(int64_t aTime) 1.1498 +{ 1.1499 + mDecoderStateMachine->UpdatePlaybackPosition(aTime); 1.1500 +} 1.1501 + 1.1502 +// Provide access to the state machine object 1.1503 +MediaDecoderStateMachine* MediaDecoder::GetStateMachine() const { 1.1504 + return mDecoderStateMachine; 1.1505 +} 1.1506 + 1.1507 +void 1.1508 +MediaDecoder::NotifyWaitingForResourcesStatusChanged() 1.1509 +{ 1.1510 + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); 1.1511 + if (mDecoderStateMachine) { 1.1512 + mDecoderStateMachine->NotifyWaitingForResourcesStatusChanged(); 1.1513 + } 1.1514 +} 1.1515 + 1.1516 +bool MediaDecoder::IsShutdown() const { 1.1517 + NS_ENSURE_TRUE(GetStateMachine(), true); 1.1518 + return GetStateMachine()->IsShutdown(); 1.1519 +} 1.1520 + 1.1521 +int64_t MediaDecoder::GetEndMediaTime() const { 1.1522 + NS_ENSURE_TRUE(GetStateMachine(), -1); 1.1523 + return GetStateMachine()->GetEndMediaTime(); 1.1524 +} 1.1525 + 1.1526 +// Drop reference to state machine. Only called during shutdown dance. 1.1527 +void MediaDecoder::ReleaseStateMachine() { 1.1528 + mDecoderStateMachine = nullptr; 1.1529 +} 1.1530 + 1.1531 +MediaDecoderOwner* MediaDecoder::GetMediaOwner() const 1.1532 +{ 1.1533 + return mOwner; 1.1534 +} 1.1535 + 1.1536 +static void ProgressCallback(nsITimer* aTimer, void* aClosure) 1.1537 +{ 1.1538 + MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure); 1.1539 + decoder->Progress(true); 1.1540 +} 1.1541 + 1.1542 +void MediaDecoder::Progress(bool aTimer) 1.1543 +{ 1.1544 + if (!mOwner) 1.1545 + return; 1.1546 + 1.1547 + TimeStamp now = TimeStamp::Now(); 1.1548 + 1.1549 + if (!aTimer) { 1.1550 + mDataTime = now; 1.1551 + } 1.1552 + 1.1553 + // If PROGRESS_MS has passed since the last progress event fired and more 1.1554 + // data has arrived since then, fire another progress event. 1.1555 + if ((mProgressTime.IsNull() || 1.1556 + now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) && 1.1557 + !mDataTime.IsNull() && 1.1558 + now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) { 1.1559 + mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress")); 1.1560 + mProgressTime = now; 1.1561 + } 1.1562 + 1.1563 + if (!mDataTime.IsNull() && 1.1564 + now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) { 1.1565 + mOwner->DownloadStalled(); 1.1566 + // Null it out 1.1567 + mDataTime = TimeStamp(); 1.1568 + } 1.1569 +} 1.1570 + 1.1571 +nsresult MediaDecoder::StartProgress() 1.1572 +{ 1.1573 + if (mProgressTimer) 1.1574 + return NS_OK; 1.1575 + 1.1576 + mProgressTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.1577 + return mProgressTimer->InitWithFuncCallback(ProgressCallback, 1.1578 + this, 1.1579 + PROGRESS_MS, 1.1580 + nsITimer::TYPE_REPEATING_SLACK); 1.1581 +} 1.1582 + 1.1583 +nsresult MediaDecoder::StopProgress() 1.1584 +{ 1.1585 + if (!mProgressTimer) 1.1586 + return NS_OK; 1.1587 + 1.1588 + nsresult rv = mProgressTimer->Cancel(); 1.1589 + mProgressTimer = nullptr; 1.1590 + 1.1591 + return rv; 1.1592 +} 1.1593 + 1.1594 +void MediaDecoder::FireTimeUpdate() 1.1595 +{ 1.1596 + if (!mOwner) 1.1597 + return; 1.1598 + mOwner->FireTimeUpdate(true); 1.1599 +} 1.1600 + 1.1601 +void MediaDecoder::PinForSeek() 1.1602 +{ 1.1603 + MediaResource* resource = GetResource(); 1.1604 + if (!resource || mPinnedForSeek) { 1.1605 + return; 1.1606 + } 1.1607 + mPinnedForSeek = true; 1.1608 + resource->Pin(); 1.1609 +} 1.1610 + 1.1611 +void MediaDecoder::UnpinForSeek() 1.1612 +{ 1.1613 + MediaResource* resource = GetResource(); 1.1614 + if (!resource || !mPinnedForSeek) { 1.1615 + return; 1.1616 + } 1.1617 + mPinnedForSeek = false; 1.1618 + resource->Unpin(); 1.1619 +} 1.1620 + 1.1621 +bool MediaDecoder::CanPlayThrough() 1.1622 +{ 1.1623 + Statistics stats = GetStatistics(); 1.1624 + if (!stats.mDownloadRateReliable || !stats.mPlaybackRateReliable) { 1.1625 + return false; 1.1626 + } 1.1627 + int64_t bytesToDownload = stats.mTotalBytes - stats.mDownloadPosition; 1.1628 + int64_t bytesToPlayback = stats.mTotalBytes - stats.mPlaybackPosition; 1.1629 + double timeToDownload = bytesToDownload / stats.mDownloadRate; 1.1630 + double timeToPlay = bytesToPlayback / stats.mPlaybackRate; 1.1631 + 1.1632 + if (timeToDownload > timeToPlay) { 1.1633 + // Estimated time to download is greater than the estimated time to play. 1.1634 + // We probably can't play through without having to stop to buffer. 1.1635 + return false; 1.1636 + } 1.1637 + 1.1638 + // Estimated time to download is less than the estimated time to play. 1.1639 + // We can probably play through without having to buffer, but ensure that 1.1640 + // we've got a reasonable amount of data buffered after the current 1.1641 + // playback position, so that if the bitrate of the media fluctuates, or if 1.1642 + // our download rate or decode rate estimation is otherwise inaccurate, 1.1643 + // we don't suddenly discover that we need to buffer. This is particularly 1.1644 + // required near the start of the media, when not much data is downloaded. 1.1645 + int64_t readAheadMargin = 1.1646 + static_cast<int64_t>(stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN); 1.1647 + return stats.mTotalBytes == stats.mDownloadPosition || 1.1648 + stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin; 1.1649 +} 1.1650 + 1.1651 +#ifdef MOZ_RAW 1.1652 +bool 1.1653 +MediaDecoder::IsRawEnabled() 1.1654 +{ 1.1655 + return Preferences::GetBool("media.raw.enabled"); 1.1656 +} 1.1657 +#endif 1.1658 + 1.1659 +bool 1.1660 +MediaDecoder::IsOpusEnabled() 1.1661 +{ 1.1662 +#ifdef MOZ_OPUS 1.1663 + return Preferences::GetBool("media.opus.enabled"); 1.1664 +#else 1.1665 + return false; 1.1666 +#endif 1.1667 +} 1.1668 + 1.1669 +bool 1.1670 +MediaDecoder::IsOggEnabled() 1.1671 +{ 1.1672 + return Preferences::GetBool("media.ogg.enabled"); 1.1673 +} 1.1674 + 1.1675 +#ifdef MOZ_WAVE 1.1676 +bool 1.1677 +MediaDecoder::IsWaveEnabled() 1.1678 +{ 1.1679 + return Preferences::GetBool("media.wave.enabled"); 1.1680 +} 1.1681 +#endif 1.1682 + 1.1683 +#ifdef MOZ_WEBM 1.1684 +bool 1.1685 +MediaDecoder::IsWebMEnabled() 1.1686 +{ 1.1687 + return Preferences::GetBool("media.webm.enabled"); 1.1688 +} 1.1689 +#endif 1.1690 + 1.1691 +#ifdef NECKO_PROTOCOL_rtsp 1.1692 +bool 1.1693 +MediaDecoder::IsRtspEnabled() 1.1694 +{ 1.1695 + //Currently the Rtsp decoded by omx. 1.1696 + return (Preferences::GetBool("media.rtsp.enabled", false) && IsOmxEnabled()); 1.1697 +} 1.1698 +#endif 1.1699 + 1.1700 +#ifdef MOZ_GSTREAMER 1.1701 +bool 1.1702 +MediaDecoder::IsGStreamerEnabled() 1.1703 +{ 1.1704 + return Preferences::GetBool("media.gstreamer.enabled"); 1.1705 +} 1.1706 +#endif 1.1707 + 1.1708 +#ifdef MOZ_OMX_DECODER 1.1709 +bool 1.1710 +MediaDecoder::IsOmxEnabled() 1.1711 +{ 1.1712 + return Preferences::GetBool("media.omx.enabled", false); 1.1713 +} 1.1714 +#endif 1.1715 + 1.1716 +#ifdef MOZ_MEDIA_PLUGINS 1.1717 +bool 1.1718 +MediaDecoder::IsMediaPluginsEnabled() 1.1719 +{ 1.1720 + return Preferences::GetBool("media.plugins.enabled"); 1.1721 +} 1.1722 +#endif 1.1723 + 1.1724 +#ifdef MOZ_WMF 1.1725 +bool 1.1726 +MediaDecoder::IsWMFEnabled() 1.1727 +{ 1.1728 + return WMFDecoder::IsEnabled(); 1.1729 +} 1.1730 +#endif 1.1731 + 1.1732 +#ifdef MOZ_APPLEMEDIA 1.1733 +bool 1.1734 +MediaDecoder::IsAppleMP3Enabled() 1.1735 +{ 1.1736 + return Preferences::GetBool("media.apple.mp3.enabled"); 1.1737 +} 1.1738 +#endif 1.1739 + 1.1740 +NS_IMETHODIMP 1.1741 +MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport, 1.1742 + nsISupports* aData) 1.1743 +{ 1.1744 + int64_t video = 0, audio = 0; 1.1745 + size_t resources = 0; 1.1746 + DecodersArray& decoders = Decoders(); 1.1747 + for (size_t i = 0; i < decoders.Length(); ++i) { 1.1748 + MediaDecoder* decoder = decoders[i]; 1.1749 + video += decoder->SizeOfVideoQueue(); 1.1750 + audio += decoder->SizeOfAudioQueue(); 1.1751 + 1.1752 + if (decoder->GetResource()) { 1.1753 + resources += decoder->GetResource()->SizeOfIncludingThis(MallocSizeOf); 1.1754 + } 1.1755 + } 1.1756 + 1.1757 +#define REPORT(_path, _amount, _desc) \ 1.1758 + do { \ 1.1759 + nsresult rv; \ 1.1760 + rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \ 1.1761 + KIND_HEAP, UNITS_BYTES, _amount, \ 1.1762 + NS_LITERAL_CSTRING(_desc), aData); \ 1.1763 + NS_ENSURE_SUCCESS(rv, rv); \ 1.1764 + } while (0) 1.1765 + 1.1766 + REPORT("explicit/media/decoded/video", video, 1.1767 + "Memory used by decoded video frames."); 1.1768 + 1.1769 + REPORT("explicit/media/decoded/audio", audio, 1.1770 + "Memory used by decoded audio chunks."); 1.1771 + 1.1772 + REPORT("explicit/media/resources", resources, 1.1773 + "Memory used by media resources including streaming buffers, caches, " 1.1774 + "etc."); 1.1775 + 1.1776 +#undef REPORT 1.1777 + 1.1778 + return NS_OK; 1.1779 +} 1.1780 + 1.1781 +MediaDecoderOwner* 1.1782 +MediaDecoder::GetOwner() 1.1783 +{ 1.1784 + MOZ_ASSERT(NS_IsMainThread()); 1.1785 + return mOwner; 1.1786 +} 1.1787 + 1.1788 +MediaMemoryTracker::MediaMemoryTracker() 1.1789 +{ 1.1790 +} 1.1791 + 1.1792 +void 1.1793 +MediaMemoryTracker::InitMemoryReporter() 1.1794 +{ 1.1795 + RegisterWeakMemoryReporter(this); 1.1796 +} 1.1797 + 1.1798 +MediaMemoryTracker::~MediaMemoryTracker() 1.1799 +{ 1.1800 + UnregisterWeakMemoryReporter(this); 1.1801 +} 1.1802 + 1.1803 +} // namespace mozilla 1.1804 + 1.1805 +// avoid redefined macro in unified build 1.1806 +#undef DECODER_LOG