Bug 1128420 - remove finished output streams from MediaDecoder::mOutputStreams. r=roc.

This commit is contained in:
JW Wang 2015-02-10 10:45:41 +08:00
Родитель ae23b9318a
Коммит 42b559fb41
2 изменённых файлов: 81 добавлений и 48 удалений

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

@ -350,6 +350,71 @@ MediaDecoder::DecodedStreamGraphListener::NotifyEvent(MediaStreamGraph* aGraph,
}
}
class MediaDecoder::OutputStreamListener : public MediaStreamListener {
public:
OutputStreamListener(MediaDecoder* aDecoder, MediaStream* aStream)
: mDecoder(aDecoder), mStream(aStream) {}
virtual void NotifyEvent(
MediaStreamGraph* aGraph,
MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE {
if (event == EVENT_FINISHED) {
nsRefPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &OutputStreamListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(r.forget());
}
}
void Forget() {
MOZ_ASSERT(NS_IsMainThread());
mDecoder = nullptr;
}
private:
void DoNotifyFinished() {
MOZ_ASSERT(NS_IsMainThread());
if (!mDecoder) {
return;
}
// Remove the finished stream so it won't block the decoded stream.
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
auto& streams = mDecoder->OutputStreams();
// Don't read |mDecoder| in the loop since removing the element will lead
// to ~OutputStreamData() which will call Forget() to reset |mDecoder|.
for (int32_t i = streams.Length() - 1; i >= 0; --i) {
auto& os = streams[i];
MediaStream* p = os.mStream.get();
if (p == mStream.get()) {
if (os.mPort) {
os.mPort->Destroy();
os.mPort = nullptr;
}
streams.RemoveElementAt(i);
break;
}
}
}
// Main thread only
MediaDecoder* mDecoder;
nsRefPtr<MediaStream> mStream;
};
void
MediaDecoder::OutputStreamData::Init(MediaDecoder* aDecoder,
ProcessedMediaStream* aStream)
{
mStream = aStream;
mListener = new OutputStreamListener(aDecoder, aStream);
aStream->AddListener(mListener);
}
MediaDecoder::OutputStreamData::~OutputStreamData()
{
mListener->Forget();
}
void MediaDecoder::DestroyDecodedStream()
{
MOZ_ASSERT(NS_IsMainThread());
@ -366,22 +431,20 @@ void MediaDecoder::DestroyDecodedStream()
// need to be explicitly blocked again.
for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
OutputStreamData& os = mOutputStreams[i];
// Explicitly remove all existing ports.
// This is not strictly necessary but it's good form.
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
os.mPort->Destroy();
os.mPort = nullptr;
// During cycle collection, nsDOMMediaStream can be destroyed and send
// its Destroy message before this decoder is destroyed. So we have to
// be careful not to send any messages after the Destroy().
if (os.mStream->IsDestroyed()) {
// Probably the DOM MediaStream was GCed. Clean up.
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
os.mPort->Destroy();
mOutputStreams.RemoveElementAt(i);
continue;
} else {
os.mStream->ChangeExplicitBlockerCount(1);
}
os.mStream->ChangeExplicitBlockerCount(1);
// Explicitly remove all existing ports. This is not strictly necessary but it's
// good form.
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
os.mPort->Destroy();
os.mPort = nullptr;
}
mDecodedStream = nullptr;
@ -429,12 +492,8 @@ void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
// between main-thread stable states take effect atomically.
for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
OutputStreamData& os = mOutputStreams[i];
if (os.mStream->IsDestroyed()) {
// Probably the DOM MediaStream was GCed. Clean up.
// No need to destroy the port; all ports have been destroyed here.
mOutputStreams.RemoveElementAt(i);
continue;
}
MOZ_ASSERT(!os.mStream->IsDestroyed(),
"Should've been removed in DestroyDecodedStream()");
ConnectDecodedStreamToOutputStream(&os);
}
UpdateStreamBlockingForStateMachinePlaying();
@ -462,7 +521,7 @@ void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
RecreateDecodedStream(t);
}
OutputStreamData* os = mOutputStreams.AppendElement();
os->Init(aStream, aFinishWhenEnded);
os->Init(this, aStream);
ConnectDecodedStreamToOutputStream(os);
if (aFinishWhenEnded) {
// Ensure that aStream finishes the moment mDecodedStream does.
@ -945,32 +1004,6 @@ void MediaDecoder::PlaybackEnded()
return;
}
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
OutputStreamData& os = mOutputStreams[i];
if (os.mStream->IsDestroyed()) {
// Probably the DOM MediaStream was GCed. Clean up.
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
os.mPort->Destroy();
mOutputStreams.RemoveElementAt(i);
continue;
}
if (os.mFinishWhenEnded) {
// Shouldn't really be needed since mDecodedStream should already have
// finished, but doesn't hurt.
os.mStream->Finish();
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
os.mPort->Destroy();
// Not really needed but it keeps the invariant that a stream not
// connected to mDecodedStream is explicity blocked.
os.mStream->ChangeExplicitBlockerCount(1);
mOutputStreams.RemoveElementAt(i);
}
}
}
PlaybackPositionChanged();
ChangeState(PLAY_STATE_ENDED);
InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);

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

@ -479,17 +479,17 @@ public:
bool mStreamFinishedOnMainThread;
};
class OutputStreamListener;
struct OutputStreamData {
void Init(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
{
mStream = aStream;
mFinishWhenEnded = aFinishWhenEnded;
}
void Init(MediaDecoder* aDecoder, ProcessedMediaStream* aStream);
~OutputStreamData();
nsRefPtr<ProcessedMediaStream> mStream;
// mPort connects mDecodedStream->mStream to our mStream.
nsRefPtr<MediaInputPort> mPort;
bool mFinishWhenEnded;
nsRefPtr<OutputStreamListener> mListener;
};
/**
* Connects mDecodedStream->mStream to aStream->mStream.
*/