Fix regression from #511 (#515)

* M1233650 Part 1

* M1233650 Part 2+3

* M1233650 Part 4
This commit is contained in:
NapalmSauce 2018-07-17 01:27:37 +00:00 committed by Cameron Kaiser
parent 2f6fe15d30
commit 1ec3662359
7 changed files with 234 additions and 210 deletions

View File

@ -19,6 +19,7 @@
#include "mediasink/AudioSinkWrapper.h"
#include "mediasink/VideoSink.h"
#include "mediasink/DecodedStream.h"
#include "mediasink/OutputStreamManager.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Logging.h"
#include "mozilla/mozalloc.h"
@ -288,7 +289,9 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mSentLoadedMetadataEvent(false),
mSentFirstFrameLoadedEvent(false, "MediaDecoderStateMachine::mSentFirstFrameLoadedEvent"),
mSentPlaybackEndedEvent(false),
mStreamSink(new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue)),
mOutputStreamManager(new OutputStreamManager()),
mStreamSink(new DecodedStream(
mTaskQueue, mAudioQueue, mVideoQueue, mOutputStreamManager)),
mResource(aDecoder->GetResource()),
mAudioOffloading(false),
mBuffered(mTaskQueue, TimeIntervals(),
@ -3141,7 +3144,7 @@ void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
{
MOZ_ASSERT(NS_IsMainThread());
DECODER_LOG("AddOutputStream aStream=%p!", aStream);
mStreamSink->AddOutput(aStream, aFinishWhenEnded);
mOutputStreamManager->Add(aStream, aFinishWhenEnded);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
this, &MediaDecoderStateMachine::SetAudioCaptured, true);
OwnerThread()->Dispatch(r.forget());
@ -3151,8 +3154,8 @@ void MediaDecoderStateMachine::RemoveOutputStream(MediaStream* aStream)
{
MOZ_ASSERT(NS_IsMainThread());
DECODER_LOG("RemoveOutputStream=%p!", aStream);
mStreamSink->RemoveOutput(aStream);
if (!mStreamSink->HasConsumers()) {
mOutputStreamManager->Remove(aStream);
if (mOutputStreamManager->IsEmpty()) {
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
this, &MediaDecoderStateMachine::SetAudioCaptured, false);
OwnerThread()->Dispatch(r.forget());

View File

@ -105,6 +105,7 @@ class MediaSink;
class AudioSegment;
class DecodedStream;
class OutputStreamManager;
class TaskQueue;
extern LazyLogModule gMediaDecoderLog;
@ -1185,6 +1186,9 @@ private:
bool mSentPlaybackEndedEvent;
// Data about MediaStreams that are being fed by the decoder.
const RefPtr<OutputStreamManager> mOutputStreamManager;
// The SourceMediaStream we are using to feed the mOutputStreams. This stream
// is never exposed outside the decoder.
// Only written on the main thread while holding the monitor. Therefore it

View File

@ -13,6 +13,7 @@
#include "MediaData.h"
#include "MediaQueue.h"
#include "MediaStreamGraph.h"
#include "OutputStreamManager.h"
#include "SharedBuffer.h"
#include "VideoSegment.h"
#include "VideoUtils.h"
@ -126,7 +127,7 @@ UpdateStreamSuspended(MediaStream* aStream, bool aBlocking)
*/
class DecodedStreamData {
public:
DecodedStreamData(OutputStreamManager aOutputStreamManager,
DecodedStreamData(OutputStreamManager* aOutputStreamManager,
PlaybackInfoInit&& aInit,
MozPromiseHolder<GenericPromise>&& aPromise);
~DecodedStreamData();
@ -160,10 +161,10 @@ public:
// StreamTime going forward.
bool mEOSVideoCompensation;
OutputStreamManager mOutputStreamManager;
const RefPtr<OutputStreamManager> mOutputStreamManager;
};
DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager,
DecodedStreamData::DecodedStreamData(OutputStreamManager* aOutputStreamManager,
PlaybackInfoInit&& aInit,
MozPromiseHolder<GenericPromise>&& aPromise)
: mAudioFramesWritten(0)
@ -172,7 +173,7 @@ DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager,
, mHaveSentFinish(false)
, mHaveSentFinishAudio(false)
, mHaveSentFinishVideo(false)
, mStream(aOutputStreamManager.Graph()->CreateSourceStream(nullptr))
, mStream(aOutputStreamManager->Graph()->CreateSourceStream(nullptr))
// DecodedStreamGraphListener will resolve this promise.
, mListener(new DecodedStreamGraphListener(mStream, Move(aPromise)))
// mPlaying is initially true because MDSM won't start playback until playing
@ -182,7 +183,7 @@ DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager,
, mOutputStreamManager(aOutputStreamManager)
{
mStream->AddListener(mListener);
mOutputStreamManager.Connect(mStream);
mOutputStreamManager->Connect(mStream);
// Initialize tracks.
if (aInit.mInfo.HasAudio()) {
@ -197,7 +198,7 @@ DecodedStreamData::DecodedStreamData(OutputStreamManager aOutputStreamManager,
DecodedStreamData::~DecodedStreamData()
{
mOutputStreamManager.Disconnect();
mOutputStreamManager->Disconnect();
mListener->Forget();
mStream->Destroy();
}
@ -223,119 +224,12 @@ DecodedStreamData::SetPlaying(bool aPlaying)
}
}
OutputStreamData::~OutputStreamData()
{
MOZ_ASSERT(NS_IsMainThread());
// Break the connection to the input stream if necessary.
if (mPort) {
mPort->Destroy();
}
}
void
OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream)
{
mOwner = aOwner;
mStream = aStream;
}
void
OutputStreamData::Connect(MediaStream* aStream)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mPort, "Already connected?");
MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream.");
mPort = mStream->AllocateInputPort(aStream);
}
bool
OutputStreamData::Disconnect()
{
MOZ_ASSERT(NS_IsMainThread());
// During cycle collection, DOMMediaStream can be destroyed and send
// its Destroy message before this decoder is destroyed. So we have to
// be careful not to send any messages after the Destroy().
if (mStream->IsDestroyed()) {
return false;
}
// Disconnect the existing port if necessary.
if (mPort) {
mPort->Destroy();
mPort = nullptr;
}
return true;
}
MediaStreamGraph*
OutputStreamData::Graph() const
{
return mStream->Graph();
}
void
OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
{
MOZ_ASSERT(NS_IsMainThread());
// All streams must belong to the same graph.
MOZ_ASSERT(!Graph() || Graph() == aStream->Graph());
// Ensure that aStream finishes the moment mDecodedStream does.
if (aFinishWhenEnded) {
aStream->SetAutofinish(true);
}
OutputStreamData* p = mStreams.AppendElement();
p->Init(this, aStream);
// Connect to the input stream if we have one. Otherwise the output stream
// will be connected in Connect().
if (mInputStream) {
p->Connect(mInputStream);
}
}
void
OutputStreamManager::Remove(MediaStream* aStream)
{
MOZ_ASSERT(NS_IsMainThread());
for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
if (mStreams[i].Equals(aStream)) {
mStreams.RemoveElementAt(i);
break;
}
}
}
void
OutputStreamManager::Connect(MediaStream* aStream)
{
MOZ_ASSERT(NS_IsMainThread());
mInputStream = aStream;
for (auto&& os : mStreams) {
os.Connect(aStream);
}
}
void
OutputStreamManager::Disconnect()
{
MOZ_ASSERT(NS_IsMainThread());
mInputStream = nullptr;
for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
if (!mStreams[i].Disconnect()) {
// Probably the DOMMediaStream was GCed. Clean up.
mStreams.RemoveElementAt(i);
}
}
}
DecodedStream::DecodedStream(AbstractThread* aOwnerThread,
MediaQueue<MediaData>& aAudioQueue,
MediaQueue<MediaData>& aVideoQueue)
MediaQueue<MediaData>& aVideoQueue,
OutputStreamManager* aOutputStreamManager)
: mOwnerThread(aOwnerThread)
, mOutputStreamManager(aOutputStreamManager)
, mShuttingDown(false)
, mPlaying(false)
, mSameOrigin(false)
@ -400,7 +294,7 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo)
class R : public nsRunnable {
typedef MozPromiseHolder<GenericPromise> Promise;
public:
R(PlaybackInfoInit&& aInit, Promise&& aPromise, OutputStreamManager aManager)
R(PlaybackInfoInit&& aInit, Promise&& aPromise, OutputStreamManager* aManager)
: mInit(Move(aInit)), mOutputStreamManager(aManager)
{
mPromise = Move(aPromise);
@ -410,7 +304,7 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo)
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()) {
if (!mOutputStreamManager->Graph()) {
// Resolve the promise to indicate the end of playback.
mPromise.Resolve(true, __func__);
return NS_OK;
@ -426,7 +320,7 @@ DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo)
private:
PlaybackInfoInit mInit;
Promise mPromise;
OutputStreamManager mOutputStreamManager;
RefPtr<OutputStreamManager> mOutputStreamManager;
UniquePtr<DecodedStreamData> mData;
};
@ -491,28 +385,6 @@ DecodedStream::DestroyData(UniquePtr<DecodedStreamData> aData)
AbstractThread::MainThread()->Dispatch(r.forget());
}
bool
DecodedStream::HasConsumers() const
{
return !mOutputStreamManager.IsEmpty();
}
void
DecodedStream::AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
{
mOutputStreamManager.Add(aStream, aFinishWhenEnded);
}
void
DecodedStream::RemoveOutput(MediaStream* aStream)
{
mOutputStreamManager.Remove(aStream);
}
void
DecodedStream::SetPlaying(bool aPlaying)
{

View File

@ -7,7 +7,6 @@
#ifndef DecodedStream_h_
#define DecodedStream_h_
#include "nsTArray.h"
#include "MediaEventSource.h"
#include "MediaInfo.h"
#include "MediaSink.h"
@ -20,12 +19,9 @@
namespace mozilla {
class DecodedStream;
class DecodedStreamData;
class MediaData;
class MediaInputPort;
class MediaStream;
class MediaStreamGraph;
class OutputStreamManager;
struct PlaybackInfoInit;
class ProcessedMediaStream;
@ -33,69 +29,14 @@ class TimeStamp;
template <class T> class MediaQueue;
class OutputStreamData {
public:
~OutputStreamData();
void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream);
// Connect mStream to the input stream.
void Connect(MediaStream* aStream);
// Disconnect mStream from its input stream.
// Return false is mStream is already destroyed, otherwise true.
bool Disconnect();
// Return true if aStream points to the same object as mStream.
// Used by OutputStreamManager to remove an output stream.
bool Equals(MediaStream* aStream)
{
return mStream == aStream;
}
// Return the graph mStream belongs to.
MediaStreamGraph* Graph() const;
private:
OutputStreamManager* mOwner;
RefPtr<ProcessedMediaStream> mStream;
// mPort connects our mStream to an input stream.
RefPtr<MediaInputPort> mPort;
};
class OutputStreamManager {
public:
// Add the output stream to the collection.
void Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
// Remove the output stream from the collection.
void Remove(MediaStream* aStream);
// Return true if the collection empty.
bool IsEmpty() const
{
MOZ_ASSERT(NS_IsMainThread());
return mStreams.IsEmpty();
}
// Connect all output streams in the collection to the input stream.
void Connect(MediaStream* aStream);
// Disconnect all output streams from the input stream.
void Disconnect();
// Return the graph these streams belong to or null if empty.
MediaStreamGraph* Graph() const
{
MOZ_ASSERT(NS_IsMainThread());
return !IsEmpty() ? mStreams[0].Graph() : nullptr;
}
private:
// Keep the input stream so we can connect the output streams that
// are added after Connect().
RefPtr<MediaStream> mInputStream;
nsTArray<OutputStreamData> mStreams;
};
class DecodedStream : public media::MediaSink {
using media::MediaSink::PlaybackParams;
public:
DecodedStream(AbstractThread* aOwnerThread,
MediaQueue<MediaData>& aAudioQueue,
MediaQueue<MediaData>& aVideoQueue);
MediaQueue<MediaData>& aVideoQueue,
OutputStreamManager* aOutputStreamManager);
// MediaSink functions.
const PlaybackParams& GetPlaybackParams() const override;
@ -122,11 +63,8 @@ public:
// TODO: fix these functions that don't fit into the interface of MediaSink.
void BeginShutdown();
void AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
void RemoveOutput(MediaStream* aStream);
void SetSameOrigin(bool aSameOrigin);
bool IsFinished() const;
bool HasConsumers() const;
protected:
virtual ~DecodedStream();
@ -151,7 +89,7 @@ private:
* Main thread only members.
*/
// Data about MediaStreams that are being fed by the decoder.
OutputStreamManager mOutputStreamManager;
const RefPtr<OutputStreamManager> mOutputStreamManager;
// True if MDSM has begun shutdown.
bool mShuttingDown;

View File

@ -0,0 +1,127 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "MediaStreamGraph.h"
#include "OutputStreamManager.h"
namespace mozilla {
OutputStreamData::~OutputStreamData()
{
MOZ_ASSERT(NS_IsMainThread());
// Break the connection to the input stream if necessary.
if (mPort) {
mPort->Destroy();
}
}
void
OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream)
{
mOwner = aOwner;
mStream = aStream;
}
void
OutputStreamData::Connect(MediaStream* aStream)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mPort, "Already connected?");
MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream.");
mPort = mStream->AllocateInputPort(aStream);
}
bool
OutputStreamData::Disconnect()
{
MOZ_ASSERT(NS_IsMainThread());
// During cycle collection, DOMMediaStream can be destroyed and send
// its Destroy message before this decoder is destroyed. So we have to
// be careful not to send any messages after the Destroy().
if (mStream->IsDestroyed()) {
return false;
}
// Disconnect the existing port if necessary.
if (mPort) {
mPort->Destroy();
mPort = nullptr;
}
return true;
}
bool
OutputStreamData::Equals(MediaStream* aStream) const
{
return mStream == aStream;
}
MediaStreamGraph*
OutputStreamData::Graph() const
{
return mStream->Graph();
}
void
OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
{
MOZ_ASSERT(NS_IsMainThread());
// All streams must belong to the same graph.
MOZ_ASSERT(!Graph() || Graph() == aStream->Graph());
// Ensure that aStream finishes the moment mDecodedStream does.
if (aFinishWhenEnded) {
aStream->SetAutofinish(true);
}
OutputStreamData* p = mStreams.AppendElement();
p->Init(this, aStream);
// Connect to the input stream if we have one. Otherwise the output stream
// will be connected in Connect().
if (mInputStream) {
p->Connect(mInputStream);
}
}
void
OutputStreamManager::Remove(MediaStream* aStream)
{
MOZ_ASSERT(NS_IsMainThread());
for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
if (mStreams[i].Equals(aStream)) {
mStreams.RemoveElementAt(i);
break;
}
}
}
void
OutputStreamManager::Connect(MediaStream* aStream)
{
MOZ_ASSERT(NS_IsMainThread());
mInputStream = aStream;
for (auto&& os : mStreams) {
os.Connect(aStream);
}
}
void
OutputStreamManager::Disconnect()
{
MOZ_ASSERT(NS_IsMainThread());
mInputStream = nullptr;
for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
if (!mStreams[i].Disconnect()) {
// Probably the DOMMediaStream was GCed. Clean up.
mStreams.RemoveElementAt(i);
}
}
}
} // namespace mozilla

