author | Bobby Holley <bobbyholley@gmail.com> |
Mon, 29 Jun 2015 14:27:16 -0700 | |
changeset 251159 | 4272c902d349c7ca8972398b4d7490ba6cbe06fe |
parent 251158 | 44d9a47dfefdfa33fc28a7d5c6a96951de364925 |
child 251160 | 26e369c2f34fcf90e46957e0ecf84b6b8ecdf548 |
push id | 61784 |
push user | bobbyholley@gmail.com |
push date | Thu, 02 Jul 2015 19:38:54 +0000 |
treeherder | mozilla-inbound@4272c902d349 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jww |
bugs | 1178938 |
milestone | 42.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/media/AbstractMediaDecoder.h +++ b/dom/media/AbstractMediaDecoder.h @@ -49,16 +49,20 @@ class AbstractMediaDecoder : public nsIO public: // Returns the monitor for other threads to synchronise access to // state. virtual ReentrantMonitor& GetReentrantMonitor() = 0; // Returns true if the decoder is shut down. virtual bool IsShutdown() const = 0; + // A special version of the above for the ogg decoder that is allowed to be + // called cross-thread. + virtual bool IsOggDecoderShutdown() { return false; } + virtual bool OnStateMachineTaskQueue() const = 0; virtual bool OnDecodeTaskQueue() const = 0; // Get the current MediaResource being used. Its URI will be returned // by currentSrc. Returns what was passed to Load(), if Load() has been called. virtual MediaResource* GetResource() const = 0;
--- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -330,16 +330,18 @@ bool MediaDecoder::IsInfinite() { MOZ_ASSERT(NS_IsMainThread()); return mInfiniteStream; } MediaDecoder::MediaDecoder() : mWatchManager(this, AbstractThread::MainThread()), mBuffered(AbstractThread::MainThread(), TimeIntervals(), "MediaDecoder::mBuffered (Mirror)"), + mStateMachineIsShutdown(AbstractThread::MainThread(), true, + "MediaDecoder::mStateMachineIsShutdown (Mirror)"), mNextFrameStatus(AbstractThread::MainThread(), MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED, "MediaDecoder::mNextFrameStatus (Mirror)"), mDormantSupported(false), mDecoderPosition(0), mPlaybackPosition(0), mLogicalPosition(0.0), mCurrentPosition(AbstractThread::MainThread(), 0, "MediaDecoder::mCurrentPosition (Mirror)"), @@ -388,16 +390,19 @@ MediaDecoder::MediaDecoder() : // // Initialize watchers. // // mDuration mWatchManager.Watch(mStateMachineDuration, &MediaDecoder::DurationChanged); + // mStateMachineIsShutdown + mWatchManager.Watch(mStateMachineIsShutdown, &MediaDecoder::ShutdownBitChanged); + // readyState mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateReadyState); mWatchManager.Watch(mNextFrameStatus, &MediaDecoder::UpdateReadyState); // mLogicalPosition mWatchManager.Watch(mCurrentPosition, &MediaDecoder::UpdateLogicalPosition); mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateLogicalPosition); mWatchManager.Watch(mLogicallySeeking, &MediaDecoder::UpdateLogicalPosition); @@ -1249,21 +1254,23 @@ void MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine) { MOZ_ASSERT_IF(aStateMachine, !mDecoderStateMachine); mDecoderStateMachine = aStateMachine; if (mDecoderStateMachine) { mStateMachineDuration.Connect(mDecoderStateMachine->CanonicalDuration()); mBuffered.Connect(mDecoderStateMachine->CanonicalBuffered()); + mStateMachineIsShutdown.Connect(mDecoderStateMachine->CanonicalIsShutdown()); mNextFrameStatus.Connect(mDecoderStateMachine->CanonicalNextFrameStatus()); mCurrentPosition.Connect(mDecoderStateMachine->CanonicalCurrentPosition()); } else { mStateMachineDuration.DisconnectIfConnected(); mBuffered.DisconnectIfConnected(); + mStateMachineIsShutdown.DisconnectIfConnected(); mNextFrameStatus.DisconnectIfConnected(); mCurrentPosition.DisconnectIfConnected(); } } ReentrantMonitor& MediaDecoder::GetReentrantMonitor() { return mReentrantMonitor; } @@ -1331,18 +1338,19 @@ MediaDecoder::NotifyWaitingForResourcesS RefPtr<nsRunnable> task = NS_NewRunnableMethod(mDecoderStateMachine, &MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged); mDecoderStateMachine->TaskQueue()->Dispatch(task.forget()); } } bool MediaDecoder::IsShutdown() const { + MOZ_ASSERT(NS_IsMainThread()); NS_ENSURE_TRUE(GetStateMachine(), true); - return GetStateMachine()->IsShutdown(); + return mStateMachineIsShutdown; } // Drop reference to state machine. Only called during shutdown dance. void MediaDecoder::BreakCycles() { SetStateMachine(nullptr); } MediaDecoderOwner* MediaDecoder::GetMediaOwner() const
--- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -890,16 +890,22 @@ protected: void DurationChanged(); // State-watching manager. WatchManager<MediaDecoder> mWatchManager; // Buffered range, mirrored from the reader. Mirror<media::TimeIntervals> mBuffered; + // Whether the state machine is shut down. + Mirror<bool> mStateMachineIsShutdown; + + // Used by the ogg decoder to watch mStateMachineIsShutdown. + virtual void ShutdownBitChanged() {} + // NextFrameStatus, mirrored from the state machine. Mirror<MediaDecoderOwner::NextFrameStatus> mNextFrameStatus; /****** * The following members should be accessed with the decoder lock held. ******/ // Whether the decoder implementation supports dormant mode.
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -195,16 +195,18 @@ MediaDecoderStateMachine::MediaDecoderSt "MediaDecoderStateMachine::mExplicitDuration (Mirror)"), mObservedDuration(TimeUnit(), "MediaDecoderStateMachine::mObservedDuration"), mPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_LOADING, "MediaDecoderStateMachine::mPlayState (Mirror)"), mNextPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_PAUSED, "MediaDecoderStateMachine::mNextPlayState (Mirror)"), mLogicallySeeking(mTaskQueue, false, "MediaDecoderStateMachine::mLogicallySeeking (Mirror)"), + mIsShutdown(mTaskQueue, false, + "MediaDecoderStateMachine::mIsShutdown (Canonical)"), mNextFrameStatus(mTaskQueue, MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED, "MediaDecoderStateMachine::mNextFrameStatus (Canonical)"), mFragmentEndTime(-1), mReader(aReader), mCurrentPosition(mTaskQueue, 0, "MediaDecoderStateMachine::mCurrentPosition (Canonical)"), mStreamStartTime(0), mAudioStartTime(0), mAudioEndTime(-1), @@ -1372,16 +1374,18 @@ void MediaDecoderStateMachine::SetState( if (mState == aState) { return; } DECODER_LOG("Change machine state from %s to %s", gMachineStateStr[mState], gMachineStateStr[aState]); mState = aState; + mIsShutdown = mState == DECODER_STATE_ERROR || mState == DECODER_STATE_SHUTDOWN; + // Clear state-scoped state. mSentPlaybackEndedEvent = false; } void MediaDecoderStateMachine::VolumeChanged() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); @@ -2461,16 +2465,17 @@ MediaDecoderStateMachine::FinishShutdown mExplicitDuration.DisconnectIfConnected(); mPlayState.DisconnectIfConnected(); mNextPlayState.DisconnectIfConnected(); mLogicallySeeking.DisconnectIfConnected(); mVolume.DisconnectIfConnected(); mLogicalPlaybackRate.DisconnectIfConnected(); mPreservesPitch.DisconnectIfConnected(); mDuration.DisconnectAll(); + mIsShutdown.DisconnectAll(); mNextFrameStatus.DisconnectAll(); mCurrentPosition.DisconnectAll(); // Shut down the watch manager before shutting down our task queue. mWatchManager.Shutdown(); MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN, "How did we escape from the shutdown state?"); @@ -3264,18 +3269,18 @@ void MediaDecoderStateMachine::Preserves if (mAudioSink) { mAudioSink->SetPreservesPitch(mPreservesPitch); } } bool MediaDecoderStateMachine::IsShutdown() { - return mState == DECODER_STATE_ERROR || - mState == DECODER_STATE_SHUTDOWN; + MOZ_ASSERT(OnTaskQueue()); + return mIsShutdown; } void MediaDecoderStateMachine::QueueMetadata(int64_t aPublishTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) { MOZ_ASSERT(OnDecodeTaskQueue()); AssertCurrentThreadInMonitor();
--- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -977,16 +977,22 @@ protected: // The decoder monitor must be held. bool IsLogicallyPlaying() { MOZ_ASSERT(OnTaskQueue()); return mPlayState == MediaDecoder::PLAY_STATE_PLAYING || mNextPlayState == MediaDecoder::PLAY_STATE_PLAYING; } + // Whether we're currently in or transitioning to shutdown state. + Canonical<bool> mIsShutdown; +public: + AbstractCanonical<bool>* CanonicalIsShutdown() { return &mIsShutdown; } +protected: + // The status of our next frame. Mirrored on the main thread and used to // compute ready state. Canonical<NextFrameStatus> mNextFrameStatus; public: AbstractCanonical<NextFrameStatus>* CanonicalNextFrameStatus() { return &mNextFrameStatus; } protected: struct SeekJob {
--- a/dom/media/mediasource/MediaSourceReader.cpp +++ b/dom/media/mediasource/MediaSourceReader.cpp @@ -717,17 +717,18 @@ CreateReaderForType(const nsACString& aT #endif return nullptr; } already_AddRefed<SourceBufferDecoder> MediaSourceReader::CreateSubDecoder(const nsACString& aType, int64_t aTimestampOffset) { - if (IsShutdown()) { + MOZ_ASSERT(NS_IsMainThread()); + if (mDecoder->IsShutdown()) { return nullptr; } // The task queue borrowing is icky. It would be nicer to just give each subreader // its own task queue. Unfortunately though, Request{Audio,Video}Data implementations // currently assert that they're on "the decode thread", and so having // separate task queues makes MediaSource stuff unnecessarily cumbersome. We // should remove the need for these assertions (which probably involves making @@ -832,23 +833,19 @@ nsRefPtr<MediaDecoderReader::SeekPromise MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg which is non-MSE */) { MSE_DEBUG("Seek(aTime=%lld, aEnd=%lld, aCurrent=%lld)", aTime); MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty()); MOZ_DIAGNOSTIC_ASSERT(mAudioPromise.IsEmpty()); MOZ_DIAGNOSTIC_ASSERT(mVideoPromise.IsEmpty()); + MOZ_ASSERT(!mShutdown); nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__); - if (IsShutdown()) { - mSeekPromise.Reject(NS_ERROR_FAILURE, __func__); - return p; - } - // Store pending seek target in case the track buffers don't contain // the desired time and we delay doing the seek. mOriginalSeekTime = aTime; mPendingSeekTime = aTime; { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); mWaitingForSeekData = true;
--- a/dom/media/mediasource/MediaSourceReader.h +++ b/dom/media/mediasource/MediaSourceReader.h @@ -119,22 +119,16 @@ public: void AddTrackBuffer(TrackBuffer* aTrackBuffer); void RemoveTrackBuffer(TrackBuffer* aTrackBuffer); void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo); nsRefPtr<ShutdownPromise> Shutdown() override; virtual void BreakCycles() override; - bool IsShutdown() - { - ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor()); - return mDecoder->IsShutdown(); - } - // Return true if all of the active tracks contain data for the specified time. bool TrackBuffersContainTime(int64_t aTime); // Mark the reader to indicate that EndOfStream has been called on our MediaSource // and that the media element has all the media data. // If called with true, the reader will come out of ended mode. void Ended(bool aEnded); // Return true if reader has all of the media data.
--- a/dom/media/ogg/OggDecoder.h +++ b/dom/media/ogg/OggDecoder.h @@ -8,20 +8,45 @@ #include "MediaDecoder.h" namespace mozilla { class OggDecoder : public MediaDecoder { public: + OggDecoder() + : mShutdownBitMonitor("mShutdownBitMonitor") + , mShutdownBit(false) + {} + virtual MediaDecoder* Clone() { if (!IsOggEnabled()) { return nullptr; } return new OggDecoder(); } virtual MediaDecoderStateMachine* CreateStateMachine(); + + // For yucky legacy reasons, the ogg decoder needs to do a cross-thread read + // to check for shutdown while it hogs its own task queue. We don't want to + // protect the general state with a lock, so we make a special copy and a + // special-purpose lock. This method may be called on any thread. + bool IsOggDecoderShutdown() override + { + MonitorAutoLock lock(mShutdownBitMonitor); + return mShutdownBit; + } + +protected: + void ShutdownBitChanged() override + { + MonitorAutoLock lock(mShutdownBitMonitor); + mShutdownBit = mStateMachineIsShutdown; + } + + Monitor mShutdownBitMonitor; + bool mShutdownBit; }; } // namespace mozilla #endif
--- a/dom/media/ogg/OggReader.cpp +++ b/dom/media/ogg/OggReader.cpp @@ -469,17 +469,17 @@ nsresult OggReader::ReadMetadata(MediaIn SetupTargetSkeleton(mSkeletonState); SetupMediaTracksInfo(serials); if (HasAudio() || HasVideo()) { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); MediaResource* resource = mDecoder->GetResource(); - if (mInfo.mMetadataDuration.isNothing() && !mDecoder->IsShutdown() && + if (mInfo.mMetadataDuration.isNothing() && !mDecoder->IsOggDecoderShutdown() && resource->GetLength() >= 0 && mDecoder->IsMediaSeekable()) { // We didn't get a duration from the index or a Content-Duration header. // Seek to the end of file to find the end time. int64_t length = resource->GetLength(); NS_ASSERTION(length > 0, "Must have a content length to get end time"); @@ -1353,21 +1353,18 @@ nsresult OggReader::SeekInBufferedRange( } // We have an active Theora bitstream. Decode the next Theora frame, and // extract its keyframe's time. bool eof; do { bool skip = false; eof = !DecodeVideoFrame(skip, 0); - { - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - if (mDecoder->IsShutdown()) { - return NS_ERROR_FAILURE; - } + if (mDecoder->IsOggDecoderShutdown()) { + return NS_ERROR_FAILURE; } } while (!eof && mVideoQueue.GetSize() == 0); VideoData* video = mVideoQueue.PeekFront(); if (video && !video->mKeyframe) { // First decoded frame isn't a keyframe, seek back to previous keyframe, // otherwise we'll get visual artifacts. @@ -1505,18 +1502,17 @@ nsresult OggReader::SeekInternal(int64_t while ((v = mVideoQueue.PeekFront()) && !v->mKeyframe) { nsRefPtr<VideoData> releaseMe = mVideoQueue.PopFront(); } if (mVideoQueue.GetSize() == 0) { // We didn't find a keyframe in the frames already here, so decode // forwards until we find a keyframe. bool skip = true; while (DecodeVideoFrame(skip, 0) && skip) { - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - if (mDecoder->IsShutdown()) { + if (mDecoder->IsOggDecoderShutdown()) { return NS_ERROR_FAILURE; } } } #ifdef DEBUG v = mVideoQueue.PeekFront(); if (!v || !v->mKeyframe) { NS_WARNING("Ogg seek didn't end up before a key frame!"); @@ -2002,40 +1998,34 @@ VideoData* OggReader::FindStartTime(int6 return videoData; } AudioData* OggReader::SyncDecodeToFirstAudioData() { bool eof = false; while (!eof && AudioQueue().GetSize() == 0) { - { - ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor()); - if (mDecoder->IsShutdown()) { - return nullptr; - } + if (mDecoder->IsOggDecoderShutdown()) { + return nullptr; } eof = !DecodeAudioData(); } if (eof) { AudioQueue().Finish(); } AudioData* d = nullptr; return (d = AudioQueue().PeekFront()) ? d : nullptr; } VideoData* OggReader::SyncDecodeToFirstVideoData() { bool eof = false; while (!eof && VideoQueue().GetSize() == 0) { - { - ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor()); - if (mDecoder->IsShutdown()) { - return nullptr; - } + if (mDecoder->IsOggDecoderShutdown()) { + return nullptr; } bool keyframeSkip = false; eof = !DecodeVideoFrame(keyframeSkip, 0); } if (eof) { VideoQueue().Finish(); } VideoData* d = nullptr;
--- a/dom/media/ogg/OggReader.h +++ b/dom/media/ogg/OggReader.h @@ -12,16 +12,17 @@ #include <tremor/ivorbiscodec.h> #else #include <vorbis/codec.h> #endif #include "MediaDecoderReader.h" #include "OggCodecState.h" #include "VideoUtils.h" #include "mozilla/Monitor.h" +#include "OggDecoder.h" namespace mozilla { // Thread safe container to store the codec information and the serial for each // streams. class OggCodecStore { public: