Bug 1191696. Part 1 - Handle the promise returned by DecodedStream::StartPlayback in MDSM. r=roc.

This commit is contained in:
JW Wang 2015-08-17 07:52:28 +08:00
parent 48135fb02b
commit 6afdd97a95
4 changed files with 91 additions and 32 deletions

View File

@ -4,13 +4,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DecodedStream.h"
#include "MediaStreamGraph.h"
#include "AudioSegment.h"
#include "VideoSegment.h"
#include "MediaQueue.h"
#include "DecodedStream.h"
#include "MediaData.h"
#include "MediaQueue.h"
#include "MediaStreamGraph.h"
#include "SharedBuffer.h"
#include "VideoSegment.h"
#include "VideoUtils.h"
namespace mozilla {
@ -244,14 +244,15 @@ DecodedStream::~DecodedStream()
{
}
void
nsRefPtr<GenericPromise>
DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mStartTime.isNothing()) {
mStartTime.emplace(aStartTime);
mInfo = aInfo;
}
MOZ_ASSERT(mStartTime.isNothing(), "playback already started.");
mStartTime.emplace(aStartTime);
mInfo = aInfo;
// TODO: fix me in next patches.
return nullptr;
}
void DecodedStream::StopPlayback()
@ -680,12 +681,17 @@ DecodedStream::AdvanceTracks()
}
}
bool
void
DecodedStream::SendData()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()");
// Nothing to do when the stream is finished.
if (mData->mHaveSentFinish) {
return;
}
InitTracks();
SendAudio(mVolume, mSameOrigin);
SendVideo(mSameOrigin);
@ -698,8 +704,6 @@ DecodedStream::SendData()
mData->mHaveSentFinish = true;
mData->mStream->Finish();
}
return finished;
}
int64_t

View File

@ -12,6 +12,7 @@
#include "mozilla/CheckedInt.h"
#include "mozilla/Maybe.h"
#include "mozilla/MozPromise.h"
#include "mozilla/nsRefPtr.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/UniquePtr.h"
@ -53,7 +54,11 @@ public:
// Mimic MDSM::StartAudioThread.
// Must be called before any calls to SendData().
void StartPlayback(int64_t aStartTime, const MediaInfo& aInfo);
//
// Return a promise which will be resolved when the stream is finished
// or rejected if any error.
nsRefPtr<GenericPromise> StartPlayback(int64_t aStartTime,
const MediaInfo& aInfo);
// Mimic MDSM::StopAudioThread.
void StopPlayback();
@ -71,8 +76,7 @@ public:
bool IsFinished() const;
bool HasConsumers() const;
// Return true if stream is finished.
bool SendData();
void SendData();
protected:
virtual ~DecodedStream();

View File