View File

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#ifndef OutputStreamManager_h
#define OutputStreamManager_h
#include "mozilla/RefPtr.h"
#include "nsTArray.h"
namespace mozilla {
class MediaInputPort;
class MediaStream;
class MediaStreamGraph;
class OutputStreamManager;
class ProcessedMediaStream;
class OutputStreamData {
public:
~OutputStreamData();
void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream);
// Connect mStream to the input stream.
void Connect(MediaStream* aStream);
// Disconnect mStream from its input stream.
// Return false is mStream is already destroyed, otherwise true.
bool Disconnect();
// Return true if aStream points to the same object as mStream.
// Used by OutputStreamManager to remove an output stream.
bool Equals(MediaStream* aStream) const;
// Return the graph mStream belongs to.
MediaStreamGraph* Graph() const;
private:
OutputStreamManager* mOwner;
RefPtr<ProcessedMediaStream> mStream;
// mPort connects our mStream to an input stream.
RefPtr<MediaInputPort> mPort;
};
class OutputStreamManager {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OutputStreamManager);
public:
// Add the output stream to the collection.
void Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
// Remove the output stream from the collection.
void Remove(MediaStream* aStream);
// Return true if the collection empty.
bool IsEmpty() const
{
MOZ_ASSERT(NS_IsMainThread());
return mStreams.IsEmpty();
}
// Connect all output streams in the collection to the input stream.
void Connect(MediaStream* aStream);
// Disconnect all output streams from the input stream.
void Disconnect();
// Return the graph these streams belong to or null if empty.
MediaStreamGraph* Graph() const
{
MOZ_ASSERT(NS_IsMainThread());
return !IsEmpty() ? mStreams[0].Graph() : nullptr;
}
private:
~OutputStreamManager() {}
// Keep the input stream so we can connect the output streams that
// are added after Connect().
RefPtr<MediaStream> mInputStream;
nsTArray<OutputStreamData> mStreams;
};
} // namespace mozilla
#endif // OutputStreamManager_h

View File

@ -8,6 +8,7 @@ UNIFIED_SOURCES += [
'AudioSinkWrapper.cpp',
'DecodedAudioDataSink.cpp',
'DecodedStream.cpp',
'OutputStreamManager.cpp',
'VideoSink.cpp',
]