Bug 804387. Part 1.5: Clean up main-thread MediaStream event listeners. r=jesup

There is no need for these to be independent objects in general and we
don't need to addref/release them. We can just require the caller to
remove them before they die.
We can also save some refcount churn by having
DispatchToMainThreadAfterStreamStateUpdate take already_AddRefed.

--HG--
extra : rebase_source : 751114a1befd73b405dff3ee986496efb6f76201
This commit is contained in:
Robert O'Callahan 2013-02-04 23:04:24 +13:00
Родитель 8f4990d761
Коммит e945b5a3e3
4 изменённых файлов: 36 добавлений и 35 удалений

Просмотреть файл

@ -2450,13 +2450,13 @@ public:
} else { } else {
event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked); event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
} }
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
} }
virtual void NotifyFinished(MediaStreamGraph* aGraph) virtual void NotifyFinished(MediaStreamGraph* aGraph)
{ {
nsCOMPtr<nsIRunnable> event = nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished); NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
} }
virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph, virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph,
bool aHasCurrentData) bool aHasCurrentData)
@ -2471,7 +2471,7 @@ public:
if (aHasCurrentData) { if (aHasCurrentData) {
nsCOMPtr<nsIRunnable> event = nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData); NS_NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
} }
} }
virtual void NotifyOutput(MediaStreamGraph* aGraph) virtual void NotifyOutput(MediaStreamGraph* aGraph)
@ -2482,7 +2482,7 @@ public:
mPendingNotifyOutput = true; mPendingNotifyOutput = true;
nsCOMPtr<nsIRunnable> event = nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &StreamListener::DoNotifyOutput); NS_NewRunnableMethod(this, &StreamListener::DoNotifyOutput);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event); aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
} }
private: private:

Просмотреть файл

@ -157,30 +157,36 @@ void MediaDecoder::ConnectDecodedStreamToOutputStream(OutputStreamData* aStream)
} }
MediaDecoder::DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder, MediaDecoder::DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder,
int64_t aInitialTime, int64_t aInitialTime,
SourceMediaStream* aStream) SourceMediaStream* aStream)
: mLastAudioPacketTime(-1), : mLastAudioPacketTime(-1),
mLastAudioPacketEndTime(-1), mLastAudioPacketEndTime(-1),
mAudioFramesWritten(0), mAudioFramesWritten(0),
mInitialTime(aInitialTime), mInitialTime(aInitialTime),
mNextVideoTime(aInitialTime), mNextVideoTime(aInitialTime),
mDecoder(aDecoder),
mStreamInitialized(false), mStreamInitialized(false),
mHaveSentFinish(false), mHaveSentFinish(false),
mHaveSentFinishAudio(false), mHaveSentFinishAudio(false),
mHaveSentFinishVideo(false), mHaveSentFinishVideo(false),
mStream(aStream), mStream(aStream),
mMainThreadListener(new DecodedStreamMainThreadListener(aDecoder)),
mHaveBlockedForPlayState(false) mHaveBlockedForPlayState(false)
{ {
mStream->AddMainThreadListener(mMainThreadListener); mStream->AddMainThreadListener(this);
} }
MediaDecoder::DecodedStreamData::~DecodedStreamData() MediaDecoder::DecodedStreamData::~DecodedStreamData()
{ {
mStream->RemoveMainThreadListener(mMainThreadListener); mStream->RemoveMainThreadListener(this);
mStream->Destroy(); mStream->Destroy();
} }
void
MediaDecoder::DecodedStreamData::NotifyMainThreadStateChanged()
{
mDecoder->NotifyDecodedStreamMainThreadStateChanged();
}
void MediaDecoder::DestroyDecodedStream() void MediaDecoder::DestroyDecodedStream()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());

Просмотреть файл

