content/media/MediaDecoder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "MediaDecoder.h"
michael@0 8 #include "mozilla/FloatingPoint.h"
michael@0 9 #include "mozilla/MathAlgorithms.h"
michael@0 10 #include <limits>
michael@0 11 #include "nsIObserver.h"
michael@0 12 #include "nsTArray.h"
michael@0 13 #include "VideoUtils.h"
michael@0 14 #include "MediaDecoderStateMachine.h"
michael@0 15 #include "mozilla/dom/TimeRanges.h"
michael@0 16 #include "ImageContainer.h"
michael@0 17 #include "MediaResource.h"
michael@0 18 #include "nsError.h"
michael@0 19 #include "mozilla/Preferences.h"
michael@0 20 #include "mozilla/StaticPtr.h"
michael@0 21 #include "nsIMemoryReporter.h"
michael@0 22 #include "nsComponentManagerUtils.h"
michael@0 23 #include "nsITimer.h"
michael@0 24 #include <algorithm>
michael@0 25 #include "MediaShutdownManager.h"
michael@0 26 #include "AudioChannelService.h"
michael@0 27
michael@0 28 #ifdef MOZ_WMF
michael@0 29 #include "WMFDecoder.h"
michael@0 30 #endif
michael@0 31
michael@0 32 using namespace mozilla::layers;
michael@0 33 using namespace mozilla::dom;
michael@0 34
michael@0 35 namespace mozilla {
michael@0 36
michael@0 37 // Number of milliseconds between progress events as defined by spec
michael@0 38 static const uint32_t PROGRESS_MS = 350;
michael@0 39
michael@0 40 // Number of milliseconds of no data before a stall event is fired as defined by spec
michael@0 41 static const uint32_t STALL_MS = 3000;
michael@0 42
michael@0 43 // Number of estimated seconds worth of data we need to have buffered
michael@0 44 // ahead of the current playback position before we allow the media decoder
michael@0 45 // to report that it can play through the entire media without the decode
michael@0 46 // catching up with the download. Having this margin make the
michael@0 47 // MediaDecoder::CanPlayThrough() calculation more stable in the case of
michael@0 48 // fluctuating bitrates.
michael@0 49 static const int64_t CAN_PLAY_THROUGH_MARGIN = 1;
michael@0 50
michael@0 51 // avoid redefined macro in unified build
michael@0 52 #undef DECODER_LOG
michael@0 53
michael@0 54 #ifdef PR_LOGGING
michael@0 55 PRLogModuleInfo* gMediaDecoderLog;
michael@0 56 #define DECODER_LOG(type, msg, ...) \
michael@0 57 PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, this, ##__VA_ARGS__))
michael@0 58 #else
michael@0 59 #define DECODER_LOG(type, msg, ...)
michael@0 60 #endif
michael@0 61
michael@0 62 class MediaMemoryTracker : public nsIMemoryReporter
michael@0 63 {
michael@0 64 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 65 NS_DECL_NSIMEMORYREPORTER
michael@0 66
michael@0 67 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
michael@0 68
michael@0 69 MediaMemoryTracker();
michael@0 70 virtual ~MediaMemoryTracker();
michael@0 71 void InitMemoryReporter();
michael@0 72
michael@0 73 static StaticRefPtr<MediaMemoryTracker> sUniqueInstance;
michael@0 74
michael@0 75 static MediaMemoryTracker* UniqueInstance() {
michael@0 76 if (!sUniqueInstance) {
michael@0 77 sUniqueInstance = new MediaMemoryTracker();
michael@0 78 sUniqueInstance->InitMemoryReporter();
michael@0 79 }
michael@0 80 return sUniqueInstance;
michael@0 81 }
michael@0 82
michael@0 83 typedef nsTArray<MediaDecoder*> DecodersArray;
michael@0 84 static DecodersArray& Decoders() {
michael@0 85 return UniqueInstance()->mDecoders;
michael@0 86 }
michael@0 87
michael@0 88 DecodersArray mDecoders;
michael@0 89
michael@0 90 public:
michael@0 91 static void AddMediaDecoder(MediaDecoder* aDecoder)
michael@0 92 {
michael@0 93 Decoders().AppendElement(aDecoder);
michael@0 94 }
michael@0 95
michael@0 96 static void RemoveMediaDecoder(MediaDecoder* aDecoder)
michael@0 97 {
michael@0 98 DecodersArray& decoders = Decoders();
michael@0 99 decoders.RemoveElement(aDecoder);
michael@0 100 if (decoders.IsEmpty()) {
michael@0 101 sUniqueInstance = nullptr;
michael@0 102 }
michael@0 103 }
michael@0 104 };
michael@0 105
michael@0 106 StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
michael@0 107
michael@0 108 NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
michael@0 109
michael@0 110 NS_IMPL_ISUPPORTS(MediaDecoder, nsIObserver)
michael@0 111
michael@0 112 void MediaDecoder::SetDormantIfNecessary(bool aDormant)
michael@0 113 {
michael@0 114 MOZ_ASSERT(NS_IsMainThread());
michael@0 115 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 116
michael@0 117 if (!mDecoderStateMachine || !mDecoderStateMachine->IsDormantNeeded() || (mPlayState == PLAY_STATE_SHUTDOWN)) {
michael@0 118 return;
michael@0 119 }
michael@0 120
michael@0 121 if (mIsDormant == aDormant) {
michael@0 122 // no change to dormant state
michael@0 123 return;
michael@0 124 }
michael@0 125
michael@0 126 if(aDormant) {
michael@0 127 // enter dormant state
michael@0 128 StopProgress();
michael@0 129 DestroyDecodedStream();
michael@0 130 mDecoderStateMachine->SetDormant(true);
michael@0 131
michael@0 132 mRequestedSeekTarget = SeekTarget(mCurrentTime, SeekTarget::Accurate);
michael@0 133 if (mPlayState == PLAY_STATE_PLAYING){
michael@0 134 mNextState = PLAY_STATE_PLAYING;
michael@0 135 } else {
michael@0 136 mNextState = PLAY_STATE_PAUSED;
michael@0 137 }
michael@0 138 mNextState = mPlayState;
michael@0 139 mIsDormant = true;
michael@0 140 mIsExitingDormant = false;
michael@0 141 ChangeState(PLAY_STATE_LOADING);
michael@0 142 } else if ((aDormant != true) && (mPlayState == PLAY_STATE_LOADING)) {
michael@0 143 // exit dormant state
michael@0 144 // trigger to state machine.
michael@0 145 mDecoderStateMachine->SetDormant(false);
michael@0 146 mIsExitingDormant = true;
michael@0 147 }
michael@0 148 }
michael@0 149
michael@0 150 void MediaDecoder::Pause()
michael@0 151 {
michael@0 152 MOZ_ASSERT(NS_IsMainThread());
michael@0 153 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 154 if ((mPlayState == PLAY_STATE_LOADING && mIsDormant) || mPlayState == PLAY_STATE_SEEKING || mPlayState == PLAY_STATE_ENDED) {
michael@0 155 mNextState = PLAY_STATE_PAUSED;
michael@0 156 return;
michael@0 157 }
michael@0 158
michael@0 159 ChangeState(PLAY_STATE_PAUSED);
michael@0 160 }
michael@0 161
michael@0 162 void MediaDecoder::SetVolume(double aVolume)
michael@0 163 {
michael@0 164 MOZ_ASSERT(NS_IsMainThread());
michael@0 165 mInitialVolume = aVolume;
michael@0 166 if (mDecoderStateMachine) {
michael@0 167 mDecoderStateMachine->SetVolume(aVolume);
michael@0 168 }
michael@0 169 }
michael@0 170
michael@0 171 void MediaDecoder::SetAudioCaptured(bool aCaptured)
michael@0 172 {
michael@0 173 MOZ_ASSERT(NS_IsMainThread());
michael@0 174 mInitialAudioCaptured = aCaptured;
michael@0 175 if (mDecoderStateMachine) {
michael@0 176 mDecoderStateMachine->SetAudioCaptured(aCaptured);
michael@0 177 }
michael@0 178 }
michael@0 179
michael@0 180 void MediaDecoder::ConnectDecodedStreamToOutputStream(OutputStreamData* aStream)
michael@0 181 {
michael@0 182 NS_ASSERTION(!aStream->mPort, "Already connected?");
michael@0 183
michael@0 184 // The output stream must stay in sync with the decoded stream, so if
michael@0 185 // either stream is blocked, we block the other.
michael@0 186 aStream->mPort = aStream->mStream->AllocateInputPort(mDecodedStream->mStream,
michael@0 187 MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT);
michael@0 188 // Unblock the output stream now. While it's connected to mDecodedStream,
michael@0 189 // mDecodedStream is responsible for controlling blocking.
michael@0 190 aStream->mStream->ChangeExplicitBlockerCount(-1);
michael@0 191 }
michael@0 192
michael@0 193 MediaDecoder::DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder,
michael@0 194 int64_t aInitialTime,
michael@0 195 SourceMediaStream* aStream)
michael@0 196 : mLastAudioPacketTime(-1),
michael@0 197 mLastAudioPacketEndTime(-1),
michael@0 198 mAudioFramesWritten(0),
michael@0 199 mInitialTime(aInitialTime),
michael@0 200 mNextVideoTime(aInitialTime),
michael@0 201 mDecoder(aDecoder),
michael@0 202 mStreamInitialized(false),
michael@0 203 mHaveSentFinish(false),
michael@0 204 mHaveSentFinishAudio(false),
michael@0 205 mHaveSentFinishVideo(false),
michael@0 206 mStream(aStream),
michael@0 207 mHaveBlockedForPlayState(false),
michael@0 208 mHaveBlockedForStateMachineNotPlaying(false)
michael@0 209 {
michael@0 210 mListener = new DecodedStreamGraphListener(mStream, this);
michael@0 211 mStream->AddListener(mListener);
michael@0 212 }
michael@0 213
michael@0 214 MediaDecoder::DecodedStreamData::~DecodedStreamData()
michael@0 215 {
michael@0 216 mListener->Forget();
michael@0 217 mStream->Destroy();
michael@0 218 }
michael@0 219
michael@0 220 MediaDecoder::DecodedStreamGraphListener::DecodedStreamGraphListener(MediaStream* aStream,
michael@0 221 DecodedStreamData* aData)
michael@0 222 : mData(aData),
michael@0 223 mMutex("MediaDecoder::DecodedStreamData::mMutex"),
michael@0 224 mStream(aStream),
michael@0 225 mLastOutputTime(aStream->GetCurrentTime()),
michael@0 226 mStreamFinishedOnMainThread(false)
michael@0 227 {
michael@0 228 }
michael@0 229
michael@0 230 void
michael@0 231 MediaDecoder::DecodedStreamGraphListener::NotifyOutput(MediaStreamGraph* aGraph,
michael@0 232 GraphTime aCurrentTime)
michael@0 233 {
michael@0 234 MutexAutoLock lock(mMutex);
michael@0 235 if (mStream) {
michael@0 236 mLastOutputTime = mStream->GraphTimeToStreamTime(aCurrentTime);
michael@0 237 }
michael@0 238 }
michael@0 239
michael@0 240 void
michael@0 241 MediaDecoder::DecodedStreamGraphListener::DoNotifyFinished()
michael@0 242 {
michael@0 243 if (mData && mData->mDecoder) {
michael@0 244 if (mData->mDecoder->GetState() == PLAY_STATE_PLAYING) {
michael@0 245 nsCOMPtr<nsIRunnable> event =
michael@0 246 NS_NewRunnableMethod(mData->mDecoder, &MediaDecoder::PlaybackEnded);
michael@0 247 NS_DispatchToCurrentThread(event);
michael@0 248 }
michael@0 249 }
michael@0 250
michael@0 251 MutexAutoLock lock(mMutex);
michael@0 252 mStreamFinishedOnMainThread = true;
michael@0 253 }
michael@0 254
michael@0 255 void
michael@0 256 MediaDecoder::DecodedStreamGraphListener::NotifyFinished(MediaStreamGraph* aGraph)
michael@0 257 {
michael@0 258 nsCOMPtr<nsIRunnable> event =
michael@0 259 NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
michael@0 260 aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
michael@0 261 }
michael@0 262
michael@0 263 void MediaDecoder::DestroyDecodedStream()
michael@0 264 {
michael@0 265 MOZ_ASSERT(NS_IsMainThread());
michael@0 266 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 267
michael@0 268 // All streams are having their SourceMediaStream disconnected, so they
michael@0 269 // need to be explicitly blocked again.
michael@0 270 for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
michael@0 271 OutputStreamData& os = mOutputStreams[i];
michael@0 272 // During cycle collection, nsDOMMediaStream can be destroyed and send
michael@0 273 // its Destroy message before this decoder is destroyed. So we have to
michael@0 274 // be careful not to send any messages after the Destroy().
michael@0 275 if (os.mStream->IsDestroyed()) {
michael@0 276 // Probably the DOM MediaStream was GCed. Clean up.
michael@0 277 os.mPort->Destroy();
michael@0 278 mOutputStreams.RemoveElementAt(i);
michael@0 279 continue;
michael@0 280 }
michael@0 281 os.mStream->ChangeExplicitBlockerCount(1);
michael@0 282 // Explicitly remove all existing ports. This is not strictly necessary but it's
michael@0 283 // good form.
michael@0 284 os.mPort->Destroy();
michael@0 285 os.mPort = nullptr;
michael@0 286 }
michael@0 287
michael@0 288 mDecodedStream = nullptr;
michael@0 289 }
michael@0 290
michael@0 291 void MediaDecoder::UpdateStreamBlockingForStateMachinePlaying()
michael@0 292 {
michael@0 293 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 294 if (!mDecodedStream) {
michael@0 295 return;
michael@0 296 }
michael@0 297 if (mDecoderStateMachine) {
michael@0 298 mDecoderStateMachine->SetSyncPointForMediaStream();
michael@0 299 }
michael@0 300 bool blockForStateMachineNotPlaying =
michael@0 301 mDecoderStateMachine && !mDecoderStateMachine->IsPlaying() &&
michael@0 302 mDecoderStateMachine->GetState() != MediaDecoderStateMachine::DECODER_STATE_COMPLETED;
michael@0 303 if (blockForStateMachineNotPlaying != mDecodedStream->mHaveBlockedForStateMachineNotPlaying) {
michael@0 304 mDecodedStream->mHaveBlockedForStateMachineNotPlaying = blockForStateMachineNotPlaying;
michael@0 305 int32_t delta = blockForStateMachineNotPlaying ? 1 : -1;
michael@0 306 if (NS_IsMainThread()) {
michael@0 307 mDecodedStream->mStream->ChangeExplicitBlockerCount(delta);
michael@0 308 } else {
michael@0 309 nsCOMPtr<nsIRunnable> runnable =
michael@0 310 NS_NewRunnableMethodWithArg<int32_t>(mDecodedStream->mStream.get(),
michael@0 311 &MediaStream::ChangeExplicitBlockerCount, delta);
michael@0 312 NS_DispatchToMainThread(runnable);
michael@0 313 }
michael@0 314 }
michael@0 315 }
michael@0 316
michael@0 317 void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
michael@0 318 {
michael@0 319 MOZ_ASSERT(NS_IsMainThread());
michael@0 320 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 321 DECODER_LOG(PR_LOG_DEBUG, "RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
michael@0 322
michael@0 323 DestroyDecodedStream();
michael@0 324
michael@0 325 mDecodedStream = new DecodedStreamData(this, aStartTimeUSecs,
michael@0 326 MediaStreamGraph::GetInstance()->CreateSourceStream(nullptr));
michael@0 327
michael@0 328 // Note that the delay between removing ports in DestroyDecodedStream
michael@0 329 // and adding new ones won't cause a glitch since all graph operations
michael@0 330 // between main-thread stable states take effect atomically.
michael@0 331 for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
michael@0 332 OutputStreamData& os = mOutputStreams[i];
michael@0 333 if (os.mStream->IsDestroyed()) {
michael@0 334 // Probably the DOM MediaStream was GCed. Clean up.
michael@0 335 // No need to destroy the port; all ports have been destroyed here.
michael@0 336 mOutputStreams.RemoveElementAt(i);
michael@0 337 continue;
michael@0 338 }
michael@0 339 ConnectDecodedStreamToOutputStream(&os);
michael@0 340 }
michael@0 341 UpdateStreamBlockingForStateMachinePlaying();
michael@0 342
michael@0 343 mDecodedStream->mHaveBlockedForPlayState = mPlayState != PLAY_STATE_PLAYING;
michael@0 344 if (mDecodedStream->mHaveBlockedForPlayState) {
michael@0 345 mDecodedStream->mStream->ChangeExplicitBlockerCount(1);
michael@0 346 }
michael@0 347 }
michael@0 348
michael@0 349 void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
michael@0 350 bool aFinishWhenEnded)
michael@0 351 {
michael@0 352 MOZ_ASSERT(NS_IsMainThread());
michael@0 353 DECODER_LOG(PR_LOG_DEBUG, "AddOutputStream aStream=%p!", aStream);
michael@0 354
michael@0 355 {
michael@0 356 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 357 if (!mDecodedStream) {
michael@0 358 RecreateDecodedStream(mDecoderStateMachine ?
michael@0 359 int64_t(mDecoderStateMachine->GetCurrentTime()*USECS_PER_S) : 0);
michael@0 360 }
michael@0 361 OutputStreamData* os = mOutputStreams.AppendElement();
michael@0 362 os->Init(aStream, aFinishWhenEnded);
michael@0 363 ConnectDecodedStreamToOutputStream(os);
michael@0 364 if (aFinishWhenEnded) {
michael@0 365 // Ensure that aStream finishes the moment mDecodedStream does.
michael@0 366 aStream->SetAutofinish(true);
michael@0 367 }
michael@0 368 }
michael@0 369
michael@0 370 // This can be called before Load(), in which case our mDecoderStateMachine
michael@0 371 // won't have been created yet and we can rely on Load() to schedule it
michael@0 372 // once it is created.
michael@0 373 if (mDecoderStateMachine) {
michael@0 374 // Make sure the state machine thread runs so that any buffered data
michael@0 375 // is fed into our stream.
michael@0 376 ScheduleStateMachineThread();
michael@0 377 }
michael@0 378 }
michael@0 379
michael@0 380 double MediaDecoder::GetDuration()
michael@0 381 {
michael@0 382 MOZ_ASSERT(NS_IsMainThread());
michael@0 383 if (mInfiniteStream) {
michael@0 384 return std::numeric_limits<double>::infinity();
michael@0 385 }
michael@0 386 if (mDuration >= 0) {
michael@0 387 return static_cast<double>(mDuration) / static_cast<double>(USECS_PER_S);
michael@0 388 }
michael@0 389 return std::numeric_limits<double>::quiet_NaN();
michael@0 390 }
michael@0 391
michael@0 392 int64_t MediaDecoder::GetMediaDuration()
michael@0 393 {
michael@0 394 NS_ENSURE_TRUE(GetStateMachine(), -1);
michael@0 395 return GetStateMachine()->GetDuration();
michael@0 396 }
michael@0 397
michael@0 398 void MediaDecoder::SetInfinite(bool aInfinite)
michael@0 399 {
michael@0 400 MOZ_ASSERT(NS_IsMainThread());
michael@0 401 mInfiniteStream = aInfinite;
michael@0 402 }
michael@0 403
michael@0 404 bool MediaDecoder::IsInfinite()
michael@0 405 {
michael@0 406 MOZ_ASSERT(NS_IsMainThread());
michael@0 407 return mInfiniteStream;
michael@0 408 }
michael@0 409
michael@0 410 MediaDecoder::MediaDecoder() :
michael@0 411 mDecoderPosition(0),
michael@0 412 mPlaybackPosition(0),
michael@0 413 mCurrentTime(0.0),
michael@0 414 mInitialVolume(0.0),
michael@0 415 mInitialPlaybackRate(1.0),
michael@0 416 mInitialPreservesPitch(true),
michael@0 417 mDuration(-1),
michael@0 418 mTransportSeekable(true),
michael@0 419 mMediaSeekable(true),
michael@0 420 mSameOriginMedia(false),
michael@0 421 mReentrantMonitor("media.decoder"),
michael@0 422 mIsDormant(false),
michael@0 423 mIsExitingDormant(false),
michael@0 424 mPlayState(PLAY_STATE_PAUSED),
michael@0 425 mNextState(PLAY_STATE_PAUSED),
michael@0 426 mCalledResourceLoaded(false),
michael@0 427 mIgnoreProgressData(false),
michael@0 428 mInfiniteStream(false),
michael@0 429 mOwner(nullptr),
michael@0 430 mPinnedForSeek(false),
michael@0 431 mShuttingDown(false),
michael@0 432 mPausedForPlaybackRateNull(false),
michael@0 433 mMinimizePreroll(false)
michael@0 434 {
michael@0 435 MOZ_COUNT_CTOR(MediaDecoder);
michael@0 436 MOZ_ASSERT(NS_IsMainThread());
michael@0 437 MediaMemoryTracker::AddMediaDecoder(this);
michael@0 438 #ifdef PR_LOGGING
michael@0 439 if (!gMediaDecoderLog) {
michael@0 440 gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
michael@0 441 }
michael@0 442 #endif
michael@0 443
michael@0 444 mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
michael@0 445 }
michael@0 446
michael@0 447 bool MediaDecoder::Init(MediaDecoderOwner* aOwner)
michael@0 448 {
michael@0 449 MOZ_ASSERT(NS_IsMainThread());
michael@0 450 mOwner = aOwner;
michael@0 451 mVideoFrameContainer = aOwner->GetVideoFrameContainer();
michael@0 452 MediaShutdownManager::Instance().Register(this);
michael@0 453 return true;
michael@0 454 }
michael@0 455
michael@0 456 void MediaDecoder::Shutdown()
michael@0 457 {
michael@0 458 MOZ_ASSERT(NS_IsMainThread());
michael@0 459
michael@0 460 if (mShuttingDown)
michael@0 461 return;
michael@0 462
michael@0 463 mShuttingDown = true;
michael@0 464
michael@0 465 {
michael@0 466 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 467 DestroyDecodedStream();
michael@0 468 }
michael@0 469
michael@0 470 // This changes the decoder state to SHUTDOWN and does other things
michael@0 471 // necessary to unblock the state machine thread if it's blocked, so
michael@0 472 // the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
michael@0 473 if (mDecoderStateMachine) {
michael@0 474 mDecoderStateMachine->Shutdown();
michael@0 475 }
michael@0 476
michael@0 477 // Force any outstanding seek and byterange requests to complete
michael@0 478 // to prevent shutdown from deadlocking.
michael@0 479 if (mResource) {
michael@0 480 mResource->Close();
michael@0 481 }
michael@0 482
michael@0 483 ChangeState(PLAY_STATE_SHUTDOWN);
michael@0 484
michael@0 485 StopProgress();
michael@0 486 mOwner = nullptr;
michael@0 487
michael@0 488 MediaShutdownManager::Instance().Unregister(this);
michael@0 489 }
michael@0 490
michael@0 491 MediaDecoder::~MediaDecoder()
michael@0 492 {
michael@0 493 MOZ_ASSERT(NS_IsMainThread());
michael@0 494 MediaMemoryTracker::RemoveMediaDecoder(this);
michael@0 495 UnpinForSeek();
michael@0 496 MOZ_COUNT_DTOR(MediaDecoder);
michael@0 497 }
michael@0 498
michael@0 499 nsresult MediaDecoder::OpenResource(nsIStreamListener** aStreamListener)
michael@0 500 {
michael@0 501 MOZ_ASSERT(NS_IsMainThread());
michael@0 502 if (aStreamListener) {
michael@0 503 *aStreamListener = nullptr;
michael@0 504 }
michael@0 505
michael@0 506 {
michael@0 507 // Hold the lock while we do this to set proper lock ordering
michael@0 508 // expectations for dynamic deadlock detectors: decoder lock(s)
michael@0 509 // should be grabbed before the cache lock
michael@0 510 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 511
michael@0 512 nsresult rv = mResource->Open(aStreamListener);
michael@0 513 if (NS_FAILED(rv)) {
michael@0 514 DECODER_LOG(PR_LOG_WARNING, "Failed to open stream!");
michael@0 515 return rv;
michael@0 516 }
michael@0 517 }
michael@0 518 return NS_OK;
michael@0 519 }
michael@0 520
michael@0 521 nsresult MediaDecoder::Load(nsIStreamListener** aStreamListener,
michael@0 522 MediaDecoder* aCloneDonor)
michael@0 523 {
michael@0 524 MOZ_ASSERT(NS_IsMainThread());
michael@0 525
michael@0 526 nsresult rv = OpenResource(aStreamListener);
michael@0 527 NS_ENSURE_SUCCESS(rv, rv);
michael@0 528
michael@0 529 mDecoderStateMachine = CreateStateMachine();
michael@0 530 if (!mDecoderStateMachine) {
michael@0 531 DECODER_LOG(PR_LOG_WARNING, "Failed to create state machine!");
michael@0 532 return NS_ERROR_FAILURE;
michael@0 533 }
michael@0 534
michael@0 535 return InitializeStateMachine(aCloneDonor);
michael@0 536 }
michael@0 537
michael@0 538 nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
michael@0 539 {
michael@0 540 MOZ_ASSERT(NS_IsMainThread());
michael@0 541 NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
michael@0 542
michael@0 543 MediaDecoder* cloneDonor = static_cast<MediaDecoder*>(aCloneDonor);
michael@0 544 if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ?
michael@0 545 cloneDonor->mDecoderStateMachine : nullptr))) {
michael@0 546 DECODER_LOG(PR_LOG_WARNING, "Failed to init state machine!");
michael@0 547 return NS_ERROR_FAILURE;
michael@0 548 }
michael@0 549 {
michael@0 550 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 551 mDecoderStateMachine->SetTransportSeekable(mTransportSeekable);
michael@0 552 mDecoderStateMachine->SetMediaSeekable(mMediaSeekable);
michael@0 553 mDecoderStateMachine->SetDuration(mDuration);
michael@0 554 mDecoderStateMachine->SetVolume(mInitialVolume);
michael@0 555 mDecoderStateMachine->SetAudioCaptured(mInitialAudioCaptured);
michael@0 556 SetPlaybackRate(mInitialPlaybackRate);
michael@0 557 mDecoderStateMachine->SetPreservesPitch(mInitialPreservesPitch);
michael@0 558 if (mMinimizePreroll) {
michael@0 559 mDecoderStateMachine->SetMinimizePrerollUntilPlaybackStarts();
michael@0 560 }
michael@0 561 }
michael@0 562
michael@0 563 ChangeState(PLAY_STATE_LOADING);
michael@0 564
michael@0 565 return ScheduleStateMachineThread();
michael@0 566 }
michael@0 567
michael@0 568 void MediaDecoder::SetMinimizePrerollUntilPlaybackStarts()
michael@0 569 {
michael@0 570 MOZ_ASSERT(NS_IsMainThread());
michael@0 571 mMinimizePreroll = true;
michael@0 572 }
michael@0 573
michael@0 574 nsresult MediaDecoder::ScheduleStateMachineThread()
michael@0 575 {
michael@0 576 MOZ_ASSERT(NS_IsMainThread());
michael@0 577 NS_ASSERTION(mDecoderStateMachine,
michael@0 578 "Must have state machine to start state machine thread");
michael@0 579 NS_ENSURE_STATE(mDecoderStateMachine);
michael@0 580
michael@0 581 if (mShuttingDown)
michael@0 582 return NS_OK;
michael@0 583 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 584 return mDecoderStateMachine->ScheduleStateMachine();
michael@0 585 }
michael@0 586
michael@0 587 nsresult MediaDecoder::Play()
michael@0 588 {
michael@0 589 MOZ_ASSERT(NS_IsMainThread());
michael@0 590 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 591 NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
michael@0 592 nsresult res = ScheduleStateMachineThread();
michael@0 593 NS_ENSURE_SUCCESS(res,res);
michael@0 594 if ((mPlayState == PLAY_STATE_LOADING && mIsDormant) || mPlayState == PLAY_STATE_SEEKING) {
michael@0 595 mNextState = PLAY_STATE_PLAYING;
michael@0 596 return NS_OK;
michael@0 597 }
michael@0 598 if (mPlayState == PLAY_STATE_ENDED)
michael@0 599 return Seek(0, SeekTarget::PrevSyncPoint);
michael@0 600
michael@0 601 ChangeState(PLAY_STATE_PLAYING);
michael@0 602 return NS_OK;
michael@0 603 }
michael@0 604
michael@0 605 nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
michael@0 606 {
michael@0 607 MOZ_ASSERT(NS_IsMainThread());
michael@0 608 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 609
michael@0 610 NS_ABORT_IF_FALSE(aTime >= 0.0, "Cannot seek to a negative value.");
michael@0 611
michael@0 612 int64_t timeUsecs = 0;
michael@0 613 nsresult rv = SecondsToUsecs(aTime, timeUsecs);
michael@0 614 NS_ENSURE_SUCCESS(rv, rv);
michael@0 615
michael@0 616 mRequestedSeekTarget = SeekTarget(timeUsecs, aSeekType);
michael@0 617 mCurrentTime = aTime;
michael@0 618
michael@0 619 // If we are already in the seeking state, then setting mRequestedSeekTarget
michael@0 620 // above will result in the new seek occurring when the current seek
michael@0 621 // completes.
michael@0 622 if ((mPlayState != PLAY_STATE_LOADING || !mIsDormant) && mPlayState != PLAY_STATE_SEEKING) {
michael@0 623 bool paused = false;
michael@0 624 if (mOwner) {
michael@0 625 paused = mOwner->GetPaused();
michael@0 626 }
michael@0 627 mNextState = paused ? PLAY_STATE_PAUSED : PLAY_STATE_PLAYING;
michael@0 628 PinForSeek();
michael@0 629 ChangeState(PLAY_STATE_SEEKING);
michael@0 630 }
michael@0 631
michael@0 632 return ScheduleStateMachineThread();
michael@0 633 }
michael@0 634
michael@0 635 bool MediaDecoder::IsLogicallyPlaying()
michael@0 636 {
michael@0 637 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 638 return mPlayState == PLAY_STATE_PLAYING ||
michael@0 639 mNextState == PLAY_STATE_PLAYING;
michael@0 640 }
michael@0 641
michael@0 642 double MediaDecoder::GetCurrentTime()
michael@0 643 {
michael@0 644 MOZ_ASSERT(NS_IsMainThread());
michael@0 645 return mCurrentTime;
michael@0 646 }
michael@0 647
michael@0 648 already_AddRefed<nsIPrincipal> MediaDecoder::GetCurrentPrincipal()
michael@0 649 {
michael@0 650 MOZ_ASSERT(NS_IsMainThread());
michael@0 651 return mResource ? mResource->GetCurrentPrincipal() : nullptr;
michael@0 652 }
michael@0 653
michael@0 654 void MediaDecoder::QueueMetadata(int64_t aPublishTime,
michael@0 655 int aChannels,
michael@0 656 int aRate,
michael@0 657 bool aHasAudio,
michael@0 658 bool aHasVideo,
michael@0 659 MetadataTags* aTags)
michael@0 660 {
michael@0 661 NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
michael@0 662 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 663 mDecoderStateMachine->QueueMetadata(aPublishTime, aChannels, aRate, aHasAudio, aHasVideo, aTags);
michael@0 664 }
michael@0 665
michael@0 666 bool
michael@0 667 MediaDecoder::IsDataCachedToEndOfResource()
michael@0 668 {
michael@0 669 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 670 return (mResource &&
michael@0 671 mResource->IsDataCachedToEndOfResource(mDecoderPosition));
michael@0 672 }
michael@0 673
michael@0 674 void MediaDecoder::MetadataLoaded(int aChannels, int aRate, bool aHasAudio, bool aHasVideo, MetadataTags* aTags)
michael@0 675 {
michael@0 676 MOZ_ASSERT(NS_IsMainThread());
michael@0 677 if (mShuttingDown) {
michael@0 678 return;
michael@0 679 }
michael@0 680
michael@0 681 {
michael@0 682 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 683 if (mPlayState == PLAY_STATE_LOADING && mIsDormant && !mIsExitingDormant) {
michael@0 684 return;
michael@0 685 } else if (mPlayState == PLAY_STATE_LOADING && mIsDormant && mIsExitingDormant) {
michael@0 686 mIsDormant = false;
michael@0 687 mIsExitingDormant = false;
michael@0 688 }
michael@0 689 mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
michael@0 690 // Duration has changed so we should recompute playback rate
michael@0 691 UpdatePlaybackRate();
michael@0 692 }
michael@0 693
michael@0 694 if (mDuration == -1) {
michael@0 695 SetInfinite(true);
michael@0 696 }
michael@0 697
michael@0 698 if (mOwner) {
michael@0 699 // Make sure the element and the frame (if any) are told about
michael@0 700 // our new size.
michael@0 701 Invalidate();
michael@0 702 mOwner->MetadataLoaded(aChannels, aRate, aHasAudio, aHasVideo, aTags);
michael@0 703 }
michael@0 704
michael@0 705 if (!mCalledResourceLoaded) {
michael@0 706 StartProgress();
michael@0 707 } else if (mOwner) {
michael@0 708 // Resource was loaded during metadata loading, when progress
michael@0 709 // events are being ignored. Fire the final progress event.
michael@0 710 mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
michael@0 711 }
michael@0 712
michael@0 713 // Only inform the element of FirstFrameLoaded if not doing a load() in order
michael@0 714 // to fulfill a seek, otherwise we'll get multiple loadedfirstframe events.
michael@0 715 bool notifyResourceIsLoaded = !mCalledResourceLoaded &&
michael@0 716 IsDataCachedToEndOfResource();
michael@0 717 if (mOwner) {
michael@0 718 mOwner->FirstFrameLoaded(notifyResourceIsLoaded);
michael@0 719 }
michael@0 720
michael@0 721 // This can run cache callbacks.
michael@0 722 mResource->EnsureCacheUpToDate();
michael@0 723
michael@0 724 // The element can run javascript via events
michael@0 725 // before reaching here, so only change the
michael@0 726 // state if we're still set to the original
michael@0 727 // loading state.
michael@0 728 if (mPlayState == PLAY_STATE_LOADING) {
michael@0 729 if (mRequestedSeekTarget.IsValid()) {
michael@0 730 ChangeState(PLAY_STATE_SEEKING);
michael@0 731 }
michael@0 732 else {
michael@0 733 ChangeState(mNextState);
michael@0 734 }
michael@0 735 }
michael@0 736
michael@0 737 if (notifyResourceIsLoaded) {
michael@0 738 ResourceLoaded();
michael@0 739 }
michael@0 740
michael@0 741 // Run NotifySuspendedStatusChanged now to give us a chance to notice
michael@0 742 // that autoplay should run.
michael@0 743 NotifySuspendedStatusChanged();
michael@0 744 }
michael@0 745
michael@0 746 void MediaDecoder::ResourceLoaded()
michael@0 747 {
michael@0 748 MOZ_ASSERT(NS_IsMainThread());
michael@0 749
michael@0 750 // Don't handle ResourceLoaded if we are shutting down, or if
michael@0 751 // we need to ignore progress data due to seeking (in the case
michael@0 752 // that the seek results in reaching end of file, we get a bogus call
michael@0 753 // to ResourceLoaded).
michael@0 754 if (mShuttingDown)
michael@0 755 return;
michael@0 756
michael@0 757 {
michael@0 758 // If we are seeking or loading then the resource loaded notification we get
michael@0 759 // should be ignored, since it represents the end of the seek request.
michael@0 760 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 761 if (mIgnoreProgressData || mCalledResourceLoaded || mPlayState == PLAY_STATE_LOADING)
michael@0 762 return;
michael@0 763
michael@0 764 Progress(false);
michael@0 765
michael@0 766 mCalledResourceLoaded = true;
michael@0 767 StopProgress();
michael@0 768 }
michael@0 769
michael@0 770 // Ensure the final progress event gets fired
michael@0 771 if (mOwner) {
michael@0 772 mOwner->ResourceLoaded();
michael@0 773 }
michael@0 774 }
michael@0 775
michael@0 776 void MediaDecoder::ResetConnectionState()
michael@0 777 {
michael@0 778 MOZ_ASSERT(NS_IsMainThread());
michael@0 779 if (mShuttingDown)
michael@0 780 return;
michael@0 781
michael@0 782 if (mOwner) {
michael@0 783 // Notify the media element that connection gets lost.
michael@0 784 mOwner->ResetConnectionState();
michael@0 785 }
michael@0 786
michael@0 787 // Since we have notified the media element the connection
michael@0 788 // lost event, the decoder will be reloaded when user tries
michael@0 789 // to play the Rtsp streaming next time.
michael@0 790 Shutdown();
michael@0 791 }
michael@0 792
michael@0 793 void MediaDecoder::NetworkError()
michael@0 794 {
michael@0 795 MOZ_ASSERT(NS_IsMainThread());
michael@0 796 if (mShuttingDown)
michael@0 797 return;
michael@0 798
michael@0 799 if (mOwner)
michael@0 800 mOwner->NetworkError();
michael@0 801
michael@0 802 Shutdown();
michael@0 803 }
michael@0 804
michael@0 805 void MediaDecoder::DecodeError()
michael@0 806 {
michael@0 807 MOZ_ASSERT(NS_IsMainThread());
michael@0 808 if (mShuttingDown)
michael@0 809 return;
michael@0 810
michael@0 811 if (mOwner)
michael@0 812 mOwner->DecodeError();
michael@0 813
michael@0 814 Shutdown();
michael@0 815 }
michael@0 816
michael@0 817 void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin)
michael@0 818 {
michael@0 819 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 820 mSameOriginMedia = aSameOrigin;
michael@0 821 }
michael@0 822
michael@0 823 bool MediaDecoder::IsSameOriginMedia()
michael@0 824 {
michael@0 825 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 826 return mSameOriginMedia;
michael@0 827 }
michael@0 828
michael@0 829 bool MediaDecoder::IsSeeking() const
michael@0 830 {
michael@0 831 MOZ_ASSERT(NS_IsMainThread());
michael@0 832 return mPlayState == PLAY_STATE_SEEKING;
michael@0 833 }
michael@0 834
michael@0 835 bool MediaDecoder::IsEnded() const
michael@0 836 {
michael@0 837 MOZ_ASSERT(NS_IsMainThread());
michael@0 838 return mPlayState == PLAY_STATE_ENDED || mPlayState == PLAY_STATE_SHUTDOWN;
michael@0 839 }
michael@0 840
michael@0 841 void MediaDecoder::PlaybackEnded()
michael@0 842 {
michael@0 843 MOZ_ASSERT(NS_IsMainThread());
michael@0 844
michael@0 845 if (mShuttingDown || mPlayState == MediaDecoder::PLAY_STATE_SEEKING)
michael@0 846 return;
michael@0 847
michael@0 848 {
michael@0 849 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 850
michael@0 851 for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
michael@0 852 OutputStreamData& os = mOutputStreams[i];
michael@0 853 if (os.mStream->IsDestroyed()) {
michael@0 854 // Probably the DOM MediaStream was GCed. Clean up.
michael@0 855 os.mPort->Destroy();
michael@0 856 mOutputStreams.RemoveElementAt(i);
michael@0 857 continue;
michael@0 858 }
michael@0 859 if (os.mFinishWhenEnded) {
michael@0 860 // Shouldn't really be needed since mDecodedStream should already have
michael@0 861 // finished, but doesn't hurt.
michael@0 862 os.mStream->Finish();
michael@0 863 os.mPort->Destroy();
michael@0 864 // Not really needed but it keeps the invariant that a stream not
michael@0 865 // connected to mDecodedStream is explicity blocked.
michael@0 866 os.mStream->ChangeExplicitBlockerCount(1);
michael@0 867 mOutputStreams.RemoveElementAt(i);
michael@0 868 }
michael@0 869 }
michael@0 870 }
michael@0 871
michael@0 872 PlaybackPositionChanged();
michael@0 873 ChangeState(PLAY_STATE_ENDED);
michael@0 874 InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);
michael@0 875
michael@0 876 UpdateReadyStateForData();
michael@0 877 if (mOwner) {
michael@0 878 mOwner->PlaybackEnded();
michael@0 879 }
michael@0 880
michael@0 881 // This must be called after |mOwner->PlaybackEnded()| call above, in order
michael@0 882 // to fire the required durationchange.
michael@0 883 if (IsInfinite()) {
michael@0 884 SetInfinite(false);
michael@0 885 }
michael@0 886 }
michael@0 887
michael@0 888 NS_IMETHODIMP MediaDecoder::Observe(nsISupports *aSubjet,
michael@0 889 const char *aTopic,
michael@0 890 const char16_t *someData)
michael@0 891 {
michael@0 892 MOZ_ASSERT(NS_IsMainThread());
michael@0 893 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
michael@0 894 Shutdown();
michael@0 895 }
michael@0 896
michael@0 897 return NS_OK;
michael@0 898 }
michael@0 899
michael@0 900 MediaDecoder::Statistics
michael@0 901 MediaDecoder::GetStatistics()
michael@0 902 {
michael@0 903 MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread());
michael@0 904 Statistics result;
michael@0 905
michael@0 906 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 907 if (mResource) {
michael@0 908 result.mDownloadRate =
michael@0 909 mResource->GetDownloadRate(&result.mDownloadRateReliable);
michael@0 910 result.mDownloadPosition =
michael@0 911 mResource->GetCachedDataEnd(mDecoderPosition);
michael@0 912 result.mTotalBytes = mResource->GetLength();
michael@0 913 result.mPlaybackRate = ComputePlaybackRate(&result.mPlaybackRateReliable);
michael@0 914 result.mDecoderPosition = mDecoderPosition;
michael@0 915 result.mPlaybackPosition = mPlaybackPosition;
michael@0 916 }
michael@0 917 else {
michael@0 918 result.mDownloadRate = 0;
michael@0 919 result.mDownloadRateReliable = true;
michael@0 920 result.mPlaybackRate = 0;
michael@0 921 result.mPlaybackRateReliable = true;
michael@0 922 result.mDecoderPosition = 0;
michael@0 923 result.mPlaybackPosition = 0;
michael@0 924 result.mDownloadPosition = 0;
michael@0 925 result.mTotalBytes = 0;
michael@0 926 }
michael@0 927
michael@0 928 return result;
michael@0 929 }
michael@0 930
michael@0 931 double MediaDecoder::ComputePlaybackRate(bool* aReliable)
michael@0 932 {
michael@0 933 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 934 MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread() || OnDecodeThread());
michael@0 935
michael@0 936 int64_t length = mResource ? mResource->GetLength() : -1;
michael@0 937 if (mDuration >= 0 && length >= 0) {
michael@0 938 *aReliable = true;
michael@0 939 return length * static_cast<double>(USECS_PER_S) / mDuration;
michael@0 940 }
michael@0 941 return mPlaybackStatistics.GetRateAtLastStop(aReliable);
michael@0 942 }
michael@0 943
michael@0 944 void MediaDecoder::UpdatePlaybackRate()
michael@0 945 {
michael@0 946 MOZ_ASSERT(NS_IsMainThread() || OnStateMachineThread());
michael@0 947 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 948 if (!mResource)
michael@0 949 return;
michael@0 950 bool reliable;
michael@0 951 uint32_t rate = uint32_t(ComputePlaybackRate(&reliable));
michael@0 952 if (reliable) {
michael@0 953 // Avoid passing a zero rate
michael@0 954 rate = std::max(rate, 1u);
michael@0 955 }
michael@0 956 else {
michael@0 957 // Set a minimum rate of 10,000 bytes per second ... sometimes we just
michael@0 958 // don't have good data
michael@0 959 rate = std::max(rate, 10000u);
michael@0 960 }
michael@0 961 mResource->SetPlaybackRate(rate);
michael@0 962 }
michael@0 963
michael@0 964 void MediaDecoder::NotifySuspendedStatusChanged()
michael@0 965 {
michael@0 966 MOZ_ASSERT(NS_IsMainThread());
michael@0 967 if (!mResource)
michael@0 968 return;
michael@0 969 bool suspended = mResource->IsSuspendedByCache();
michael@0 970 if (mOwner) {
michael@0 971 mOwner->NotifySuspendedByCache(suspended);
michael@0 972 UpdateReadyStateForData();
michael@0 973 }
michael@0 974 }
michael@0 975
michael@0 976 void MediaDecoder::NotifyBytesDownloaded()
michael@0 977 {
michael@0 978 MOZ_ASSERT(NS_IsMainThread());
michael@0 979 {
michael@0 980 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 981 UpdatePlaybackRate();
michael@0 982 }
michael@0 983 UpdateReadyStateForData();
michael@0 984 Progress(false);
michael@0 985 }
michael@0 986
michael@0 987 void MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
michael@0 988 {
michael@0 989 MOZ_ASSERT(NS_IsMainThread());
michael@0 990
michael@0 991 if (aStatus == NS_BINDING_ABORTED) {
michael@0 992 // Download has been cancelled by user.
michael@0 993 if (mOwner) {
michael@0 994 mOwner->LoadAborted();
michael@0 995 }
michael@0 996 return;
michael@0 997 }
michael@0 998
michael@0 999 {
michael@0 1000 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1001 UpdatePlaybackRate();
michael@0 1002 }
michael@0 1003
michael@0 1004 if (NS_SUCCEEDED(aStatus)) {
michael@0 1005 ResourceLoaded();
michael@0 1006 }
michael@0 1007 else if (aStatus != NS_BASE_STREAM_CLOSED) {
michael@0 1008 NetworkError();
michael@0 1009 }
michael@0 1010 UpdateReadyStateForData();
michael@0 1011 }
michael@0 1012
michael@0 1013 void MediaDecoder::NotifyPrincipalChanged()
michael@0 1014 {
michael@0 1015 if (mOwner) {
michael@0 1016 mOwner->NotifyDecoderPrincipalChanged();
michael@0 1017 }
michael@0 1018 }
michael@0 1019
michael@0 1020 void MediaDecoder::NotifyBytesConsumed(int64_t aBytes, int64_t aOffset)
michael@0 1021 {
michael@0 1022 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1023 NS_ENSURE_TRUE_VOID(mDecoderStateMachine);
michael@0 1024 if (mIgnoreProgressData) {
michael@0 1025 return;
michael@0 1026 }
michael@0 1027 if (aOffset >= mDecoderPosition) {
michael@0 1028 mPlaybackStatistics.AddBytes(aBytes);
michael@0 1029 }
michael@0 1030 mDecoderPosition = aOffset + aBytes;
michael@0 1031 }
michael@0 1032
michael@0 1033 void MediaDecoder::UpdateReadyStateForData()
michael@0 1034 {
michael@0 1035 MOZ_ASSERT(NS_IsMainThread());
michael@0 1036 if (!mOwner || mShuttingDown || !mDecoderStateMachine)
michael@0 1037 return;
michael@0 1038 MediaDecoderOwner::NextFrameStatus frameStatus =
michael@0 1039 mDecoderStateMachine->GetNextFrameStatus();
michael@0 1040 mOwner->UpdateReadyStateForData(frameStatus);
michael@0 1041 }
michael@0 1042
michael@0 1043 void MediaDecoder::SeekingStopped()
michael@0 1044 {
michael@0 1045 MOZ_ASSERT(NS_IsMainThread());
michael@0 1046
michael@0 1047 if (mShuttingDown)
michael@0 1048 return;
michael@0 1049
michael@0 1050 bool seekWasAborted = false;
michael@0 1051 {
michael@0 1052 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1053
michael@0 1054 // An additional seek was requested while the current seek was
michael@0 1055 // in operation.
michael@0 1056 if (mRequestedSeekTarget.IsValid()) {
michael@0 1057 ChangeState(PLAY_STATE_SEEKING);
michael@0 1058 seekWasAborted = true;
michael@0 1059 } else {
michael@0 1060 UnpinForSeek();
michael@0 1061 ChangeState(mNextState);
michael@0 1062 }
michael@0 1063 }
michael@0 1064
michael@0 1065 PlaybackPositionChanged();
michael@0 1066
michael@0 1067 if (mOwner) {
michael@0 1068 UpdateReadyStateForData();
michael@0 1069 if (!seekWasAborted) {
michael@0 1070 mOwner->SeekCompleted();
michael@0 1071 }
michael@0 1072 }
michael@0 1073 }
michael@0 1074
michael@0 1075 // This is called when seeking stopped *and* we're at the end of the
michael@0 1076 // media.
michael@0 1077 void MediaDecoder::SeekingStoppedAtEnd()
michael@0 1078 {
michael@0 1079 MOZ_ASSERT(NS_IsMainThread());
michael@0 1080
michael@0 1081 if (mShuttingDown)
michael@0 1082 return;
michael@0 1083
michael@0 1084 bool fireEnded = false;
michael@0 1085 bool seekWasAborted = false;
michael@0 1086 {
michael@0 1087 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1088
michael@0 1089 // An additional seek was requested while the current seek was
michael@0 1090 // in operation.
michael@0 1091 if (mRequestedSeekTarget.IsValid()) {
michael@0 1092 ChangeState(PLAY_STATE_SEEKING);
michael@0 1093 seekWasAborted = true;
michael@0 1094 } else {
michael@0 1095 UnpinForSeek();
michael@0 1096 fireEnded = true;
michael@0 1097 ChangeState(PLAY_STATE_ENDED);
michael@0 1098 }
michael@0 1099 }
michael@0 1100
michael@0 1101 PlaybackPositionChanged();
michael@0 1102
michael@0 1103 if (mOwner) {
michael@0 1104 UpdateReadyStateForData();
michael@0 1105 if (!seekWasAborted) {
michael@0 1106 mOwner->SeekCompleted();
michael@0 1107 if (fireEnded) {
michael@0 1108 mOwner->PlaybackEnded();
michael@0 1109 }
michael@0 1110 }
michael@0 1111 }
michael@0 1112 }
michael@0 1113
michael@0 1114 void MediaDecoder::SeekingStarted()
michael@0 1115 {
michael@0 1116 MOZ_ASSERT(NS_IsMainThread());
michael@0 1117 if (mShuttingDown)
michael@0 1118 return;
michael@0 1119
michael@0 1120 if (mOwner) {
michael@0 1121 UpdateReadyStateForData();
michael@0 1122 mOwner->SeekStarted();
michael@0 1123 }
michael@0 1124 }
michael@0 1125
michael@0 1126 void MediaDecoder::ChangeState(PlayState aState)
michael@0 1127 {
michael@0 1128 MOZ_ASSERT(NS_IsMainThread());
michael@0 1129 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1130
michael@0 1131 if (mNextState == aState) {
michael@0 1132 mNextState = PLAY_STATE_PAUSED;
michael@0 1133 }
michael@0 1134
michael@0 1135 if ((mPlayState == PLAY_STATE_LOADING && mIsDormant && aState != PLAY_STATE_SHUTDOWN) ||
michael@0 1136 mPlayState == PLAY_STATE_SHUTDOWN) {
michael@0 1137 GetReentrantMonitor().NotifyAll();
michael@0 1138 return;
michael@0 1139 }
michael@0 1140
michael@0 1141 if (mDecodedStream) {
michael@0 1142 bool blockForPlayState = aState != PLAY_STATE_PLAYING;
michael@0 1143 if (mDecodedStream->mHaveBlockedForPlayState != blockForPlayState) {
michael@0 1144 mDecodedStream->mStream->ChangeExplicitBlockerCount(blockForPlayState ? 1 : -1);
michael@0 1145 mDecodedStream->mHaveBlockedForPlayState = blockForPlayState;
michael@0 1146 }
michael@0 1147 }
michael@0 1148 mPlayState = aState;
michael@0 1149
michael@0 1150 ApplyStateToStateMachine(mPlayState);
michael@0 1151
michael@0 1152 if (aState!= PLAY_STATE_LOADING) {
michael@0 1153 mIsDormant = false;
michael@0 1154 mIsExitingDormant = false;
michael@0 1155 }
michael@0 1156
michael@0 1157 GetReentrantMonitor().NotifyAll();
michael@0 1158 }
michael@0 1159
michael@0 1160 void MediaDecoder::ApplyStateToStateMachine(PlayState aState)
michael@0 1161 {
michael@0 1162 MOZ_ASSERT(NS_IsMainThread());
michael@0 1163 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 1164
michael@0 1165 if (mDecoderStateMachine) {
michael@0 1166 switch (aState) {
michael@0 1167 case PLAY_STATE_PLAYING:
michael@0 1168 mDecoderStateMachine->Play();
michael@0 1169 break;
michael@0 1170 case PLAY_STATE_SEEKING:
michael@0 1171 mDecoderStateMachine->Seek(mRequestedSeekTarget);
michael@0 1172 mRequestedSeekTarget.Reset();
michael@0 1173 break;
michael@0 1174 default:
michael@0 1175 /* No action needed */
michael@0 1176 break;
michael@0 1177 }
michael@0 1178 }
michael@0 1179 }
michael@0 1180
michael@0 1181 void MediaDecoder::PlaybackPositionChanged()
michael@0 1182 {
michael@0 1183 MOZ_ASSERT(NS_IsMainThread());
michael@0 1184 if (mShuttingDown)
michael@0 1185 return;
michael@0 1186
michael@0 1187 double lastTime = mCurrentTime;
michael@0 1188
michael@0 1189 // Control the scope of the monitor so it is not
michael@0 1190 // held while the timeupdate and the invalidate is run.
michael@0 1191 {
michael@0 1192 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1193 if (mDecoderStateMachine) {
michael@0 1194 if (!IsSeeking()) {
michael@0 1195 // Only update the current playback position if we're not seeking.
michael@0 1196 // If we are seeking, the update could have been scheduled on the
michael@0 1197 // state machine thread while we were playing but after the seek
michael@0 1198 // algorithm set the current playback position on the main thread,
michael@0 1199 // and we don't want to override the seek algorithm and change the
michael@0 1200 // current time after the seek has started but before it has
michael@0 1201 // completed.
michael@0 1202 if (GetDecodedStream()) {
michael@0 1203 mCurrentTime = mDecoderStateMachine->GetCurrentTimeViaMediaStreamSync()/
michael@0 1204 static_cast<double>(USECS_PER_S);
michael@0 1205 } else {
michael@0 1206 mCurrentTime = mDecoderStateMachine->GetCurrentTime();
michael@0 1207 }
michael@0 1208 }
michael@0 1209 mDecoderStateMachine->ClearPositionChangeFlag();
michael@0 1210 }
michael@0 1211 }
michael@0 1212
michael@0 1213 // Invalidate the frame so any video data is displayed.
michael@0 1214 // Do this before the timeupdate event so that if that
michael@0 1215 // event runs JavaScript that queries the media size, the
michael@0 1216 // frame has reflowed and the size updated beforehand.
michael@0 1217 Invalidate();
michael@0 1218
michael@0 1219 if (mOwner && lastTime != mCurrentTime) {
michael@0 1220 FireTimeUpdate();
michael@0 1221 }
michael@0 1222 }
michael@0 1223
michael@0 1224 void MediaDecoder::DurationChanged()
michael@0 1225 {
michael@0 1226 MOZ_ASSERT(NS_IsMainThread());
michael@0 1227 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1228 int64_t oldDuration = mDuration;
michael@0 1229 mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
michael@0 1230 // Duration has changed so we should recompute playback rate
michael@0 1231 UpdatePlaybackRate();
michael@0 1232
michael@0 1233 SetInfinite(mDuration == -1);
michael@0 1234
michael@0 1235 if (mOwner && oldDuration != mDuration && !IsInfinite()) {
michael@0 1236 DECODER_LOG(PR_LOG_DEBUG, "Duration changed to %lld", mDuration);
michael@0 1237 mOwner->DispatchEvent(NS_LITERAL_STRING("durationchange"));
michael@0 1238 }
michael@0 1239 }
michael@0 1240
michael@0 1241 void MediaDecoder::SetDuration(double aDuration)
michael@0 1242 {
michael@0 1243 MOZ_ASSERT(NS_IsMainThread());
michael@0 1244 if (mozilla::IsInfinite(aDuration)) {
michael@0 1245 SetInfinite(true);
michael@0 1246 } else if (IsNaN(aDuration)) {
michael@0 1247 mDuration = -1;
michael@0 1248 SetInfinite(true);
michael@0 1249 } else {
michael@0 1250 mDuration = static_cast<int64_t>(NS_round(aDuration * static_cast<double>(USECS_PER_S)));
michael@0 1251 }
michael@0 1252
michael@0 1253 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1254 if (mDecoderStateMachine) {
michael@0 1255 mDecoderStateMachine->SetDuration(mDuration);
michael@0 1256 }
michael@0 1257
michael@0 1258 // Duration has changed so we should recompute playback rate
michael@0 1259 UpdatePlaybackRate();
michael@0 1260 }
michael@0 1261
michael@0 1262 void MediaDecoder::SetMediaDuration(int64_t aDuration)
michael@0 1263 {
michael@0 1264 NS_ENSURE_TRUE_VOID(GetStateMachine());
michael@0 1265 GetStateMachine()->SetDuration(aDuration);
michael@0 1266 }
michael@0 1267
michael@0 1268 void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
michael@0 1269 {
michael@0 1270 if (mPlayState <= PLAY_STATE_LOADING) {
michael@0 1271 return;
michael@0 1272 }
michael@0 1273 NS_ENSURE_TRUE_VOID(GetStateMachine());
michael@0 1274 GetStateMachine()->UpdateEstimatedDuration(aDuration);
michael@0 1275 }
michael@0 1276
michael@0 1277 void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {
michael@0 1278 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1279 MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread());
michael@0 1280 mMediaSeekable = aMediaSeekable;
michael@0 1281 if (mDecoderStateMachine) {
michael@0 1282 mDecoderStateMachine->SetMediaSeekable(aMediaSeekable);
michael@0 1283 }
michael@0 1284 }
michael@0 1285
michael@0 1286 void MediaDecoder::SetTransportSeekable(bool aTransportSeekable)
michael@0 1287 {
michael@0 1288 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1289 MOZ_ASSERT(NS_IsMainThread() || OnDecodeThread());
michael@0 1290 mTransportSeekable = aTransportSeekable;
michael@0 1291 if (mDecoderStateMachine) {
michael@0 1292 mDecoderStateMachine->SetTransportSeekable(aTransportSeekable);
michael@0 1293 }
michael@0 1294 }
michael@0 1295
michael@0 1296 bool MediaDecoder::IsTransportSeekable()
michael@0 1297 {
michael@0 1298 MOZ_ASSERT(NS_IsMainThread());
michael@0 1299 return mTransportSeekable;
michael@0 1300 }
michael@0 1301
michael@0 1302 bool MediaDecoder::IsMediaSeekable()
michael@0 1303 {
michael@0 1304 NS_ENSURE_TRUE(GetStateMachine(), false);
michael@0 1305 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1306 MOZ_ASSERT(OnDecodeThread() || NS_IsMainThread());
michael@0 1307 return mMediaSeekable;
michael@0 1308 }
michael@0 1309
michael@0 1310 nsresult MediaDecoder::GetSeekable(dom::TimeRanges* aSeekable)
michael@0 1311 {
michael@0 1312 double initialTime = 0.0;
michael@0 1313
michael@0 1314 // We can seek in buffered range if the media is seekable. Also, we can seek
michael@0 1315 // in unbuffered ranges if the transport level is seekable (local file or the
michael@0 1316 // server supports range requests, etc.)
michael@0 1317 if (!IsMediaSeekable()) {
michael@0 1318 return NS_OK;
michael@0 1319 } else if (!IsTransportSeekable()) {
michael@0 1320 return GetBuffered(aSeekable);
michael@0 1321 } else {
michael@0 1322 double end = IsInfinite() ? std::numeric_limits<double>::infinity()
michael@0 1323 : initialTime + GetDuration();
michael@0 1324 aSeekable->Add(initialTime, end);
michael@0 1325 return NS_OK;
michael@0 1326 }
michael@0 1327 }
michael@0 1328
michael@0 1329 void MediaDecoder::SetFragmentEndTime(double aTime)
michael@0 1330 {
michael@0 1331 MOZ_ASSERT(NS_IsMainThread());
michael@0 1332 if (mDecoderStateMachine) {
michael@0 1333 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1334 mDecoderStateMachine->SetFragmentEndTime(static_cast<int64_t>(aTime * USECS_PER_S));
michael@0 1335 }
michael@0 1336 }
michael@0 1337
michael@0 1338 void MediaDecoder::SetMediaEndTime(int64_t aTime)
michael@0 1339 {
michael@0 1340 NS_ENSURE_TRUE_VOID(GetStateMachine());
michael@0 1341 GetStateMachine()->SetMediaEndTime(aTime);
michael@0 1342 }
michael@0 1343
michael@0 1344 void MediaDecoder::Suspend()
michael@0 1345 {
michael@0 1346 MOZ_ASSERT(NS_IsMainThread());
michael@0 1347 if (mResource) {
michael@0 1348 mResource->Suspend(true);
michael@0 1349 }
michael@0 1350 }
michael@0 1351
michael@0 1352 void MediaDecoder::Resume(bool aForceBuffering)
michael@0 1353 {
michael@0 1354 MOZ_ASSERT(NS_IsMainThread());
michael@0 1355 if (mResource) {
michael@0 1356 mResource->Resume();
michael@0 1357 }
michael@0 1358 if (aForceBuffering) {
michael@0 1359 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1360 if (mDecoderStateMachine) {
michael@0 1361 mDecoderStateMachine->StartBuffering();
michael@0 1362 }
michael@0 1363 }
michael@0 1364 }
michael@0 1365
michael@0 1366 void MediaDecoder::StopProgressUpdates()
michael@0 1367 {
michael@0 1368 MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
michael@0 1369 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 1370 mIgnoreProgressData = true;
michael@0 1371 if (mResource) {
michael@0 1372 mResource->SetReadMode(MediaCacheStream::MODE_METADATA);
michael@0 1373 }
michael@0 1374 }
michael@0 1375
michael@0 1376 void MediaDecoder::StartProgressUpdates()
michael@0 1377 {
michael@0 1378 MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
michael@0 1379 GetReentrantMonitor().AssertCurrentThreadIn();
michael@0 1380 mIgnoreProgressData = false;
michael@0 1381 if (mResource) {
michael@0 1382 mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
michael@0 1383 mDecoderPosition = mPlaybackPosition = mResource->Tell();
michael@0 1384 }
michael@0 1385 }
michael@0 1386
michael@0 1387 void MediaDecoder::MoveLoadsToBackground()
michael@0 1388 {
michael@0 1389 MOZ_ASSERT(NS_IsMainThread());
michael@0 1390 if (mResource) {
michael@0 1391 mResource->MoveLoadsToBackground();
michael@0 1392 }
michael@0 1393 }
michael@0 1394
michael@0 1395 void MediaDecoder::UpdatePlaybackOffset(int64_t aOffset)
michael@0 1396 {
michael@0 1397 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1398 mPlaybackPosition = std::max(aOffset, mPlaybackPosition);
michael@0 1399 }
michael@0 1400
michael@0 1401 bool MediaDecoder::OnStateMachineThread() const
michael@0 1402 {
michael@0 1403 return mDecoderStateMachine->OnStateMachineThread();
michael@0 1404 }
michael@0 1405
michael@0 1406 void MediaDecoder::SetPlaybackRate(double aPlaybackRate)
michael@0 1407 {
michael@0 1408 if (aPlaybackRate == 0) {
michael@0 1409 mPausedForPlaybackRateNull = true;
michael@0 1410 Pause();
michael@0 1411 return;
michael@0 1412 } else if (mPausedForPlaybackRateNull) {
michael@0 1413 // If the playbackRate is no longer null, restart the playback, iff the
michael@0 1414 // media was playing.
michael@0 1415 if (mOwner && !mOwner->GetPaused()) {
michael@0 1416 Play();
michael@0 1417 }
michael@0 1418 mPausedForPlaybackRateNull = false;
michael@0 1419 }
michael@0 1420
michael@0 1421 if (mDecoderStateMachine) {
michael@0 1422 mDecoderStateMachine->SetPlaybackRate(aPlaybackRate);
michael@0 1423 } else {
michael@0 1424 mInitialPlaybackRate = aPlaybackRate;
michael@0 1425 }
michael@0 1426 }
michael@0 1427
michael@0 1428 void MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
michael@0 1429 {
michael@0 1430 if (mDecoderStateMachine) {
michael@0 1431 mDecoderStateMachine->SetPreservesPitch(aPreservesPitch);
michael@0 1432 } else {
michael@0 1433 mInitialPreservesPitch = aPreservesPitch;
michael@0 1434 }
michael@0 1435 }
michael@0 1436
michael@0 1437 bool MediaDecoder::OnDecodeThread() const {
michael@0 1438 NS_WARN_IF_FALSE(mDecoderStateMachine, "mDecoderStateMachine is null");
michael@0 1439 return mDecoderStateMachine ? mDecoderStateMachine->OnDecodeThread() : false;
michael@0 1440 }
michael@0 1441
michael@0 1442 ReentrantMonitor& MediaDecoder::GetReentrantMonitor() {
michael@0 1443 return mReentrantMonitor.GetReentrantMonitor();
michael@0 1444 }
michael@0 1445
michael@0 1446 ImageContainer* MediaDecoder::GetImageContainer()
michael@0 1447 {
michael@0 1448 return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer() : nullptr;
michael@0 1449 }
michael@0 1450
michael@0 1451 void MediaDecoder::InvalidateWithFlags(uint32_t aFlags)
michael@0 1452 {
michael@0 1453 if (mVideoFrameContainer) {
michael@0 1454 mVideoFrameContainer->InvalidateWithFlags(aFlags);
michael@0 1455 }
michael@0 1456 }
michael@0 1457
michael@0 1458 void MediaDecoder::Invalidate()
michael@0 1459 {
michael@0 1460 if (mVideoFrameContainer) {
michael@0 1461 mVideoFrameContainer->Invalidate();
michael@0 1462 }
michael@0 1463 }
michael@0 1464
michael@0 1465 // Constructs the time ranges representing what segments of the media
michael@0 1466 // are buffered and playable.
michael@0 1467 nsresult MediaDecoder::GetBuffered(dom::TimeRanges* aBuffered) {
michael@0 1468 if (mDecoderStateMachine) {
michael@0 1469 return mDecoderStateMachine->GetBuffered(aBuffered);
michael@0 1470 }
michael@0 1471 return NS_ERROR_FAILURE;
michael@0 1472 }
michael@0 1473
michael@0 1474 size_t MediaDecoder::SizeOfVideoQueue() {
michael@0 1475 if (mDecoderStateMachine) {
michael@0 1476 return mDecoderStateMachine->SizeOfVideoQueue();
michael@0 1477 }
michael@0 1478 return 0;
michael@0 1479 }
michael@0 1480
michael@0 1481 size_t MediaDecoder::SizeOfAudioQueue() {
michael@0 1482 if (mDecoderStateMachine) {
michael@0 1483 return mDecoderStateMachine->SizeOfAudioQueue();
michael@0 1484 }
michael@0 1485 return 0;
michael@0 1486 }
michael@0 1487
michael@0 1488 void MediaDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {
michael@0 1489 if (mDecoderStateMachine) {
michael@0 1490 mDecoderStateMachine->NotifyDataArrived(aBuffer, aLength, aOffset);
michael@0 1491 }
michael@0 1492 }
michael@0 1493
michael@0 1494 void MediaDecoder::UpdatePlaybackPosition(int64_t aTime)
michael@0 1495 {
michael@0 1496 mDecoderStateMachine->UpdatePlaybackPosition(aTime);
michael@0 1497 }
michael@0 1498
michael@0 1499 // Provide access to the state machine object
michael@0 1500 MediaDecoderStateMachine* MediaDecoder::GetStateMachine() const {
michael@0 1501 return mDecoderStateMachine;
michael@0 1502 }
michael@0 1503
michael@0 1504 void
michael@0 1505 MediaDecoder::NotifyWaitingForResourcesStatusChanged()
michael@0 1506 {
michael@0 1507 ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
michael@0 1508 if (mDecoderStateMachine) {
michael@0 1509 mDecoderStateMachine->NotifyWaitingForResourcesStatusChanged();
michael@0 1510 }
michael@0 1511 }
michael@0 1512
michael@0 1513 bool MediaDecoder::IsShutdown() const {
michael@0 1514 NS_ENSURE_TRUE(GetStateMachine(), true);
michael@0 1515 return GetStateMachine()->IsShutdown();
michael@0 1516 }
michael@0 1517
michael@0 1518 int64_t MediaDecoder::GetEndMediaTime() const {
michael@0 1519 NS_ENSURE_TRUE(GetStateMachine(), -1);
michael@0 1520 return GetStateMachine()->GetEndMediaTime();
michael@0 1521 }
michael@0 1522
michael@0 1523 // Drop reference to state machine. Only called during shutdown dance.
michael@0 1524 void MediaDecoder::ReleaseStateMachine() {
michael@0 1525 mDecoderStateMachine = nullptr;
michael@0 1526 }
michael@0 1527
michael@0 1528 MediaDecoderOwner* MediaDecoder::GetMediaOwner() const
michael@0 1529 {
michael@0 1530 return mOwner;
michael@0 1531 }
michael@0 1532
michael@0 1533 static void ProgressCallback(nsITimer* aTimer, void* aClosure)
michael@0 1534 {
michael@0 1535 MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure);
michael@0 1536 decoder->Progress(true);
michael@0 1537 }
michael@0 1538
michael@0 1539 void MediaDecoder::Progress(bool aTimer)
michael@0 1540 {
michael@0 1541 if (!mOwner)
michael@0 1542 return;
michael@0 1543
michael@0 1544 TimeStamp now = TimeStamp::Now();
michael@0 1545
michael@0 1546 if (!aTimer) {
michael@0 1547 mDataTime = now;
michael@0 1548 }
michael@0 1549
michael@0 1550 // If PROGRESS_MS has passed since the last progress event fired and more
michael@0 1551 // data has arrived since then, fire another progress event.
michael@0 1552 if ((mProgressTime.IsNull() ||
michael@0 1553 now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) &&
michael@0 1554 !mDataTime.IsNull() &&
michael@0 1555 now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) {
michael@0 1556 mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
michael@0 1557 mProgressTime = now;
michael@0 1558 }
michael@0 1559
michael@0 1560 if (!mDataTime.IsNull() &&
michael@0 1561 now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) {
michael@0 1562 mOwner->DownloadStalled();
michael@0 1563 // Null it out
michael@0 1564 mDataTime = TimeStamp();
michael@0 1565 }
michael@0 1566 }
michael@0 1567
michael@0 1568 nsresult MediaDecoder::StartProgress()
michael@0 1569 {
michael@0 1570 if (mProgressTimer)
michael@0 1571 return NS_OK;
michael@0 1572
michael@0 1573 mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
michael@0 1574 return mProgressTimer->InitWithFuncCallback(ProgressCallback,
michael@0 1575 this,
michael@0 1576 PROGRESS_MS,
michael@0 1577 nsITimer::TYPE_REPEATING_SLACK);
michael@0 1578 }
michael@0 1579
michael@0 1580 nsresult MediaDecoder::StopProgress()
michael@0 1581 {
michael@0 1582 if (!mProgressTimer)
michael@0 1583 return NS_OK;
michael@0 1584
michael@0 1585 nsresult rv = mProgressTimer->Cancel();
michael@0 1586 mProgressTimer = nullptr;
michael@0 1587
michael@0 1588 return rv;
michael@0 1589 }
michael@0 1590
michael@0 1591 void MediaDecoder::FireTimeUpdate()
michael@0 1592 {
michael@0 1593 if (!mOwner)
michael@0 1594 return;
michael@0 1595 mOwner->FireTimeUpdate(true);
michael@0 1596 }
michael@0 1597
michael@0 1598 void MediaDecoder::PinForSeek()
michael@0 1599 {
michael@0 1600 MediaResource* resource = GetResource();
michael@0 1601 if (!resource || mPinnedForSeek) {
michael@0 1602 return;
michael@0 1603 }
michael@0 1604 mPinnedForSeek = true;
michael@0 1605 resource->Pin();
michael@0 1606 }
michael@0 1607
michael@0 1608 void MediaDecoder::UnpinForSeek()
michael@0 1609 {
michael@0 1610 MediaResource* resource = GetResource();
michael@0 1611 if (!resource || !mPinnedForSeek) {
michael@0 1612 return;
michael@0 1613 }
michael@0 1614 mPinnedForSeek = false;
michael@0 1615 resource->Unpin();
michael@0 1616 }
michael@0 1617
michael@0 1618 bool MediaDecoder::CanPlayThrough()
michael@0 1619 {
michael@0 1620 Statistics stats = GetStatistics();
michael@0 1621 if (!stats.mDownloadRateReliable || !stats.mPlaybackRateReliable) {
michael@0 1622 return false;
michael@0 1623 }
michael@0 1624 int64_t bytesToDownload = stats.mTotalBytes - stats.mDownloadPosition;
michael@0 1625 int64_t bytesToPlayback = stats.mTotalBytes - stats.mPlaybackPosition;
michael@0 1626 double timeToDownload = bytesToDownload / stats.mDownloadRate;
michael@0 1627 double timeToPlay = bytesToPlayback / stats.mPlaybackRate;
michael@0 1628
michael@0 1629 if (timeToDownload > timeToPlay) {
michael@0 1630 // Estimated time to download is greater than the estimated time to play.
michael@0 1631 // We probably can't play through without having to stop to buffer.
michael@0 1632 return false;
michael@0 1633 }
michael@0 1634
michael@0 1635 // Estimated time to download is less than the estimated time to play.
michael@0 1636 // We can probably play through without having to buffer, but ensure that
michael@0 1637 // we've got a reasonable amount of data buffered after the current
michael@0 1638 // playback position, so that if the bitrate of the media fluctuates, or if
michael@0 1639 // our download rate or decode rate estimation is otherwise inaccurate,
michael@0 1640 // we don't suddenly discover that we need to buffer. This is particularly
michael@0 1641 // required near the start of the media, when not much data is downloaded.
michael@0 1642 int64_t readAheadMargin =
michael@0 1643 static_cast<int64_t>(stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN);
michael@0 1644 return stats.mTotalBytes == stats.mDownloadPosition ||
michael@0 1645 stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin;
michael@0 1646 }
michael@0 1647
michael@0 1648 #ifdef MOZ_RAW
michael@0 1649 bool
michael@0 1650 MediaDecoder::IsRawEnabled()
michael@0 1651 {
michael@0 1652 return Preferences::GetBool("media.raw.enabled");
michael@0 1653 }
michael@0 1654 #endif
michael@0 1655
michael@0 1656 bool
michael@0 1657 MediaDecoder::IsOpusEnabled()
michael@0 1658 {
michael@0 1659 #ifdef MOZ_OPUS
michael@0 1660 return Preferences::GetBool("media.opus.enabled");
michael@0 1661 #else
michael@0 1662 return false;
michael@0 1663 #endif
michael@0 1664 }
michael@0 1665
michael@0 1666 bool
michael@0 1667 MediaDecoder::IsOggEnabled()
michael@0 1668 {
michael@0 1669 return Preferences::GetBool("media.ogg.enabled");
michael@0 1670 }
michael@0 1671
michael@0 1672 #ifdef MOZ_WAVE
michael@0 1673 bool
michael@0 1674 MediaDecoder::IsWaveEnabled()
michael@0 1675 {
michael@0 1676 return Preferences::GetBool("media.wave.enabled");
michael@0 1677 }
michael@0 1678 #endif
michael@0 1679
michael@0 1680 #ifdef MOZ_WEBM
michael@0 1681 bool
michael@0 1682 MediaDecoder::IsWebMEnabled()
michael@0 1683 {
michael@0 1684 return Preferences::GetBool("media.webm.enabled");
michael@0 1685 }
michael@0 1686 #endif
michael@0 1687
michael@0 1688 #ifdef NECKO_PROTOCOL_rtsp
michael@0 1689 bool
michael@0 1690 MediaDecoder::IsRtspEnabled()
michael@0 1691 {
michael@0 1692 //Currently the Rtsp decoded by omx.
michael@0 1693 return (Preferences::GetBool("media.rtsp.enabled", false) && IsOmxEnabled());
michael@0 1694 }
michael@0 1695 #endif
michael@0 1696
michael@0 1697 #ifdef MOZ_GSTREAMER
michael@0 1698 bool
michael@0 1699 MediaDecoder::IsGStreamerEnabled()
michael@0 1700 {
michael@0 1701 return Preferences::GetBool("media.gstreamer.enabled");
michael@0 1702 }
michael@0 1703 #endif
michael@0 1704
michael@0 1705 #ifdef MOZ_OMX_DECODER
michael@0 1706 bool
michael@0 1707 MediaDecoder::IsOmxEnabled()
michael@0 1708 {
michael@0 1709 return Preferences::GetBool("media.omx.enabled", false);
michael@0 1710 }
michael@0 1711 #endif
michael@0 1712
michael@0 1713 #ifdef MOZ_MEDIA_PLUGINS
michael@0 1714 bool
michael@0 1715 MediaDecoder::IsMediaPluginsEnabled()
michael@0 1716 {
michael@0 1717 return Preferences::GetBool("media.plugins.enabled");
michael@0 1718 }
michael@0 1719 #endif
michael@0 1720
michael@0 1721 #ifdef MOZ_WMF
michael@0 1722 bool
michael@0 1723 MediaDecoder::IsWMFEnabled()
michael@0 1724 {
michael@0 1725 return WMFDecoder::IsEnabled();
michael@0 1726 }
michael@0 1727 #endif
michael@0 1728
michael@0 1729 #ifdef MOZ_APPLEMEDIA
michael@0 1730 bool
michael@0 1731 MediaDecoder::IsAppleMP3Enabled()
michael@0 1732 {
michael@0 1733 return Preferences::GetBool("media.apple.mp3.enabled");
michael@0 1734 }
michael@0 1735 #endif
michael@0 1736
michael@0 1737 NS_IMETHODIMP
michael@0 1738 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
michael@0 1739 nsISupports* aData)
michael@0 1740 {
michael@0 1741 int64_t video = 0, audio = 0;
michael@0 1742 size_t resources = 0;
michael@0 1743 DecodersArray& decoders = Decoders();
michael@0 1744 for (size_t i = 0; i < decoders.Length(); ++i) {
michael@0 1745 MediaDecoder* decoder = decoders[i];
michael@0 1746 video += decoder->SizeOfVideoQueue();
michael@0 1747 audio += decoder->SizeOfAudioQueue();
michael@0 1748
michael@0 1749 if (decoder->GetResource()) {
michael@0 1750 resources += decoder->GetResource()->SizeOfIncludingThis(MallocSizeOf);
michael@0 1751 }
michael@0 1752 }
michael@0 1753
michael@0 1754 #define REPORT(_path, _amount, _desc) \
michael@0 1755 do { \
michael@0 1756 nsresult rv; \
michael@0 1757 rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
michael@0 1758 KIND_HEAP, UNITS_BYTES, _amount, \
michael@0 1759 NS_LITERAL_CSTRING(_desc), aData); \
michael@0 1760 NS_ENSURE_SUCCESS(rv, rv); \
michael@0 1761 } while (0)
michael@0 1762
michael@0 1763 REPORT("explicit/media/decoded/video", video,
michael@0 1764 "Memory used by decoded video frames.");
michael@0 1765
michael@0 1766 REPORT("explicit/media/decoded/audio", audio,
michael@0 1767 "Memory used by decoded audio chunks.");
michael@0 1768
michael@0 1769 REPORT("explicit/media/resources", resources,
michael@0 1770 "Memory used by media resources including streaming buffers, caches, "
michael@0 1771 "etc.");
michael@0 1772
michael@0 1773 #undef REPORT
michael@0 1774
michael@0 1775 return NS_OK;
michael@0 1776 }
michael@0 1777
michael@0 1778 MediaDecoderOwner*
michael@0 1779 MediaDecoder::GetOwner()
michael@0 1780 {
michael@0 1781 MOZ_ASSERT(NS_IsMainThread());
michael@0 1782 return mOwner;
michael@0 1783 }
michael@0 1784
michael@0 1785 MediaMemoryTracker::MediaMemoryTracker()
michael@0 1786 {
michael@0 1787 }
michael@0 1788
michael@0 1789 void
michael@0 1790 MediaMemoryTracker::InitMemoryReporter()
michael@0 1791 {
michael@0 1792 RegisterWeakMemoryReporter(this);
michael@0 1793 }
michael@0 1794
michael@0 1795 MediaMemoryTracker::~MediaMemoryTracker()
michael@0 1796 {
michael@0 1797 UnregisterWeakMemoryReporter(this);
michael@0 1798 }
michael@0 1799
michael@0 1800 } // namespace mozilla
michael@0 1801
michael@0 1802 // avoid redefined macro in unified build
michael@0 1803 #undef DECODER_LOG

mercurial