Bug 1208373 - Introduce a new blocking mode to MediaInputPort. r?jesup
This lets us notify about a created TrackUnionStream track (and since it was
created, we can notify when it ends), even though it has been blocked from main
thread.
MozReview-Commit-ID: HyopzISBfbb
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -93,20 +93,20 @@ DOMMediaStream::TrackPort::GetSource() c
TrackID
DOMMediaStream::TrackPort::GetSourceTrackId() const
{
return mInputPort ? mInputPort->GetSourceTrackId() : TRACK_INVALID;
}
already_AddRefed<Pledge<bool>>
-DOMMediaStream::TrackPort::BlockSourceTrackId(TrackID aTrackId)
+DOMMediaStream::TrackPort::BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode)
{
if (mInputPort) {
- return mInputPort->BlockSourceTrackId(aTrackId);
+ return mInputPort->BlockSourceTrackId(aTrackId, aBlockingMode);
}
RefPtr<Pledge<bool>> rejected = new Pledge<bool>();
rejected->Reject(NS_ERROR_FAILURE);
return rejected.forget();
}
NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackPort, mTrack)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMediaStream::TrackPort, AddRef)
@@ -1035,18 +1035,20 @@ DOMMediaStream::CloneDOMTrack(MediaStrea
NotifyTrackAdded(newTrack);
newTrack->SetEnabled(aTrack.Enabled());
newTrack->SetReadyState(aTrack.ReadyState());
if (aTrack.Ended()) {
// For extra suspenders, make sure that we don't forward data by mistake
// to the clone when the original has already ended.
+ // We only block END_EXISTING to allow any pending clones to end.
RefPtr<Pledge<bool, nsresult>> blockingPledge =
- inputPort->BlockSourceTrackId(inputTrackID);
+ inputPort->BlockSourceTrackId(inputTrackID,
+ BlockingMode::END_EXISTING);
Unused << blockingPledge;
}
return newTrack.forget();
}
static DOMMediaStream::TrackPort*
FindTrackPortAmongTracks(const MediaStreamTrack& aTrack,
@@ -1244,17 +1246,19 @@ DOMMediaStream::CreateAndAddPlaybackStre
aStream->AddListener(mPlaybackListener);
}
void
DOMMediaStream::BlockPlaybackTrack(TrackPort* aTrack)
{
MOZ_ASSERT(aTrack);
++mTracksPendingRemoval;
- RefPtr<Pledge<bool>> p = aTrack->BlockSourceTrackId(aTrack->GetTrack()->mTrackID);
+ RefPtr<Pledge<bool>> p =
+ aTrack->BlockSourceTrackId(aTrack->GetTrack()->mTrackID,
+ BlockingMode::CREATION);
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()
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -28,16 +28,18 @@ class DOMHwMediaStream;
class DOMLocalMediaStream;
class DOMMediaStream;
class MediaStream;
class MediaInputPort;
class MediaStreamDirectListener;
class MediaStreamGraph;
class ProcessedMediaStream;
+enum class BlockingMode;
+
namespace dom {
class AudioNode;
class HTMLCanvasElement;
class MediaStreamTrack;
class MediaStreamTrackSource;
class AudioStreamTrack;
class VideoStreamTrack;
class AudioTrack;
@@ -298,17 +300,18 @@ public:
MediaInputPort* GetInputPort() const { return mInputPort; }
MediaStreamTrack* GetTrack() const { return mTrack; }
/**
* Blocks aTrackId from going into mInputPort unless the port has been
* destroyed. Returns a pledge that gets resolved when the MediaStreamGraph
* has applied the block in the playback stream.
*/
- already_AddRefed<media::Pledge<bool, nsresult>> BlockSourceTrackId(TrackID aTrackId);
+ already_AddRefed<media::Pledge<bool, nsresult>>
+ BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode);
private:
RefPtr<MediaInputPort> mInputPort;
RefPtr<MediaStreamTrack> mTrack;
// Defines if we've been given ownership of the input port or if it's owned
// externally. The owner is responsible for destroying the port.
const InputPortOwnership mOwnership;
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -3131,57 +3131,60 @@ MediaInputPort::Graph()
void
MediaInputPort::SetGraphImpl(MediaStreamGraphImpl* aGraph)
{
MOZ_ASSERT(!mGraph || !aGraph, "Should only be set once");
mGraph = aGraph;
}
void
-MediaInputPort::BlockSourceTrackIdImpl(TrackID aTrackId)
+MediaInputPort::BlockSourceTrackIdImpl(TrackID aTrackId, BlockingMode aBlockingMode)
{
- mBlockedTracks.AppendElement(aTrackId);
+ mBlockedTracks.AppendElement(Pair<TrackID, BlockingMode>(aTrackId, aBlockingMode));
}
already_AddRefed<Pledge<bool>>
-MediaInputPort::BlockSourceTrackId(TrackID aTrackId)
+MediaInputPort::BlockSourceTrackId(TrackID aTrackId, BlockingMode aBlockingMode)
{
class Message : public ControlMessage {
public:
explicit Message(MediaInputPort* aPort,
TrackID aTrackId,
+ BlockingMode aBlockingMode,
already_AddRefed<nsIRunnable> aRunnable)
: ControlMessage(aPort->GetDestination()),
- mPort(aPort), mTrackId(aTrackId), mRunnable(aRunnable) {}
+ mPort(aPort), mTrackId(aTrackId), mBlockingMode(aBlockingMode),
+ mRunnable(aRunnable) {}
void Run() override
{
- mPort->BlockSourceTrackIdImpl(mTrackId);
+ mPort->BlockSourceTrackIdImpl(mTrackId, mBlockingMode);
if (mRunnable) {
mStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(mRunnable.forget());
}
}
void RunDuringShutdown() override
{
Run();
}
RefPtr<MediaInputPort> mPort;
TrackID mTrackId;
+ BlockingMode mBlockingMode;
nsCOMPtr<nsIRunnable> mRunnable;
};
MOZ_ASSERT(IsTrackIDExplicit(aTrackId),
"Only explicit TrackID is allowed");
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()));
+ GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackId, aBlockingMode, runnable.forget()));
return pledge.forget();
}
already_AddRefed<MediaInputPort>
ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, TrackID aTrackID,
TrackID aDestTrackID,
uint16_t aInputNumber, uint16_t aOutputNumber,
nsTArray<TrackID>* aBlockedTracks)
@@ -3214,17 +3217,17 @@ ProcessedMediaStream::AllocateInputPort(
"Only TRACK_ANY and explicit ID are allowed for destination track");
MOZ_ASSERT(aTrackID != TRACK_ANY || aDestTrackID == TRACK_ANY,
"Generic MediaInputPort cannot produce a single destination track");
RefPtr<MediaInputPort> port =
new MediaInputPort(aStream, aTrackID, this, aDestTrackID,
aInputNumber, aOutputNumber);
if (aBlockedTracks) {
for (TrackID trackID : *aBlockedTracks) {
- port->BlockSourceTrackIdImpl(trackID);
+ port->BlockSourceTrackIdImpl(trackID, BlockingMode::CREATION);
}
}
port->SetGraphImpl(GraphImpl());
GraphImpl()->AppendMessage(MakeUnique<Message>(port));
return port.forget();
}
void
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -1172,16 +1172,33 @@ protected:
nsTArray<RefPtr<MediaStreamDirectListener> > mDirectListeners;
nsTArray<TrackBound<MediaStreamTrackDirectListener>> mDirectTrackListeners;
bool mPullEnabled;
bool mUpdateFinished;
bool mNeedsMixing;
};
/**
+ * The blocking mode decides how a track should be blocked in a MediaInputPort.
+ */
+enum class BlockingMode
+{
+ /**
+ * BlockingMode CREATION blocks the source track from being created
+ * in the destination. It'll end if it already exists.
+ */
+ CREATION,
+ /**
+ * BlockingMode END_EXISTING allows a track to be created in the destination
+ * but will end it before any data has been passed through.
+ */
+ END_EXISTING,
+};
+
+/**
* Represents a connection between a ProcessedMediaStream and one of its
* input streams.
* We make these refcounted so that stream-related messages with MediaInputPort*
* pointers can be sent to the main thread safely.
*
* A port can be locked to a specific track in the source stream, in which case
* only this track will be forwarded to the destination stream. TRACK_ANY
* can used to signal that all tracks shall be forwarded.
@@ -1252,26 +1269,49 @@ public:
TrackID GetDestinationTrackId() { return mDestTrack; }
/**
* Block aTrackId in the source stream from being passed through 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>> BlockSourceTrackId(TrackID aTrackId);
+ already_AddRefed<media::Pledge<bool, nsresult>> BlockSourceTrackId(TrackID aTrackId,
+ BlockingMode aBlockingMode);
private:
- void BlockSourceTrackIdImpl(TrackID aTrackId);
+ void BlockSourceTrackIdImpl(TrackID aTrackId, BlockingMode aBlockingMode);
public:
- // Returns true if aTrackId has not been blocked and this port has not
- // been locked to another track.
+ // Returns true if aTrackId has not been blocked for any reason and this port
+ // has not been locked to another track.
bool PassTrackThrough(TrackID aTrackId) {
- return !mBlockedTracks.Contains(aTrackId) &&
- (mSourceTrack == TRACK_ANY || mSourceTrack == aTrackId);
+ bool blocked = false;
+ for (auto pair : mBlockedTracks) {
+ if (pair.first() == aTrackId &&
+ (pair.second() == BlockingMode::CREATION ||
+ pair.second() == BlockingMode::END_EXISTING)) {
+ blocked = true;
+ break;
+ }
+ }
+ return !blocked && (mSourceTrack == TRACK_ANY || mSourceTrack == aTrackId);
+ }
+
+ // Returns true if aTrackId has not been blocked for track creation and this
+ // port has not been locked to another track.
+ bool AllowCreationOf(TrackID aTrackId) {
+ bool blocked = false;
+ for (auto pair : mBlockedTracks) {
+ if (pair.first() == aTrackId &&
+ pair.second() == BlockingMode::CREATION) {
+ blocked = true;
+ break;
+ }
+ }
+ return !blocked && (mSourceTrack == TRACK_ANY || mSourceTrack == aTrackId);
}
uint16_t InputNumber() const { return mInputNumber; }
uint16_t OutputNumber() const { return mOutputNumber; }
// Call on graph manager thread
struct InputInterval {
GraphTime mStart;
@@ -1316,17 +1356,19 @@ private:
MediaStream* mSource;
TrackID mSourceTrack;
ProcessedMediaStream* mDest;
TrackID mDestTrack;
// The input and output numbers are optional, and are currently only used by
// Web Audio.
const uint16_t mInputNumber;
const uint16_t mOutputNumber;
- nsTArray<TrackID> mBlockedTracks;
+
+ typedef Pair<TrackID, BlockingMode> BlockedTrack;
+ nsTArray<BlockedTrack> mBlockedTracks;
// Our media stream graph
MediaStreamGraphImpl* mGraph;
};
/**
* This stream processes zero or more input streams in parallel to produce
* its output. The details of how the output is produced are handled by
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -232,17 +232,17 @@ MediaStreamTrack::Stop()
return;
}
mSource->UnregisterSink(this);
MOZ_ASSERT(mOwningStream, "Every MediaStreamTrack needs an owning DOMMediaStream");
DOMMediaStream::TrackPort* port = mOwningStream->FindOwnedTrackPort(*this);
MOZ_ASSERT(port, "A MediaStreamTrack must exist in its owning DOMMediaStream");
- RefPtr<Pledge<bool>> p = port->BlockSourceTrackId(mInputTrackID);
+ RefPtr<Pledge<bool>> p = port->BlockSourceTrackId(mInputTrackID, BlockingMode::CREATION);
Unused << p;
mReadyState = MediaStreamTrackState::Ended;
}
already_AddRefed<Promise>
MediaStreamTrack::ApplyConstraints(const MediaTrackConstraints& aConstraints,
ErrorResult &aRv)
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -102,17 +102,17 @@ TrackUnionStream::TrackUnionStream(DOMMe
} else {
CopyTrackData(tracks.get(), j, aFrom, aTo, &trackFinished);
}
mappedTracksFinished[j] = trackFinished;
mappedTracksWithMatchingInputTracks[j] = true;
break;
}
}
- if (!found && mInputs[i]->PassTrackThrough(tracks->GetID())) {
+ if (!found && mInputs[i]->AllowCreationOf(tracks->GetID())) {
bool trackFinished = false;
trackAdded = true;
uint32_t mapIndex = AddTrack(mInputs[i], tracks.get(), aFrom);
CopyTrackData(tracks.get(), mapIndex, aFrom, aTo, &trackFinished);
mappedTracksFinished.AppendElement(trackFinished);
mappedTracksWithMatchingInputTracks.AppendElement(true);
}
}