Bug 1208316 - Implement MediaStream.active. r?jib, r?smaug
MozReview-Commit-ID: Fzk5vepqQ35
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -361,17 +361,18 @@ NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMe
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
MediaStreamTrackSourceGetter* aTrackSourceGetter)
: mLogicalStreamStartTime(0), mWindow(aWindow),
mInputStream(nullptr), mOwnedStream(nullptr), mPlaybackStream(nullptr),
mTracksPendingRemoval(0), mTrackSourceGetter(aTrackSourceGetter),
- mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false)
+ mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false),
+ mActive(false)
{
nsresult rv;
nsCOMPtr<nsIUUIDGenerator> uuidgen =
do_GetService("@mozilla.org/uuid-generator;1", &rv);
if (NS_SUCCEEDED(rv) && uuidgen) {
nsID uuid;
memset(&uuid, 0, sizeof(uuid));
@@ -613,23 +614,23 @@ DOMMediaStream::RemoveTrack(MediaStreamT
// If the track comes from a TRACK_ANY input port (i.e., mOwnedPort), we need
// 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.
// If the track has already ended, it's input port might be gone, so in those
// cases blocking the underlying track should be avoided.
if (!aTrack.Ended()) {
BlockPlaybackTrack(toRemove);
+
+ bool removed = mTracks.RemoveElement(toRemove);
+ if (removed) {
+ NotifyTrackRemoved(&aTrack);
+ }
}
- DebugOnly<bool> removed = mTracks.RemoveElement(toRemove);
- MOZ_ASSERT(removed);
-
- NotifyTrackRemoved(&aTrack);
-
LOG(LogLevel::Debug, ("DOMMediaStream %p Removed track %p", this, &aTrack));
}
class ClonedStreamSourceGetter :
public MediaStreamTrackSourceGetter
{
public:
NS_DECL_ISUPPORTS_INHERITED
@@ -725,16 +726,22 @@ DOMMediaStream::CloneInternal(TrackForwa
TRACK_ANY, TRACK_ANY, 0, 0,
&tracksToBlock);
}
}
return newStream.forget();
}
+bool
+DOMMediaStream::Active() const
+{
+ return mActive;
+}
+
MediaStreamTrack*
DOMMediaStream::GetTrackById(const nsAString& aId) const
{
for (const RefPtr<TrackPort>& info : mTracks) {
nsString id;
info->GetTrack()->GetId(id);
if (id == aId) {
return info->GetTrack();
@@ -1175,16 +1182,38 @@ DOMMediaStream::OnTracksAvailable(OnTrac
void
DOMMediaStream::NotifyTracksCreated()
{
mTracksCreated = true;
CheckTracksAvailable();
}
void
+DOMMediaStream::NotifyActive()
+{
+ LOG(LogLevel::Info, ("DOMMediaStream %p NotifyActive(). ", this));
+
+ MOZ_ASSERT(mActive);
+ for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
+ mTrackListeners[i]->NotifyActive();
+ }
+}
+
+void
+DOMMediaStream::NotifyInactive()
+{
+ LOG(LogLevel::Info, ("DOMMediaStream %p NotifyInactive(). ", this));
+
+ MOZ_ASSERT(!mActive);
+ for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
+ mTrackListeners[i]->NotifyInactive();
+ }
+}
+
+void
DOMMediaStream::CheckTracksAvailable()
{
if (!mTracksCreated) {
return;
}
nsTArray<nsAutoPtr<OnTracksAvailableCallback> > callbacks;
callbacks.SwapElements(mRunOnTracksAvailable);
@@ -1238,32 +1267,70 @@ DOMMediaStream::NotifyTrackAdded(const R
RecomputePrincipal();
}
aTrack->AddPrincipalChangeObserver(this);
for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
mTrackListeners[i]->NotifyTrackAdded(aTrack);
}
+
+ if (mActive) {
+ return;
+ }
+
+ // Check if we became active.
+ bool active = false;
+ for (auto port : mTracks) {
+ if (!port->GetTrack()->Ended()) {
+ active = true;
+ break;
+ }
+ }
+
+ if (active) {
+ mActive = true;
+ NotifyActive();
+ }
}
void
DOMMediaStream::NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack)
{
MOZ_ASSERT(NS_IsMainThread());
aTrack->RemovePrincipalChangeObserver(this);
for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
mTrackListeners[i]->NotifyTrackRemoved(aTrack);
+
}
// 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().
+
+ if (!mActive) {
+ NS_ASSERTION(false, "Shouldn't remove a live track if already inactive");
+ return;
+ }
+
+ // Check if we became inactive.
+ bool active = false;
+ for (auto port : mTracks) {
+ if (!port->GetTrack()->Ended()) {
+ active = true;
+ break;
+ }
+ }
+
+ if (!active) {
+ mActive = false;
+ NotifyInactive();
+ }
}
nsresult
DOMMediaStream::DispatchTrackEvent(const nsAString& aName,
const RefPtr<MediaStreamTrack>& aTrack)
{
MOZ_ASSERT(aName == NS_LITERAL_STRING("addtrack"),
"Only 'addtrack' is supported at this time");
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -221,28 +221,40 @@ class DOMMediaStream : public DOMEventTa
public:
typedef dom::MediaTrackConstraints MediaTrackConstraints;
class TrackListener {
public:
virtual ~TrackListener() {}
/**
- * Called when the DOMMediaStream has a new track added, either by
- * JS (addTrack()) or the source creating one.
+ * Called when the DOMMediaStream has a live track added, either by
+ * script (addTrack()) or the source creating one.
*/
virtual void
NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) {};
/**
- * Called when the DOMMediaStream removes a track, either by
- * JS (removeTrack()) or the source ending it.
+ * Called when the DOMMediaStream removes a live track from playback, either
+ * by script (removeTrack(), track.stop()) or the source ending it.
*/
virtual void
NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack) {};
+
+ /**
+ * Called when the DOMMediaStream has become active.
+ */
+ virtual void
+ NotifyActive() {};
+
+ /**
+ * Called when the DOMMediaStream has become inactive.
+ */
+ virtual void
+ NotifyInactive() {};
};
/**
* TrackPort is a representation of a MediaStreamTrack-MediaInputPort pair
* that make up a link between the Owned stream and the Playback stream.
*
* Semantically, the track is the identifier/key and the port the value of this
* connection.
@@ -358,16 +370,18 @@ public:
void GetTracks(nsTArray<RefPtr<MediaStreamTrack> >& aTracks) const;
MediaStreamTrack* GetTrackById(const nsAString& aId) const;
void AddTrack(MediaStreamTrack& aTrack);
void RemoveTrack(MediaStreamTrack& aTrack);
/** Identical to CloneInternal(TrackForwardingOption::EXPLICIT) */
already_AddRefed<DOMMediaStream> Clone();
+ bool Active() const;
+
IMPL_EVENT_HANDLER(addtrack)
// NON-WebIDL
/**
* Option to provide to CloneInternal() of which tracks should be forwarded
* from the source stream (`this`) to the returned stream clone.
*
@@ -594,16 +608,22 @@ protected:
void InitPlaybackStreamCommon(MediaStreamGraph* aGraph);
void CheckTracksAvailable();
// Called when MediaStreamGraph has finished an iteration where tracks were
// created.
void NotifyTracksCreated();
+ // Dispatches NotifyActive() to all registered track listeners.
+ void NotifyActive();
+
+ // Dispatches NotifyInactive() to all registered track listeners.
+ void NotifyInactive();
+
// Dispatches NotifyTrackAdded() to all registered track listeners.
void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack);
// Dispatches NotifyTrackRemoved() to all registered track listeners.
void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack);
// Dispatches "addtrack" or "removetrack".
nsresult DispatchTrackEvent(const nsAString& aName,
@@ -694,16 +714,19 @@ protected:
// Keep these alive while the stream is alive.
nsTArray<nsCOMPtr<nsISupports>> mConsumersToKeepAlive;
bool mNotifiedOfMediaStreamGraphShutdown;
// The track listeners subscribe to changes in this stream's track set.
nsTArray<TrackListener*> mTrackListeners;
+ // True if this stream has live tracks.
+ bool mActive;
+
private:
void NotifyPrincipalChanged();
// Principal identifying who may access the collected contents of this stream.
// If null, this stream can be used by anyone because it has no content yet.
nsCOMPtr<nsIPrincipal> mPrincipal;
// Video principal is used by video element as access is requested to its
// image data.
nsCOMPtr<nsIPrincipal> mVideoPrincipal;
--- a/dom/webidl/MediaStream.webidl
+++ b/dom/webidl/MediaStream.webidl
@@ -31,15 +31,13 @@ interface MediaStream : EventTarget {
readonly attribute DOMString id;
sequence<AudioStreamTrack> getAudioTracks ();
sequence<VideoStreamTrack> getVideoTracks ();
sequence<MediaStreamTrack> getTracks ();
MediaStreamTrack? getTrackById (DOMString trackId);
void addTrack (MediaStreamTrack track);
void removeTrack (MediaStreamTrack track);
MediaStream clone ();
- // readonly attribute boolean active;
- // attribute EventHandler onactive;
- // attribute EventHandler oninactive;
+ readonly attribute boolean active;
attribute EventHandler onaddtrack;
// attribute EventHandler onremovetrack;
readonly attribute double currentTime;
};