@ -376,7 +376,7 @@ void MediaDecoderStateMachine::SendStreamData()
AssertCurrentThreadInMonitor();
MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
bool finished = mDecodedStream->SendData();
mDecodedStream->SendData();
const auto clockTime = GetClock();
while (true) {
@ -392,12 +392,6 @@ void MediaDecoderStateMachine::SendStreamData()
}
break;
}
// To be consistent with AudioSink, |mAudioCompleted| is not set
// until all samples are drained.
if (finished && AudioQueue().GetSize() == 0) {
mAudioCompleted = true;
}
}
bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
@ -1079,12 +1073,7 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
MOZ_ASSERT(IsPlaying());
StartAudioThread();
// Tell DecodedStream to start playback with specified start time and media
// info. This is consistent with how we create AudioSink in StartAudioThread().
if (mAudioCaptured) {
mDecodedStream->StartPlayback(GetMediaTime(), mInfo);
}
StartDecodedStream();
DispatchDecodeTasksIfNeeded();
}
@ -1792,6 +1781,32 @@ MediaDecoderStateMachine::StartAudioThread()
}
}
void
MediaDecoderStateMachine::StopDecodedStream()
{
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
mDecodedStream->StopPlayback();
mDecodedStreamPromise.DisconnectIfExists();
}
void
MediaDecoderStateMachine::StartDecodedStream()
{
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
// Tell DecodedStream to start playback with specified start time and media
// info. This is consistent with how we create AudioSink in StartAudioThread().
if (mAudioCaptured && !mDecodedStreamPromise.Exists()) {
mDecodedStreamPromise.Begin(
mDecodedStream->StartPlayback(GetMediaTime(), mInfo)->Then(
OwnerThread(), __func__, this,
&MediaDecoderStateMachine::OnDecodedStreamFinish,
&MediaDecoderStateMachine::OnDecodedStreamError));
}
}
int64_t MediaDecoderStateMachine::AudioDecodedUsecs()
{
MOZ_ASSERT(OnTaskQueue());
@ -2395,7 +2410,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
// Stop audio sink after call to AudioEndTime() above, otherwise it will
// return an incorrect value due to a null mAudioSink.
StopAudioThread();
mDecodedStream->StopPlayback();
StopDecodedStream();
}
return NS_OK;
@ -2425,7 +2440,7 @@ MediaDecoderStateMachine::Reset()
// outside of the decoder monitor while we are clearing the queue and causes
// crash for no samples to be popped.
StopAudioThread();
mDecodedStream->StopPlayback();
StopDecodedStream();
mVideoFrameEndTime = -1;
mDecodedVideoEndTime = -1;
@ -3076,6 +3091,32 @@ void MediaDecoderStateMachine::OnAudioSinkError()
DecodeError();
}
void
MediaDecoderStateMachine::OnDecodedStreamFinish()
{
MOZ_ASSERT(OnTaskQueue());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MOZ_ASSERT(mAudioCaptured, "Audio should be captured.");
mDecodedStreamPromise.Complete();
if (mInfo.HasAudio()) {
mAudioCompleted = true;
}
// To notify PlaybackEnded as soon as possible.
ScheduleStateMachine();
}
void
MediaDecoderStateMachine::OnDecodedStreamError()
{
MOZ_ASSERT(OnTaskQueue());
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MOZ_ASSERT(mAudioCaptured, "Audio should be captured.");
mDecodedStreamPromise.Complete();
DecodeError();
}
uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const
{
MOZ_ASSERT(OnTaskQueue());
@ -3099,7 +3140,7 @@ void MediaDecoderStateMachine::DispatchAudioCaptured()
// Start DecodedStream if we are already playing. Otherwise it will be
// handled in MaybeStartPlayback().
if (self->IsPlaying()) {
self->mDecodedStream->StartPlayback(self->GetMediaTime(), self->mInfo);
self->StartDecodedStream();
}
self->ScheduleStateMachine();
}
@ -3115,7 +3156,8 @@ void MediaDecoderStateMachine::DispatchAudioUncaptured()
MOZ_ASSERT(self->OnTaskQueue());
ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
if (self->mAudioCaptured) {
// Start again the audio sink
self->StopDecodedStream();
// Start again the audio sink.
self->mAudioCaptured = false;
if (self->IsPlaying()) {
self->StartAudioThread();

View File

@ -518,6 +518,10 @@ protected:
// one lock count. Called on the state machine thread.
void StartAudioThread();
void StopDecodedStream();
void StartDecodedStream();
// Notification method invoked when mPlayState changes.
void PlayStateChanged();
@ -670,6 +674,10 @@ private:
// Rejected by the AudioSink to signal errors.
void OnAudioSinkError();
void OnDecodedStreamFinish();
void OnDecodedStreamError();
// Return true if the video decoder's decode speed can not catch up the
// play time.
bool NeedToSkipToNextKeyframe();
@ -1284,6 +1292,7 @@ private:
nsRefPtr<MediaResource> mResource;
MozPromiseRequestHolder<GenericPromise> mAudioSinkPromise;
MozPromiseRequestHolder<GenericPromise> mDecodedStreamPromise;
MediaEventListener mAudioQueueListener;
MediaEventListener mVideoQueueListener;