Bug 1208316 - Punch a hole for media element captureStream to only go inactive as source ends. r?jib
MozReview-Commit-ID: 3H0m3fYMw1Y
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2521,16 +2521,17 @@ HTMLMediaElement::CaptureStreamInternal(
if (!mOutputStreams.IsEmpty() &&
aGraph != mOutputStreams[0].mStream->GetInputStream()->Graph()) {
return nullptr;
}
OutputMediaStream* out = mOutputStreams.AppendElement();
MediaStreamTrackSourceGetter* getter = new CaptureStreamTrackSourceGetter(this);
out->mStream = DOMMediaStream::CreateTrackUnionStreamAsInput(window, aGraph, getter);
+ out->mStream->SetInactiveOnFinish();
out->mFinishWhenEnded = aFinishWhenEnded;
out->mCapturingAudioOnly = aCaptureAudio;
if (aCaptureAudio) {
if (mSrcStream) {
// We don't support applying volume and mute to the captured stream, when
// capturing a MediaStream.
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -47,16 +47,27 @@ 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)
const TrackID TRACK_VIDEO_PRIMARY = 1;
+static bool
+ContainsLiveTracks(nsTArray<RefPtr<DOMMediaStream::TrackPort>>& aTracks)
+{
+ for (auto& port : aTracks) {
+ if (port->GetTrack()->ReadyState() == MediaStreamTrackState::Live) {
+ return true;
+ }
+ }
+
+ return false;
+}
DOMMediaStream::TrackPort::TrackPort(MediaInputPort* aInputPort,
MediaStreamTrack* aTrack,
const InputPortOwnership aOwnership)
: mInputPort(aInputPort)
, mTrack(aTrack)
, mOwnership(aOwnership)
{
@@ -266,16 +277,26 @@ public:
void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override
{
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
}
+
+ void NotifyEvent(MediaStreamGraph* aGraph,
+ MediaStreamGraphEvent event) override
+ {
+ if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
+ aGraph->DispatchToMainThreadAfterStreamStateUpdate(
+ NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinished));
+ }
+ }
+
private:
// These fields may only be accessed on the main thread
DOMMediaStream* mStream;
};
class DOMMediaStream::PlaybackTrackListener : public MediaStreamTrackConsumer
{
public:
@@ -370,17 +391,17 @@ NS_INTERFACE_MAP_END_INHERITING(DOMMedia
DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
MediaStreamTrackSourceGetter* aTrackSourceGetter)
: mLogicalStreamStartTime(0), mWindow(aWindow),
mInputStream(nullptr), mOwnedStream(nullptr), mPlaybackStream(nullptr),
mTracksPendingRemoval(0), mTrackSourceGetter(aTrackSourceGetter),
mPlaybackTrackListener(MakeAndAddRef<PlaybackTrackListener>(this)),
mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false),
- mActive(false)
+ mActive(false), mSetInactiveOnFinish(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));
@@ -801,22 +822,28 @@ void
DOMMediaStream::RemoveDirectListener(DirectMediaStreamListener* aListener)
{
if (GetInputStream() && GetInputStream()->AsSourceStream()) {
GetInputStream()->AsSourceStream()->RemoveDirectListener(aListener);
}
}
bool
-DOMMediaStream::IsFinished()
+DOMMediaStream::IsFinished() const
{
return !mPlaybackStream || mPlaybackStream->IsFinished();
}
void
+DOMMediaStream::SetInactiveOnFinish()
+{
+ mSetInactiveOnFinish = true;
+}
+
+void
DOMMediaStream::InitSourceStream(MediaStreamGraph* aGraph)
{
InitInputStreamCommon(aGraph->CreateSourceStream(), aGraph);
InitOwnedStreamCommon(aGraph);
InitPlaybackStreamCommon(aGraph);
}
void
@@ -1194,16 +1221,33 @@ DOMMediaStream::OnTracksAvailable(OnTrac
void
DOMMediaStream::NotifyTracksCreated()
{
mTracksCreated = true;
CheckTracksAvailable();
}
void
+DOMMediaStream::NotifyFinished()
+{
+ if (!mSetInactiveOnFinish) {
+ return;
+ }
+
+ if (!mActive) {
+ // This can happen if the stream never became active.
+ return;
+ }
+
+ MOZ_ASSERT(!ContainsLiveTracks(mTracks));
+ mActive = false;
+ NotifyInactive();
+}
+
+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();
}
@@ -1286,25 +1330,17 @@ DOMMediaStream::NotifyTrackAdded(const R
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) {
+ if (ContainsLiveTracks(mTracks)) {
mActive = true;
NotifyActive();
}
}
void
DOMMediaStream::NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack)
{
@@ -1322,26 +1358,24 @@ DOMMediaStream::NotifyTrackRemoved(const
// 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 (mSetInactiveOnFinish) {
+ // For compatibility with mozCaptureStream we in some cases do not go
+ // inactive until the playback stream finishes.
+ return;
}
- if (!active) {
+ // Check if we became inactive.
+ if (!ContainsLiveTracks(mTracks)) {
mActive = false;
NotifyInactive();
}
}
nsresult
DOMMediaStream::DispatchTrackEvent(const nsAString& aName,
const RefPtr<MediaStreamTrack>& aTrack)
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -454,17 +454,26 @@ public:
* queuing. Returns a bool to let us know if direct data will be delivered.
*/
bool AddDirectListener(DirectMediaStreamListener *aListener);
void RemoveDirectListener(DirectMediaStreamListener *aListener);
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
- bool IsFinished();
+ /**
+ * Legacy method that returns true when the playback stream has finished.
+ */
+ bool IsFinished() const;
+
+ /**
+ * Becomes inactive only when the playback stream has finished.
+ */
+ void SetInactiveOnFinish();
+
/**
* Returns a principal indicating who may access this stream. The stream contents
* can only be accessed by principals subsuming this principal.
*/
nsIPrincipal* GetPrincipal() { return mPrincipal; }
/**
* Returns a principal indicating who may access video data of this stream.
@@ -608,16 +617,19 @@ protected:
void InitPlaybackStreamCommon(MediaStreamGraph* aGraph);
void CheckTracksAvailable();
// Called when MediaStreamGraph has finished an iteration where tracks were
// created.
void NotifyTracksCreated();
+ // Called when our playback stream has finished in the MediaStreamGraph.
+ void NotifyFinished();
+
// 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);
@@ -723,16 +735,21 @@ protected:
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;
+ // True if this stream only sets mActive to false when its playback stream
+ // finishes. This is a hack to maintain legacy functionality for playing a
+ // HTMLMediaElement::MozCaptureStream(). See bug 1302379.
+ bool mSetInactiveOnFinish;
+
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;