M1256520 + M1248314

This commit is contained in:
Raphael Guay 2018-07-13 14:51:28 -04:00
parent 41a7492086
commit 027a6309b0
2 changed files with 70 additions and 116 deletions

View File

@ -6,6 +6,7 @@
#include "mozilla/CheckedInt.h" #include "mozilla/CheckedInt.h"
#include "mozilla/gfx/Point.h" #include "mozilla/gfx/Point.h"
#include "mozilla/SyncRunnable.h"
#include "AudioSegment.h" #include "AudioSegment.h"
#include "DecodedStream.h" #include "DecodedStream.h"
@ -18,6 +19,15 @@
namespace mozilla { namespace mozilla {
/*
* A container class to make it easier to pass the playback info all the
* way to DecodedStreamGraphListener from DecodedStream.
*/
struct PlaybackInfoInit {
int64_t mStartTime;
MediaInfo mInfo;
};
class DecodedStreamGraphListener : public MediaStreamListener { class DecodedStreamGraphListener : public MediaStreamListener {
typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent; typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
public: public:
@ -116,7 +126,8 @@ UpdateStreamSuspended(MediaStream* aStream, bool aBlocking)
*/ */
class DecodedStreamData { class DecodedStreamData {
public: public:
DecodedStreamData(SourceMediaStream* aStream, DecodedStreamData(OutputStreamManager aOutputStreamManager,
PlaybackInfoInit&& aInit,
MozPromiseHolder<GenericPromise>&& aPromise); MozPromiseHolder<GenericPromise>&& aPromise);
~DecodedStreamData(); ~DecodedStreamData();
bool IsFinished() const; bool IsFinished() const;
@ -137,45 +148,56 @@ public:
// the image. // the image.
RefPtr<layers::Image> mLastVideoImage; RefPtr<layers::Image> mLastVideoImage;
gfx::IntSize mLastVideoImageDisplaySize; gfx::IntSize mLastVideoImageDisplaySize;
// This is set to true when the stream is initialized (audio and
// video tracks added).
bool mStreamInitialized;
bool mHaveSentFinish; bool mHaveSentFinish;
bool mHaveSentFinishAudio; bool mHaveSentFinishAudio;
bool mHaveSentFinishVideo; bool mHaveSentFinishVideo;
// The decoder is responsible for calling Destroy() on this stream. // The decoder is responsible for calling Destroy() on this stream.
const RefPtr<SourceMediaStream> mStream; const RefPtr<SourceMediaStream> mStream;
RefPtr<DecodedStreamGraphListener> mListener; const RefPtr<DecodedStreamGraphListener> mListener;
bool mPlaying; bool mPlaying;
// True if we need to send a compensation video frame to ensure the // True if we need to send a compensation video frame to ensure the
// StreamTime going forward. // StreamTime going forward.
bool mEOSVideoCompensation; bool mEOSVideoCompensation;
OutputStreamManager mOutputStreamManager;
}; };
DecodedStreamData::DecodedStreamData(SourceMediaStream* aStream, DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager,
PlaybackInfoInit&& aInit,
MozPromiseHolder<GenericPromise>&& aPromise) MozPromiseHolder<GenericPromise>&& aPromise)
: mAudioFramesWritten(0) : mAudioFramesWritten(0)
, mNextVideoTime(-1) , mNextVideoTime(aInit.mStartTime)
, mNextAudioTime(-1) , mNextAudioTime(aInit.mStartTime)
, mStreamInitialized(false)
, mHaveSentFinish(false) , mHaveSentFinish(false)
, mHaveSentFinishAudio(false) , mHaveSentFinishAudio(false)
, mHaveSentFinishVideo(false) , mHaveSentFinishVideo(false)
, mStream(aStream) , mStream(aOutputStreamManager.Graph()->CreateSourceStream(nullptr))
, mPlaying(true)
, mEOSVideoCompensation(false)
{
// DecodedStreamGraphListener will resolve this promise. // DecodedStreamGraphListener will resolve this promise.
mListener = new DecodedStreamGraphListener(mStream, Move(aPromise)); , mListener(new DecodedStreamGraphListener(mStream, Move(aPromise)))
mStream->AddListener(mListener);
// mPlaying is initially true because MDSM won't start playback until playing // mPlaying is initially true because MDSM won't start playback until playing
// becomes true. This is consistent with the settings of AudioSink. // becomes true. This is consistent with the settings of AudioSink.
, mPlaying(true)
, mEOSVideoCompensation(false)
, mOutputStreamManager(aOutputStreamManager)
{
mStream->AddListener(mListener);
mOutputStreamManager.Connect(mStream);
// Initialize tracks.
if (aInit.mInfo.HasAudio()) {
mStream->AddAudioTrack(aInit.mInfo.mAudio.mTrackId,
aInit.mInfo.mAudio.mRate,
0, new AudioSegment());
}
if (aInit.mInfo.HasVideo()) {
mStream->AddTrack(aInit.mInfo.mVideo.mTrackId, 0, new VideoSegment());
}
} }
DecodedStreamData::~DecodedStreamData() DecodedStreamData::~DecodedStreamData()
{ {
mOutputStreamManager.Disconnect();
mListener->Forget(); mListener->Forget();
mStream->Destroy(); mStream->Destroy();
} }
@ -377,28 +399,51 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo)
class R : public nsRunnable { class R : public nsRunnable {
typedef MozPromiseHolder<GenericPromise> Promise; typedef MozPromiseHolder<GenericPromise> Promise;
typedef void(DecodedStream::*Method)(Promise&&);
public: public:
R(DecodedStream* aThis, Method aMethod, Promise&& aPromise) R(PlaybackInfoInit&& aInit, Promise&& aPromise, OutputStreamManager aManager)
: mThis(aThis), mMethod(aMethod) : mInit(Move(aInit)), mOutputStreamManager(aManager)
{ {
mPromise = Move(aPromise); mPromise = Move(aPromise);
} }
NS_IMETHOD Run() override NS_IMETHOD Run() override
{ {
(mThis->*mMethod)(Move(mPromise)); MOZ_ASSERT(NS_IsMainThread());
// No need to create a source stream when there are no output streams. This
// happens when RemoveOutput() is called immediately after StartPlayback().
if (!mOutputStreamManager.Graph()) {
// Resolve the promise to indicate the end of playback.
mPromise.Resolve(true, __func__);
return NS_OK;
}
mData = MakeUnique<DecodedStreamData>(
mOutputStreamManager, Move(mInit), Move(mPromise));
return NS_OK; return NS_OK;
} }
UniquePtr<DecodedStreamData> ReleaseData()
{
return Move(mData);
}
private: private:
RefPtr<DecodedStream> mThis; PlaybackInfoInit mInit;
Method mMethod;
Promise mPromise; Promise mPromise;
OutputStreamManager mOutputStreamManager;
UniquePtr<DecodedStreamData> mData;
}; };
MozPromiseHolder<GenericPromise> promise; MozPromiseHolder<GenericPromise> promise;
mFinishPromise = promise.Ensure(__func__); mFinishPromise = promise.Ensure(__func__);
nsCOMPtr<nsIRunnable> r = new R(this, &DecodedStream::CreateData, Move(promise)); PlaybackInfoInit init {
AbstractThread::MainThread()->Dispatch(r.forget()); aStartTime, aInfo
};
nsCOMPtr<nsIRunnable> r = new R(Move(init), Move(promise), mOutputStreamManager);
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
SyncRunnable::DispatchToThread(mainThread, r);
mData = static_cast<R*>(r.get())->ReleaseData();
if (mData) {
mData->SetPlaying(mPlaying);
SendData();
}
} }
void void
@ -440,54 +485,13 @@ DecodedStream::DestroyData(UniquePtr<DecodedStreamData> aData)
} }
DecodedStreamData* data = aData.release(); DecodedStreamData* data = aData.release();
RefPtr<DecodedStream> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () { nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
self->mOutputStreamManager.Disconnect();
delete data; delete data;
}); });
AbstractThread::MainThread()->Dispatch(r.forget()); AbstractThread::MainThread()->Dispatch(r.forget());
} }
void
DecodedStream::CreateData(MozPromiseHolder<GenericPromise>&& aPromise)
{
MOZ_ASSERT(NS_IsMainThread());
// No need to create a source stream when there are no output streams. This
// happens when RemoveOutput() is called immediately after StartPlayback().
// Also we don't create a source stream when MDSM has begun shutdown.
if (!mOutputStreamManager.Graph() || mShuttingDown) {
// Resolve the promise to indicate the end of playback.
aPromise.Resolve(true, __func__);
return;
}
auto source = mOutputStreamManager.Graph()->CreateSourceStream(nullptr);
auto data = new DecodedStreamData(source, Move(aPromise));
mOutputStreamManager.Connect(data->mStream);
class R : public nsRunnable {
typedef void(DecodedStream::*Method)(UniquePtr<DecodedStreamData>);
public:
R(DecodedStream* aThis, Method aMethod, DecodedStreamData* aData)
: mThis(aThis), mMethod(aMethod), mData(aData) {}
NS_IMETHOD Run() override
{
(mThis->*mMethod)(Move(mData));
return NS_OK;
}
private:
RefPtr<DecodedStream> mThis;
Method mMethod;
UniquePtr<DecodedStreamData> mData;
};
// Post a message to ensure |mData| is only updated on the worker thread.
// Note this must be done before MDSM's shutdown since dispatch could fail
// when the worker thread is shut down.
nsCOMPtr<nsIRunnable> r = new R(this, &DecodedStream::OnDataCreated, data);
mOwnerThread->Dispatch(r.forget());
}
bool bool
DecodedStream::HasConsumers() const DecodedStream::HasConsumers() const
@ -495,23 +499,7 @@ DecodedStream::HasConsumers() const
return !mOutputStreamManager.IsEmpty(); return !mOutputStreamManager.IsEmpty();
} }
void
DecodedStream::OnDataCreated(UniquePtr<DecodedStreamData> aData)
{
AssertOwnerThread();
MOZ_ASSERT(!mData, "Already created.");
// Start to send data to the stream immediately
if (mStartTime.isSome()) {
aData->SetPlaying(mPlaying);
mData = Move(aData);
SendData();
return;
}
// Playback has ended. Destroy aData which is not needed anymore.
DestroyData(Move(aData));
}
void void
DecodedStream::AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded) DecodedStream::AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
@ -569,37 +557,6 @@ DecodedStream::SetSameOrigin(bool aSameOrigin)
mSameOrigin = aSameOrigin; mSameOrigin = aSameOrigin;
} }
void
DecodedStream::InitTracks()
{
AssertOwnerThread();
if (mData->mStreamInitialized) {
return;
}
SourceMediaStream* sourceStream = mData->mStream;
if (mInfo.HasAudio()) {
TrackID audioTrackId = mInfo.mAudio.mTrackId;
AudioSegment* audio = new AudioSegment();
sourceStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio,
SourceMediaStream::ADDTRACK_QUEUED);
mData->mNextAudioTime = mStartTime.ref();
}
if (mInfo.HasVideo()) {
TrackID videoTrackId = mInfo.mVideo.mTrackId;
VideoSegment* video = new VideoSegment();
sourceStream->AddTrack(videoTrackId, 0, video,
SourceMediaStream::ADDTRACK_QUEUED);
mData->mNextVideoTime = mStartTime.ref();
}
sourceStream->FinishAddTracks();
mData->mStreamInitialized = true;
}
static void static void
SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime, SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime,
MediaData* aData, AudioSegment* aOutput, MediaData* aData, AudioSegment* aOutput,
@ -832,7 +789,6 @@ DecodedStream::SendData()
return; return;
} }
InitTracks();
SendAudio(mParams.mVolume, mSameOrigin); SendAudio(mParams.mVolume, mSameOrigin);
SendVideo(mSameOrigin); SendVideo(mSameOrigin);
AdvanceTracks(); AdvanceTracks();

View File

@ -27,6 +27,7 @@ class MediaInputPort;
class MediaStream; class MediaStream;
class MediaStreamGraph; class MediaStreamGraph;
class OutputStreamManager; class OutputStreamManager;
struct PlaybackInfoInit;
class ProcessedMediaStream; class ProcessedMediaStream;
class TimeStamp; class TimeStamp;
@ -131,10 +132,7 @@ protected:
virtual ~DecodedStream(); virtual ~DecodedStream();
private: private:
void CreateData(MozPromiseHolder<GenericPromise>&& aPromise);
void DestroyData(UniquePtr<DecodedStreamData> aData); void DestroyData(UniquePtr<DecodedStreamData> aData);
void OnDataCreated(UniquePtr<DecodedStreamData> aData);
void InitTracks();
void AdvanceTracks(); void AdvanceTracks();
void SendAudio(double aVolume, bool aIsSameOrigin); void SendAudio(double aVolume, bool aIsSameOrigin);
void SendVideo(bool aIsSameOrigin); void SendVideo(bool aIsSameOrigin);