@ -235,7 +235,6 @@ class MediaDecoder : public nsIObserver,
{ {
public: public:
typedef mozilla::layers::Image Image; typedef mozilla::layers::Image Image;
class DecodedStreamMainThreadListener;
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER NS_DECL_NSIOBSERVER
@ -340,7 +339,7 @@ public:
// replaying after the input as ended. In the latter case, the new source is // replaying after the input as ended. In the latter case, the new source is
// not connected to streams created by captureStreamUntilEnded. // not connected to streams created by captureStreamUntilEnded.
struct DecodedStreamData { struct DecodedStreamData MOZ_FINAL : public MainThreadMediaStreamListener {
DecodedStreamData(MediaDecoder* aDecoder, DecodedStreamData(MediaDecoder* aDecoder,
int64_t aInitialTime, SourceMediaStream* aStream); int64_t aInitialTime, SourceMediaStream* aStream);
~DecodedStreamData(); ~DecodedStreamData();
@ -358,6 +357,7 @@ public:
// Therefore video packets starting at or after this time need to be copied // Therefore video packets starting at or after this time need to be copied
// to the output stream. // to the output stream.
int64_t mNextVideoTime; // microseconds int64_t mNextVideoTime; // microseconds
MediaDecoder* mDecoder;
// The last video image sent to the stream. Useful if we need to replicate // The last video image sent to the stream. Useful if we need to replicate
// the image. // the image.
nsRefPtr<Image> mLastVideoImage; nsRefPtr<Image> mLastVideoImage;
@ -372,12 +372,11 @@ public:
// The decoder is responsible for calling Destroy() on this stream. // The decoder is responsible for calling Destroy() on this stream.
// Can be read from any thread. // Can be read from any thread.
const nsRefPtr<SourceMediaStream> mStream; const nsRefPtr<SourceMediaStream> mStream;
// A listener object that receives notifications when mStream's
// main-thread-visible state changes. Used on the main thread only.
const nsRefPtr<DecodedStreamMainThreadListener> mMainThreadListener;
// True when we've explicitly blocked this stream because we're // True when we've explicitly blocked this stream because we're
// not in PLAY_STATE_PLAYING. Used on the main thread only. // not in PLAY_STATE_PLAYING. Used on the main thread only.
bool mHaveBlockedForPlayState; bool mHaveBlockedForPlayState;
virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
}; };
struct OutputStreamData { struct OutputStreamData {
void Init(ProcessedMediaStream* aStream, bool aFinishWhenEnded) void Init(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
@ -421,16 +420,6 @@ public:
GetReentrantMonitor().AssertCurrentThreadIn(); GetReentrantMonitor().AssertCurrentThreadIn();
return mDecodedStream; return mDecodedStream;
} }
class DecodedStreamMainThreadListener : public MainThreadMediaStreamListener {
public:
DecodedStreamMainThreadListener(MediaDecoder* aDecoder)
: mDecoder(aDecoder) {}
virtual void NotifyMainThreadStateChanged()
{
mDecoder->NotifyDecodedStreamMainThreadStateChanged();
}
MediaDecoder* mDecoder;
};
// Add an output stream. All decoder output will be sent to the stream. // Add an output stream. All decoder output will be sent to the stream.
// The stream is initially blocked. The decoder is responsible for unblocking // The stream is initially blocked. The decoder is responsible for unblocking

Просмотреть файл

@ -172,19 +172,16 @@ public:
* This callback is invoked on the main thread when the main-thread-visible * This callback is invoked on the main thread when the main-thread-visible
* state of a stream has changed. * state of a stream has changed.
* *
* These methods are called without the media graph monitor held, so * These methods are called with the media graph monitor held, so
* reentry into media graph methods is possible, although very much discouraged! * reentry into general media graph methods is not possible.
* You should do something non-blocking and non-reentrant (e.g. dispatch an * You should do something non-blocking and non-reentrant (e.g. dispatch an
* event) and return. * event) and return. DispatchFromMainThreadAfterNextStreamStateUpdate
* would be a good choice.
* The listener is allowed to synchronously remove itself from the stream, but * The listener is allowed to synchronously remove itself from the stream, but
* not add or remove any other listeners. * not add or remove any other listeners.
*/ */
class MainThreadMediaStreamListener { class MainThreadMediaStreamListener {
public: public:
virtual ~MainThreadMediaStreamListener() {}
NS_INLINE_DECL_REFCOUNTING(MainThreadMediaStreamListener)
virtual void NotifyMainThreadStateChanged() = 0; virtual void NotifyMainThreadStateChanged() = 0;
}; };
@ -274,7 +271,12 @@ public:
, mMainThreadDestroyed(false) , mMainThreadDestroyed(false)
{ {
} }
virtual ~MediaStream() {} virtual ~MediaStream()
{
NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
NS_ASSERTION(mMainThreadListeners.IsEmpty(),
"All main thread listeners should have been removed");
}
/** /**
* Returns the graph that owns this stream. * Returns the graph that owns this stream.
@ -303,11 +305,15 @@ public:
// Events will be dispatched by calling methods of aListener. // Events will be dispatched by calling methods of aListener.
void AddListener(MediaStreamListener* aListener); void AddListener(MediaStreamListener* aListener);
void RemoveListener(MediaStreamListener* aListener); void RemoveListener(MediaStreamListener* aListener);
// Events will be dispatched by calling methods of aListener. It is the
// responsibility of the caller to remove aListener before it is destroyed.
void AddMainThreadListener(MainThreadMediaStreamListener* aListener) void AddMainThreadListener(MainThreadMediaStreamListener* aListener)
{ {
NS_ASSERTION(NS_IsMainThread(), "Call only on main thread"); NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
mMainThreadListeners.AppendElement(aListener); mMainThreadListeners.AppendElement(aListener);
} }
// It's safe to call this even if aListener is not currently a listener;
// the call will be ignored.
void RemoveMainThreadListener(MainThreadMediaStreamListener* aListener) void RemoveMainThreadListener(MainThreadMediaStreamListener* aListener)
{ {
NS_ASSERTION(NS_IsMainThread(), "Call only on main thread"); NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
@ -424,7 +430,7 @@ protected:
// API, minus the number of times it has been explicitly unblocked. // API, minus the number of times it has been explicitly unblocked.
TimeVarying<GraphTime,uint32_t> mExplicitBlockerCount; TimeVarying<GraphTime,uint32_t> mExplicitBlockerCount;
nsTArray<nsRefPtr<MediaStreamListener> > mListeners; nsTArray<nsRefPtr<MediaStreamListener> > mListeners;
nsTArray<nsRefPtr<MainThreadMediaStreamListener> > mMainThreadListeners; nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners;
// Precomputed blocking status (over GraphTime). // Precomputed blocking status (over GraphTime).
// This is only valid between the graph's mCurrentTime and // This is only valid between the graph's mCurrentTime and
@ -838,9 +844,9 @@ public:
* main-thread stream state has been next updated. * main-thread stream state has been next updated.
* Should only be called during MediaStreamListener callbacks. * Should only be called during MediaStreamListener callbacks.
*/ */
void DispatchToMainThreadAfterStreamStateUpdate(nsIRunnable* aRunnable) void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
{ {
mPendingUpdateRunnables.AppendElement(aRunnable); *mPendingUpdateRunnables.AppendElement() = aRunnable;
} }
protected: protected: