Bug 1301675 - Implement StreamCaptureTrackSource::Stop. r?jib
MozReview-Commit-ID: 2qwNba6bA80
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1392,16 +1392,45 @@ void HTMLMediaElement::NotifyMediaStream
// We are a video element and HasVideo() changed so update the screen
// wakelock
NotifyOwnerDocumentActivityChanged();
}
mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
}
+void
+HTMLMediaElement::NotifyOutputTrackStopped(DOMMediaStream* aOwningStream,
+ TrackID aDestinationTrackID)
+{
+ for (OutputMediaStream& ms : mOutputStreams) {
+ if (!ms.mCapturingMediaStream) {
+ continue;
+ }
+
+ if (ms.mStream != aOwningStream) {
+ continue;
+ }
+
+ for (int32_t i = ms.mTrackPorts.Length() - 1; i >= 0; --i) {
+ MediaInputPort* port = ms.mTrackPorts[i].second();
+ if (port->GetDestinationTrackId() != aDestinationTrackID) {
+ continue;
+ }
+
+ port->Destroy();
+ ms.mTrackPorts.RemoveElementAt(i);
+ return;
+ }
+ }
+
+ // An output track ended but its port is already gone.
+ // It was probably cleared by the removal of the source MediaTrack.
+}
+
void HTMLMediaElement::LoadFromSourceChildren()
{
NS_ASSERTION(mDelayingLoadEvent,
"Should delay load event (if in document) during load");
NS_ASSERTION(mIsLoadingFromSourceChildren,
"Must remember we're loading from source children");
nsIDocument* parentDoc = OwnerDoc()->GetParentDocument();
@@ -2198,26 +2227,35 @@ class HTMLMediaElement::StreamCaptureTra
public MediaStreamTrackSource,
public MediaStreamTrackSource::Sink
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(StreamCaptureTrackSource,
MediaStreamTrackSource)
- explicit StreamCaptureTrackSource(MediaStreamTrackSource* aCapturedTrackSource)
+ StreamCaptureTrackSource(HTMLMediaElement* aElement,
+ MediaStreamTrackSource* aCapturedTrackSource,
+ DOMMediaStream* aOwningStream,
+ TrackID aDestinationTrackID)
: MediaStreamTrackSource(aCapturedTrackSource->GetPrincipal(),
nsString())
+ , mElement(aElement)
, mCapturedTrackSource(aCapturedTrackSource)
+ , mOwningStream(aOwningStream)
+ , mDestinationTrackID(aDestinationTrackID)
{
}
void Destroy() override
{
- MOZ_ASSERT(mCapturedTrackSource);
+ if (mCapturedTrackSource) {
+ mCapturedTrackSource->UnregisterSink(this);
+ mCapturedTrackSource = nullptr;
+ }
}
MediaSourceEnum GetMediaSource() const override
{
return MediaSourceEnum::Other;
}
CORSMode GetCORSMode() const override
@@ -2233,42 +2271,53 @@ public:
p->Reject(new dom::MediaStreamError(aWindow,
NS_LITERAL_STRING("OverconstrainedError"),
NS_LITERAL_STRING("")));
return p.forget();
}
void Stop() override
{
- // XXX fix in later patch
- NS_ERROR("We're reporting remote=true to not be stoppable. "
- "Stop() should not be called.");
+ if (mElement && mElement->mSrcStream) {
+ // Only notify if we're still playing the source stream. GC might have
+ // cleared it before the track sources.
+ mElement->NotifyOutputTrackStopped(mOwningStream, mDestinationTrackID);
+ }
+ mElement = nullptr;
+ mOwningStream = nullptr;
+
+ Destroy();
}
void PrincipalChanged() override
{
mPrincipal = mCapturedTrackSource->GetPrincipal();
MediaStreamTrackSource::PrincipalChanged();
}
private:
virtual ~StreamCaptureTrackSource() {}
+ RefPtr<HTMLMediaElement> mElement;
RefPtr<MediaStreamTrackSource> mCapturedTrackSource;
+ RefPtr<DOMMediaStream> mOwningStream;
+ TrackID mDestinationTrackID;
};
NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::StreamCaptureTrackSource,
MediaStreamTrackSource)
NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::StreamCaptureTrackSource,
MediaStreamTrackSource)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::StreamCaptureTrackSource)
NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource)
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::StreamCaptureTrackSource,
MediaStreamTrackSource,
- mCapturedTrackSource)
+ mElement,
+ mCapturedTrackSource,
+ mOwningStream)
class HTMLMediaElement::DecoderCaptureTrackSource :
public MediaStreamTrackSource,
public DecoderPrincipalChangeObserver
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DecoderCaptureTrackSource,
@@ -2454,17 +2503,20 @@ HTMLMediaElement::AddCaptureMediaTrackTo
for (auto pair : aOutputStream.mTrackPorts) {
MOZ_ASSERT(pair.first() != aTrack->GetId(),
"Captured track already captured to output stream");
}
#endif
TrackID destinationTrackID = aOutputStream.mNextAvailableTrackID++;
RefPtr<MediaStreamTrackSource> source =
- new StreamCaptureTrackSource(&inputTrack->GetSource());
+ new StreamCaptureTrackSource(this,
+ &inputTrack->GetSource(),
+ aOutputStream.mStream,
+ destinationTrackID);
MediaSegment::Type type = inputTrack->AsAudioStreamTrack()
? MediaSegment::AUDIO
: MediaSegment::VIDEO;
RefPtr<MediaStreamTrack> track =
aOutputStream.mStream->CreateDOMTrack(destinationTrackID, type, source);
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -350,16 +350,23 @@ public:
*/
void NotifyMediaTrackDisabled(MediaTrack* aTrack);
/**
* Called when tracks become available to the source media stream.
*/
void NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream);
+ /**
+ * Called when a captured MediaStreamTrack is stopped so we can clean up its
+ * MediaInputPort.
+ */
+ void NotifyOutputTrackStopped(DOMMediaStream* aOwningStream,
+ TrackID aDestinationTrackID);
+
virtual bool IsNodeOfType(uint32_t aFlags) const override;
/**
* Returns the current load ID. Asynchronous events store the ID that was
* current when they were enqueued, and if it has changed when they come to
* fire, they consider themselves cancelled, and don't fire.
*/
uint32_t GetCurrentLoadID() { return mCurrentLoadID; }