зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1128420 - remove finished output streams from MediaDecoder::mOutputStreams. r=roc.
This commit is contained in:
Родитель
ae23b9318a
Коммит
42b559fb41
|
@ -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()
|
void MediaDecoder::DestroyDecodedStream()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
@ -366,22 +431,20 @@ void MediaDecoder::DestroyDecodedStream()
|
||||||
// need to be explicitly blocked again.
|
// need to be explicitly blocked again.
|
||||||
for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
|
for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
|
||||||
OutputStreamData& os = mOutputStreams[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
|
// During cycle collection, nsDOMMediaStream can be destroyed and send
|
||||||
// its Destroy message before this decoder is destroyed. So we have to
|
// its Destroy message before this decoder is destroyed. So we have to
|
||||||
// be careful not to send any messages after the Destroy().
|
// be careful not to send any messages after the Destroy().
|
||||||
if (os.mStream->IsDestroyed()) {
|
if (os.mStream->IsDestroyed()) {
|
||||||
// Probably the DOM MediaStream was GCed. Clean up.
|
// Probably the DOM MediaStream was GCed. Clean up.
|
||||||
MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
|
|
||||||
os.mPort->Destroy();
|
|
||||||
mOutputStreams.RemoveElementAt(i);
|
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;
|
mDecodedStream = nullptr;
|
||||||
|
@ -429,12 +492,8 @@ void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
|
||||||
// between main-thread stable states take effect atomically.
|
// between main-thread stable states take effect atomically.
|
||||||
for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
|
for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
|
||||||
OutputStreamData& os = mOutputStreams[i];
|
OutputStreamData& os = mOutputStreams[i];
|
||||||
if (os.mStream->IsDestroyed()) {
|
MOZ_ASSERT(!os.mStream->IsDestroyed(),
|
||||||
// Probably the DOM MediaStream was GCed. Clean up.
|
"Should've been removed in DestroyDecodedStream()");
|
||||||
// No need to destroy the port; all ports have been destroyed here.
|
|
||||||
mOutputStreams.RemoveElementAt(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ConnectDecodedStreamToOutputStream(&os);
|
ConnectDecodedStreamToOutputStream(&os);
|
||||||
}
|
}
|
||||||
UpdateStreamBlockingForStateMachinePlaying();
|
UpdateStreamBlockingForStateMachinePlaying();
|
||||||
|
@ -462,7 +521,7 @@ void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
|
||||||
RecreateDecodedStream(t);
|
RecreateDecodedStream(t);
|
||||||
}
|
}
|
||||||
OutputStreamData* os = mOutputStreams.AppendElement();
|
OutputStreamData* os = mOutputStreams.AppendElement();
|
||||||
os->Init(aStream, aFinishWhenEnded);
|
os->Init(this, aStream);
|
||||||
ConnectDecodedStreamToOutputStream(os);
|
ConnectDecodedStreamToOutputStream(os);
|
||||||
if (aFinishWhenEnded) {
|
if (aFinishWhenEnded) {
|
||||||
// Ensure that aStream finishes the moment mDecodedStream does.
|
// Ensure that aStream finishes the moment mDecodedStream does.
|
||||||
|
@ -945,32 +1004,6 @@ void MediaDecoder::PlaybackEnded()
|
||||||
return;
|
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();
|
PlaybackPositionChanged();
|
||||||
ChangeState(PLAY_STATE_ENDED);
|
ChangeState(PLAY_STATE_ENDED);
|
||||||
InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);
|
InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);
|
||||||
|
|
|
@ -479,17 +479,17 @@ public:
|
||||||
bool mStreamFinishedOnMainThread;
|
bool mStreamFinishedOnMainThread;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OutputStreamListener;
|
||||||
|
|
||||||
struct OutputStreamData {
|
struct OutputStreamData {
|
||||||
void Init(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
|
void Init(MediaDecoder* aDecoder, ProcessedMediaStream* aStream);
|
||||||
{
|
~OutputStreamData();
|
||||||
mStream = aStream;
|
|
||||||
mFinishWhenEnded = aFinishWhenEnded;
|
|
||||||
}
|
|
||||||
nsRefPtr<ProcessedMediaStream> mStream;
|
nsRefPtr<ProcessedMediaStream> mStream;
|
||||||
// mPort connects mDecodedStream->mStream to our mStream.
|
// mPort connects mDecodedStream->mStream to our mStream.
|
||||||
nsRefPtr<MediaInputPort> mPort;
|
nsRefPtr<MediaInputPort> mPort;
|
||||||
bool mFinishWhenEnded;
|
nsRefPtr<OutputStreamListener> mListener;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects mDecodedStream->mStream to aStream->mStream.
|
* Connects mDecodedStream->mStream to aStream->mStream.
|
||||||
*/
|
*/
|
||||||
|
|
Загрузка…
Ссылка в новой задаче