Bug 943461. Part 8: When a MediaDecoder is decoding to a stream, run PlaybackEnded when the stream finishes. r=padenot

--HG--
extra : rebase_source : 92c8b55c5d8330bcf8242d379bc608fa3d30bc6b
This commit is contained in:
Robert O'Callahan 2013-12-07 01:01:33 +13:00
Родитель bffdca06e1
Коммит 8db811d2b5
4 изменённых файлов: 50 добавлений и 50 удалений

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

@ -200,29 +200,20 @@ MediaDecoder::DecodedStreamData::DecodedStreamData(MediaDecoder* aDecoder,
mHaveBlockedForPlayState(false), mHaveBlockedForPlayState(false),
mHaveBlockedForStateMachineNotPlaying(false) mHaveBlockedForStateMachineNotPlaying(false)
{ {
mStream->AddMainThreadListener(this); mListener = new DecodedStreamGraphListener(mStream, this);
mListener = new DecodedStreamGraphListener(mStream);
mStream->AddListener(mListener); mStream->AddListener(mListener);
} }
MediaDecoder::DecodedStreamData::~DecodedStreamData() MediaDecoder::DecodedStreamData::~DecodedStreamData()
{ {
mStream->RemoveMainThreadListener(this);
mListener->Forget(); mListener->Forget();
mStream->Destroy(); mStream->Destroy();
} }
void MediaDecoder::DecodedStreamGraphListener::DecodedStreamGraphListener(MediaStream* aStream,
MediaDecoder::DecodedStreamData::NotifyMainThreadStateChanged() DecodedStreamData* aData)
{ : mData(aData),
mDecoder->NotifyDecodedStreamMainThreadStateChanged(); mMutex("MediaDecoder::DecodedStreamData::mMutex"),
if (mStream->IsFinished()) {
mListener->SetFinishedOnMainThread(true);
}
}
MediaDecoder::DecodedStreamGraphListener::DecodedStreamGraphListener(MediaStream* aStream)
: mMutex("MediaDecoder::DecodedStreamData::mMutex"),
mStream(aStream), mStream(aStream),
mLastOutputTime(aStream->GetCurrentTime()), mLastOutputTime(aStream->GetCurrentTime()),
mStreamFinishedOnMainThread(false) mStreamFinishedOnMainThread(false)
@ -239,6 +230,29 @@ MediaDecoder::DecodedStreamGraphListener::NotifyOutput(MediaStreamGraph* aGraph,
} }
} }
void
MediaDecoder::DecodedStreamGraphListener::DoNotifyFinished()
{
if (mData && mData->mDecoder) {
if (mData->mDecoder->GetState() == PLAY_STATE_PLAYING) {
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mData->mDecoder, &MediaDecoder::PlaybackEnded);
NS_DispatchToCurrentThread(event);
}
}
MutexAutoLock lock(mMutex);
mStreamFinishedOnMainThread = true;
}
void
MediaDecoder::DecodedStreamGraphListener::NotifyFinished(MediaStreamGraph* aGraph)
{
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
}
void MediaDecoder::DestroyDecodedStream() void MediaDecoder::DestroyDecodedStream()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@ -326,19 +340,6 @@ void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
} }
} }
void MediaDecoder::NotifyDecodedStreamMainThreadStateChanged()
{
if (mTriggerPlaybackEndedWhenSourceStreamFinishes && mDecodedStream &&
mDecodedStream->mStream->IsFinished()) {
mTriggerPlaybackEndedWhenSourceStreamFinishes = false;
if (GetState() == PLAY_STATE_PLAYING) {
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &MediaDecoder::PlaybackEnded);
NS_DispatchToCurrentThread(event);
}
}
}
void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream, void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
bool aFinishWhenEnded) bool aFinishWhenEnded)
{ {
@ -421,7 +422,6 @@ MediaDecoder::MediaDecoder() :
mCalledResourceLoaded(false), mCalledResourceLoaded(false),
mIgnoreProgressData(false), mIgnoreProgressData(false),
mInfiniteStream(false), mInfiniteStream(false),
mTriggerPlaybackEndedWhenSourceStreamFinishes(false),
mOwner(nullptr), mOwner(nullptr),
mFrameBufferLength(0), mFrameBufferLength(0),
mPinnedForSeek(false), mPinnedForSeek(false),
@ -936,12 +936,6 @@ void MediaDecoder::PlaybackEnded()
{ {
ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mDecodedStream && !mDecodedStream->mStream->IsFinished()) {
// Wait for it to finish before firing PlaybackEnded()
mTriggerPlaybackEndedWhenSourceStreamFinishes = true;
return;
}
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()) { if (os.mStream->IsDestroyed()) {

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

@ -350,13 +350,11 @@ 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 MOZ_FINAL : public MainThreadMediaStreamListener { struct DecodedStreamData {
DecodedStreamData(MediaDecoder* aDecoder, DecodedStreamData(MediaDecoder* aDecoder,
int64_t aInitialTime, SourceMediaStream* aStream); int64_t aInitialTime, SourceMediaStream* aStream);
~DecodedStreamData(); ~DecodedStreamData();
virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
StreamTime GetLastOutputTime() { return mListener->GetLastOutputTime(); } StreamTime GetLastOutputTime() { return mListener->GetLastOutputTime(); }
bool IsFinished() { return mListener->IsFinishedOnMainThread(); } bool IsFinished() { return mListener->IsFinishedOnMainThread(); }
@ -400,8 +398,11 @@ public:
class DecodedStreamGraphListener : public MediaStreamListener { class DecodedStreamGraphListener : public MediaStreamListener {
public: public:
DecodedStreamGraphListener(MediaStream* aStream); DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData);
virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) MOZ_OVERRIDE; virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) MOZ_OVERRIDE;
virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
void DoNotifyFinished();
StreamTime GetLastOutputTime() StreamTime GetLastOutputTime()
{ {
@ -410,13 +411,18 @@ public:
} }
void Forget() void Forget()
{ {
NS_ASSERTION(NS_IsMainThread(), "Main thread only");
mData = nullptr;
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
mStream = nullptr; mStream = nullptr;
} }
void SetFinishedOnMainThread(bool aFinished) bool SetFinishedOnMainThread(bool aFinished)
{ {
MutexAutoLock lock(mMutex); MutexAutoLock lock(mMutex);
bool result = !mStreamFinishedOnMainThread;
mStreamFinishedOnMainThread = aFinished; mStreamFinishedOnMainThread = aFinished;
return result;
} }
bool IsFinishedOnMainThread() bool IsFinishedOnMainThread()
{ {
@ -424,6 +430,9 @@ public:
return mStreamFinishedOnMainThread; return mStreamFinishedOnMainThread;
} }
private: private:
// Main thread only
DecodedStreamData* mData;
Mutex mMutex; Mutex mMutex;
// Protected by mMutex // Protected by mMutex
nsRefPtr<MediaStream> mStream; nsRefPtr<MediaStream> mStream;
@ -465,12 +474,6 @@ public:
* Decoder monitor must be held. * Decoder monitor must be held.
*/ */
void UpdateStreamBlockingForStateMachinePlaying(); void UpdateStreamBlockingForStateMachinePlaying();
/**
* Called when the state of mDecodedStream as visible on the main thread
* has changed. In particular we want to know when the stream has finished
* so we can call PlaybackEnded.
*/
void NotifyDecodedStreamMainThreadStateChanged();
nsTArray<OutputStreamData>& OutputStreams() nsTArray<OutputStreamData>& OutputStreams()
{ {
GetReentrantMonitor().AssertCurrentThreadIn(); GetReentrantMonitor().AssertCurrentThreadIn();
@ -1134,10 +1137,6 @@ protected:
// True if the stream is infinite (e.g. a webradio). // True if the stream is infinite (e.g. a webradio).
bool mInfiniteStream; bool mInfiniteStream;
// True if NotifyDecodedStreamMainThreadStateChanged should retrigger
// PlaybackEnded when mDecodedStream->mStream finishes.
bool mTriggerPlaybackEndedWhenSourceStreamFinishes;
// Start timer to update download progress information. // Start timer to update download progress information.
nsresult StartProgress(); nsresult StartProgress();

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

@ -2369,10 +2369,14 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
} }
StopAudioThread(); StopAudioThread();
if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING) { // When we're decoding to a stream, the stream's main-thread finish signal
// will take care of calling MediaDecoder::PlaybackEnded.
if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
!mDecoder->GetDecodedStream()) {
int64_t videoTime = HasVideo() ? mVideoFrameEndTime : 0; int64_t videoTime = HasVideo() ? mVideoFrameEndTime : 0;
int64_t clockTime = std::max(mEndTime, std::max(videoTime, GetAudioClock())); int64_t clockTime = std::max(mEndTime, std::max(videoTime, GetAudioClock()));
UpdatePlaybackPosition(clockTime); UpdatePlaybackPosition(clockTime);
nsCOMPtr<nsIRunnable> event = nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded); NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);

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

@ -52,6 +52,9 @@ public:
for (uint32_t i = 0; i < mInputs.Length(); ++i) { for (uint32_t i = 0; i < mInputs.Length(); ++i) {
MediaStream* stream = mInputs[i]->GetSource(); MediaStream* stream = mInputs[i]->GetSource();
if (!stream->IsFinishedOnGraphThread()) { if (!stream->IsFinishedOnGraphThread()) {
// XXX we really should check whether 'stream' has finished within time aTo,
// not just that it's finishing when all its queued data eventually runs
// out.
allFinished = false; allFinished = false;
} }
if (!stream->HasCurrentData()) { if (!stream->HasCurrentData()) {