Bug 1208371 - Ensure DOMMediaStream principals reflect what could reside in their playback streams. r=mt,jesup

Calculating a principal when adding a track is easy - just combine the new
track principal into the stream's principal.

When removing a track it's a bit trickier. The DOMMediaStream has to wait until
the MediaStreamGraph has removed the track from the underlying playback stream.

We do this by letting the MediaStreamGraph return a Pledge (single threaded
Promise) when blocking a track in a stream (the way we end removed tracks).
The pledge gets passed to the MediaStreamGraph and when the block has been
applied it is passed back to the main thread where it is finally resolved
and the DOMMediaStream may recompute its principal once all outstanding
track removals have been applied.

MozReview-Commit-ID: 3QP0YcDyfGf

--HG--
extra : rebase_source : 6642849ec1c7d774467395dee82b0a37fdd33a99
This commit is contained in:
Andreas Pehrson 2016-03-16 16:00:34 +01:00
parent b631e13d54
commit 6862394047
4 changed files with 137 additions and 15 deletions

View File

@ -18,6 +18,7 @@
#include "mozilla/dom/VideoTrack.h"
#include "mozilla/dom/VideoTrackList.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/media/MediaUtils.h"
#include "MediaStreamGraph.h"
#include "AudioStreamTrack.h"
#include "VideoStreamTrack.h"
@ -37,6 +38,7 @@
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::layers;
using namespace mozilla::media;
static LazyLogModule gMediaStreamLog("MediaStream");
#define LOG(type, msg) MOZ_LOG(gMediaStreamLog, type, msg)
@ -90,12 +92,15 @@ DOMMediaStream::TrackPort::GetSourceTrackId() const
return mInputPort ? mInputPort->GetSourceTrackId() : TRACK_INVALID;
}
void
already_AddRefed<Pledge<bool>>
DOMMediaStream::TrackPort::BlockTrackId(TrackID aTrackId)
{
if (mInputPort) {
mInputPort->BlockTrackId(aTrackId);
return mInputPort->BlockTrackId(aTrackId);
}
RefPtr<Pledge<bool>> rejected = new Pledge<bool>();
rejected->Reject(NS_ERROR_FAILURE);
return rejected.forget();
}
NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackPort, mTrack)
@ -299,6 +304,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream,
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrackSourceGetter)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoPrincipal)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream,
@ -308,6 +315,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream,
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrackSourceGetter)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoPrincipal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper)
@ -337,8 +346,8 @@ DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
MediaStreamTrackSourceGetter* aTrackSourceGetter)
: mLogicalStreamStartTime(0), mWindow(aWindow),
mInputStream(nullptr), mOwnedStream(nullptr), mPlaybackStream(nullptr),
mTrackSourceGetter(aTrackSourceGetter), mTracksCreated(false),
mNotifiedOfMediaStreamGraphShutdown(false)
mTracksPendingRemoval(0), mTrackSourceGetter(aTrackSourceGetter),
mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false)
{
nsresult rv;
nsCOMPtr<nsIUUIDGenerator> uuidgen =
@ -584,7 +593,7 @@ DOMMediaStream::RemoveTrack(MediaStreamTrack& aTrack)
// to block it in the port. Doing this for a locked track is still OK as it
// will first block the track, then destroy the port. Both cause the track to
// end.
toRemove->BlockTrackId(aTrack.mTrackID);
BlockPlaybackTrack(toRemove);
DebugOnly<bool> removed = mTracks.RemoveElement(toRemove);
MOZ_ASSERT(removed);
@ -884,10 +893,26 @@ DOMMediaStream::RecomputePrincipal()
{
nsCOMPtr<nsIPrincipal> previousPrincipal = mPrincipal.forget();
nsCOMPtr<nsIPrincipal> previousVideoPrincipal = mVideoPrincipal.forget();
if (mTracksPendingRemoval > 0) {
LOG(LogLevel::Info, ("DOMMediaStream %p RecomputePrincipal() Cannot "
"recompute stream principal with tracks pending "
"removal.", this));
return;
}
LOG(LogLevel::Debug, ("DOMMediaStream %p Recomputing principal. "
"Old principal was %p.", this, previousPrincipal.get()));
// mPrincipal is recomputed based on all current tracks, and tracks that have
// not ended in our playback stream.
for (const RefPtr<TrackPort>& info : mTracks) {
if (info->GetTrack()->Ended()) {
continue;
}
LOG(LogLevel::Debug, ("DOMMediaStream %p Taking live track %p with "
"principal %p into account.", this,
info->GetTrack(), info->GetTrack()->GetPrincipal()));
nsContentUtils::CombineResourcePrincipals(&mPrincipal,
info->GetTrack()->GetPrincipal());
if (info->GetTrack()->AsVideoStreamTrack()) {
@ -896,6 +921,9 @@ DOMMediaStream::RecomputePrincipal()
}
}
LOG(LogLevel::Debug, ("DOMMediaStream %p new principal is %p.",
this, mPrincipal.get()));
if (previousPrincipal != mPrincipal ||
previousVideoPrincipal != mVideoPrincipal) {
NotifyPrincipalChanged();
@ -1151,7 +1179,26 @@ DOMMediaStream::NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack)
{
MOZ_ASSERT(NS_IsMainThread());
if (mTracksPendingRemoval > 0) {
// If there are tracks pending removal we may not degrade the current
// principals until those tracks have been confirmed removed from the
// playback stream. Instead combine with the new track and the (potentially)
// degraded principal will be calculated when it's safe.
nsContentUtils::CombineResourcePrincipals(&mPrincipal,
aTrack->GetPrincipal());
LOG(LogLevel::Debug, ("DOMMediaStream %p saw a track get added. Combining "
"its principal %p into our while waiting for pending "
"tracks to be removed. New principal is %p.",
this, aTrack->GetPrincipal(), mPrincipal.get()));
if (aTrack->AsVideoStreamTrack()) {
nsContentUtils::CombineResourcePrincipals(&mVideoPrincipal,
aTrack->GetPrincipal());
}
} else {
LOG(LogLevel::Debug, ("DOMMediaStream %p saw a track get added. "
"Recomputing principal.", this));
RecomputePrincipal();
}
aTrack->AddPrincipalChangeObserver(this);
@ -1171,7 +1218,9 @@ DOMMediaStream::NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack)
mTrackListeners[i]->NotifyTrackRemoved(aTrack);
}
RecomputePrincipal();
// Don't call RecomputePrincipal here as the track may still exist in the
// playback stream in the MediaStreamGraph. It will instead be called when the
// track has been confirmed removed by the graph. See BlockPlaybackTrack().
}
void
@ -1182,6 +1231,33 @@ DOMMediaStream::CreateAndAddPlaybackStreamListener(MediaStream* aStream)
aStream->AddListener(mPlaybackListener);
}
void
DOMMediaStream::BlockPlaybackTrack(TrackPort* aTrack)
{
MOZ_ASSERT(aTrack);
++mTracksPendingRemoval;
RefPtr<Pledge<bool>> p = aTrack->BlockTrackId(aTrack->GetTrack()->mTrackID);
RefPtr<DOMMediaStream> self = this;
p->Then([self] (const bool& aIgnore) { self->NotifyPlaybackTrackBlocked(); },
[] (const nsresult& aIgnore) { NS_ERROR("Could not remove track from MSG"); }
);
}
void
DOMMediaStream::NotifyPlaybackTrackBlocked()
{
MOZ_ASSERT(mTracksPendingRemoval > 0,
"A track reported finished blocking more times than we asked for");
if (--mTracksPendingRemoval == 0) {
// The MediaStreamGraph has reported a track was blocked and we are not
// waiting for any further tracks to get blocked. It is now safe to
// recompute the principal based on our main thread track set state.
LOG(LogLevel::Debug, ("DOMMediaStream %p saw all tracks pending removal "
"finish. Recomputing principal.", this));
RecomputePrincipal();
}
}
DOMLocalMediaStream::~DOMLocalMediaStream()
{
if (mInputStream) {

View File

@ -59,6 +59,10 @@ class ImageContainer;
class OverlayImage;
} // namespace layers
namespace media {
template<typename V, typename E> class Pledge;
} // namespace media
#define NS_DOMMEDIASTREAM_IID \
{ 0x8cb65468, 0x66c0, 0x444e, \
{ 0x89, 0x9f, 0x89, 0x1d, 0x9e, 0xd2, 0xbe, 0x7c } }
@ -302,9 +306,10 @@ public:
/**
* Blocks aTrackId from going into mInputPort unless the port has been
* destroyed.
* destroyed. Returns a pledge that gets resolved when the MediaStreamGraph
* has applied the block in the playback stream.
*/
void BlockTrackId(TrackID aTrackId);
already_AddRefed<media::Pledge<bool, nsresult>> BlockTrackId(TrackID aTrackId);
private:
RefPtr<MediaInputPort> mInputPort;
@ -605,6 +610,19 @@ protected:
// XXX Bug 1124630. Remove with CameraPreviewMediaStream.
void CreateAndAddPlaybackStreamListener(MediaStream*);
/**
* Block a track in our playback stream. Calls NotifyPlaybackTrackBlocked()
* after the MediaStreamGraph has applied the block and the track is no longer
* live.
*/
void BlockPlaybackTrack(TrackPort* aTrack);
/**
* Called on main thread after MediaStreamGraph has applied a track block in
* our playback stream.
*/
void NotifyPlaybackTrackBlocked();
// Recomputes the current principal of this stream based on the set of tracks
// it currently contains. PrincipalChangeObservers will be notified only if
// the principal changes.
@ -645,6 +663,10 @@ protected:
// MediaStreamTracks corresponding to tracks in our mPlaybackStream.
AutoTArray<RefPtr<TrackPort>, 2> mTracks;
// Number of MediaStreamTracks that have been removed on main thread but are
// waiting to be removed on MediaStreamGraph thread.
size_t mTracksPendingRemoval;
// The interface through which we can query the stream producer for
// track sources.
RefPtr<MediaStreamTrackSourceGetter> mTrackSourceGetter;

View File

@ -28,6 +28,7 @@
#include "DOMMediaStream.h"
#include "GeckoProfiler.h"
#include "mozilla/unused.h"
#include "mozilla/media/MediaUtils.h"
#ifdef MOZ_WEBRTC
#include "AudioOutputObserver.h"
#endif
@ -38,6 +39,7 @@
using namespace mozilla::layers;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::media;
namespace mozilla {
@ -3052,17 +3054,22 @@ MediaInputPort::BlockTrackIdImpl(TrackID aTrackId)
mBlockedTracks.AppendElement(aTrackId);
}
void
already_AddRefed<Pledge<bool>>
MediaInputPort::BlockTrackId(TrackID aTrackId)
{
class Message : public ControlMessage {
public:
explicit Message(MediaInputPort* aPort, TrackID aTrackId)
explicit Message(MediaInputPort* aPort,
TrackID aTrackId,
already_AddRefed<nsIRunnable> aRunnable)
: ControlMessage(aPort->GetDestination()),
mPort(aPort), mTrackId(aTrackId) {}
mPort(aPort), mTrackId(aTrackId), mRunnable(aRunnable) {}
void Run() override
{
mPort->BlockTrackIdImpl(mTrackId);
if (mRunnable) {
mStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(mRunnable.forget());
}
}
void RunDuringShutdown() override
{
@ -3070,11 +3077,20 @@ MediaInputPort::BlockTrackId(TrackID aTrackId)
}
RefPtr<MediaInputPort> mPort;
TrackID mTrackId;
nsCOMPtr<nsIRunnable> mRunnable;
};
MOZ_ASSERT(IsTrackIDExplicit(aTrackId),
"Only explicit TrackID is allowed");
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackId));
RefPtr<Pledge<bool>> pledge = new Pledge<bool>();
nsCOMPtr<nsIRunnable> runnable = NewRunnableFrom([pledge]() {
MOZ_ASSERT(NS_IsMainThread());
pledge->Resolve(true);
return NS_OK;
});
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackId, runnable.forget()));
return pledge.forget();
}
already_AddRefed<MediaInputPort>

View File

@ -41,6 +41,10 @@ namespace dom {
enum class AudioContextOperation;
}
namespace media {
template<typename V, typename E> class Pledge;
}
/*
* MediaStreamGraph is a framework for synchronized audio/video processing
* and playback. It is designed to be used by other browser components such as
@ -1233,8 +1237,12 @@ public:
ProcessedMediaStream* GetDestination() { return mDest; }
TrackID GetDestinationTrackId() { return mDestTrack; }
// Block aTrackId in the port. Consumers will interpret this track as ended.
void BlockTrackId(TrackID aTrackId);
/**
* Block aTrackId in the port. Consumers will interpret this track as ended.
* Returns a pledge that resolves on the main thread after the track block has
* been applied by the MSG.
*/
already_AddRefed<media::Pledge<bool, nsresult>> BlockTrackId(TrackID aTrackId);
private:
void BlockTrackIdImpl(TrackID aTrackId);