зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
bffdca06e1
Коммит
8db811d2b5
|
@ -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()) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче