--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -524,17 +524,18 @@ nsDOMCameraControl::GetCameraStream() co
return mInput;
}
void
nsDOMCameraControl::TrackCreated(TrackID aTrackID) {
// This track is not connected through a port.
MediaInputPort* inputPort = nullptr;
dom::VideoStreamTrack* track =
- new dom::VideoStreamTrack(this, aTrackID, nsString());
+ new dom::VideoStreamTrack(this, aTrackID, nsString(),
+ new BasicUnstoppableTrackSource());
RefPtr<TrackPort> port =
new TrackPort(inputPort, track,
TrackPort::InputPortOwnership::OWNED);
mTracks.AppendElement(port.forget());
NotifyTrackAdded(track);
}
#define THROW_IF_NO_CAMERACONTROL(...) \
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -13,16 +13,17 @@
#include "MediaSegment.h"
#include "mozilla/Assertions.h"
#include "mozilla/Base64.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/CanvasCaptureMediaStream.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/HTMLCanvasElementBinding.h"
+#include "mozilla/dom/MediaStreamTrack.h"
#include "mozilla/dom/MouseEvent.h"
#include "mozilla/dom/OffscreenCanvas.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/gfx/Rect.h"
#include "mozilla/layers/AsyncCanvasRenderer.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
@@ -677,17 +678,18 @@ HTMLCanvasElement::CaptureStream(const O
TrackID videoTrackId = 1;
nsresult rv = stream->Init(aFrameRate, videoTrackId);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return nullptr;
}
- stream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString());
+ stream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString(),
+ new BasicUnstoppableTrackSource());
rv = RegisterFrameCaptureListener(stream->FrameCaptureListener());
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return nullptr;
}
return stream.forget();
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1900,21 +1900,25 @@ HTMLMediaElement::CaptureStreamInternal(
mAudioCaptured = true;
if (mDecoder) {
mDecoder->AddOutputStream(out->mStream->GetInputStream()->AsProcessedStream(),
aFinishWhenEnded);
if (mReadyState >= HAVE_METADATA) {
// Expose the tracks to JS directly.
if (HasAudio()) {
TrackID audioTrackId = mMediaInfo.mAudio.mTrackId;
- out->mStream->CreateOwnDOMTrack(audioTrackId, MediaSegment::AUDIO, nsString());
+ RefPtr<MediaStreamTrackSource> trackSource = new BasicUnstoppableTrackSource();
+ out->mStream->CreateOwnDOMTrack(audioTrackId, MediaSegment::AUDIO,
+ nsString(), trackSource);
}
if (HasVideo()) {
TrackID videoTrackId = mMediaInfo.mVideo.mTrackId;
- out->mStream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString());
+ RefPtr<MediaStreamTrackSource> trackSource = new BasicUnstoppableTrackSource();
+ out->mStream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO,
+ nsString(), trackSource);
}
}
}
RefPtr<DOMMediaStream> result = out->mStream;
return result.forget();
}
already_AddRefed<DOMMediaStream>
--- a/dom/media/AudioStreamTrack.h
+++ b/dom/media/AudioStreamTrack.h
@@ -9,18 +9,19 @@
#include "MediaStreamTrack.h"
#include "DOMMediaStream.h"
namespace mozilla {
namespace dom {
class AudioStreamTrack : public MediaStreamTrack {
public:
- AudioStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel)
- : MediaStreamTrack(aStream, aTrackID, aLabel) {}
+ AudioStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
+ const nsString& aLabel, MediaStreamTrackSource* aSource)
+ : MediaStreamTrack(aStream, aTrackID, aLabel, aSource) {}
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
AudioStreamTrack* AsAudioStreamTrack() override { return this; }
// WebIDL
void GetKind(nsAString& aKind) override { aKind.AssignLiteral("audio"); }
};
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -123,25 +123,26 @@ public:
MOZ_ASSERT(NS_IsMainThread());
if (!mStream) {
return;
}
MediaStreamTrack* track = mStream->FindOwnedDOMTrack(
mStream->GetOwnedStream(), aTrackId);
- if (track) {
- // This track has already been manually created. Abort.
- return;
+ if (!track) {
+ // Track had not been created on main thread before, create it now.
+ NS_WARN_IF_FALSE(!mStream->mTracks.IsEmpty(),
+ "A new track was detected on the input stream; creating "
+ "a corresponding MediaStreamTrack. Initial tracks "
+ "should be added manually to immediately and "
+ "synchronously be available to JS.");
+ track = mStream->CreateOwnDOMTrack(aTrackId, aType, nsString(),
+ new BasicUnstoppableTrackSource());
}
-
- NS_WARN_IF_FALSE(!mStream->mTracks.IsEmpty(),
- "A new track was detected on the input stream; creating a corresponding MediaStreamTrack. "
- "Initial tracks should be added manually to immediately and synchronously be available to JS.");
- mStream->CreateOwnDOMTrack(aTrackId, aType, nsString());
}
void DoNotifyTrackEnded(TrackID aTrackId)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mStream) {
return;
@@ -612,22 +613,25 @@ DOMMediaStream::InitTrackUnionStream(Med
InitPlaybackStreamCommon(aGraph);
}
void
DOMMediaStream::InitAudioCaptureStream(MediaStreamGraph* aGraph)
{
const TrackID AUDIO_TRACK = 1;
+ RefPtr<BasicUnstoppableTrackSource> audioCaptureSource =
+ new BasicUnstoppableTrackSource(MediaSourceEnum::AudioCapture);
+
AudioCaptureStream* audioCaptureStream =
static_cast<AudioCaptureStream*>(aGraph->CreateAudioCaptureStream(this, AUDIO_TRACK));
InitInputStreamCommon(audioCaptureStream, aGraph);
InitOwnedStreamCommon(aGraph);
InitPlaybackStreamCommon(aGraph);
- CreateOwnDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, nsString());
+ CreateOwnDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, nsString(), audioCaptureSource);
audioCaptureStream->Start();
}
void
DOMMediaStream::InitInputStreamCommon(MediaStream* aStream,
MediaStreamGraph* aGraph)
{
MOZ_ASSERT(!mOwnedStream, "Input stream must be initialized before owned stream");
@@ -700,24 +704,16 @@ void
DOMMediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled)
{
// XXX Bug 1208371 - This enables/disables the track across clones.
if (mInputStream) {
mInputStream->SetTrackEnabled(aTrackID, aEnabled);
}
}
-void
-DOMMediaStream::StopTrack(TrackID aTrackID)
-{
- if (mInputStream && mInputStream->AsSourceStream()) {
- mInputStream->AsSourceStream()->EndTrack(aTrackID);
- }
-}
-
already_AddRefed<Promise>
DOMMediaStream::ApplyConstraintsToTrack(TrackID aTrackID,
const MediaTrackConstraints& aConstraints,
ErrorResult &aRv)
{
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
RefPtr<Promise> promise = Promise::Create(go, aRv);
MOZ_RELEASE_ASSERT(!aRv.Failed());
@@ -779,30 +775,32 @@ DOMMediaStream::AddPrincipalChangeObserv
bool
DOMMediaStream::RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver)
{
return mPrincipalChangeObservers.RemoveElement(aObserver);
}
MediaStreamTrack*
-DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, const nsString& aLabel)
+DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType,
+ const nsString& aLabel,
+ MediaStreamTrackSource* aSource)
{
MOZ_RELEASE_ASSERT(mInputStream);
MOZ_RELEASE_ASSERT(mOwnedStream);
MOZ_ASSERT(FindOwnedDOMTrack(GetOwnedStream(), aTrackID) == nullptr);
MediaStreamTrack* track;
switch (aType) {
case MediaSegment::AUDIO:
- track = new AudioStreamTrack(this, aTrackID, aLabel);
+ track = new AudioStreamTrack(this, aTrackID, aLabel, aSource);
break;
case MediaSegment::VIDEO:
- track = new VideoStreamTrack(this, aTrackID, aLabel);
+ track = new VideoStreamTrack(this, aTrackID, aLabel, aSource);
break;
default:
MOZ_CRASH("Unhandled track type");
}
LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p with ID %u", this, track, aTrackID));
RefPtr<TrackPort> ownedTrackPort =
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -30,27 +30,27 @@
#endif
namespace mozilla {
class DOMHwMediaStream;
class DOMLocalMediaStream;
class DOMMediaStream;
class MediaStream;
-class MediaEngineSource;
class MediaInputPort;
class MediaStreamGraph;
class ProcessedMediaStream;
namespace dom {
class AudioNode;
class HTMLCanvasElement;
class MediaStreamTrack;
class AudioStreamTrack;
class VideoStreamTrack;
+class MediaStreamTrackSource;
class AudioTrack;
class VideoTrack;
class AudioTrackList;
class VideoTrackList;
class MediaTrackListListener;
struct MediaTrackConstraints;
} // namespace dom
@@ -179,16 +179,17 @@ public:
* (pointing to t2 in A')
*/
class DOMMediaStream : public DOMEventTargetHelper
{
friend class DOMLocalMediaStream;
typedef dom::MediaStreamTrack MediaStreamTrack;
typedef dom::AudioStreamTrack AudioStreamTrack;
typedef dom::VideoStreamTrack VideoStreamTrack;
+ typedef dom::MediaStreamTrackSource MediaStreamTrackSource;
typedef dom::AudioTrack AudioTrack;
typedef dom::VideoTrack VideoTrack;
typedef dom::AudioTrackList AudioTrackList;
typedef dom::VideoTrackList VideoTrackList;
public:
typedef dom::MediaTrackConstraints MediaTrackConstraints;
@@ -376,18 +377,16 @@ public:
virtual void RemoveDirectListener(MediaStreamDirectListener *aListener) {}
/**
* Overridden in DOMLocalMediaStreams to allow getUserMedia to disable
* media at the SourceMediaStream.
*/
virtual void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
- virtual void StopTrack(TrackID aTrackID);
-
virtual already_AddRefed<dom::Promise>
ApplyConstraintsToTrack(TrackID aTrackID,
const MediaTrackConstraints& aConstraints,
ErrorResult &aRv);
virtual DOMLocalMediaStream* AsDOMLocalMediaStream() { return nullptr; }
virtual DOMHwMediaStream* AsDOMHwMediaStream() { return nullptr; }
@@ -478,17 +477,19 @@ public:
}
/**
* Called for each track in our owned stream to indicate to JS that we
* are carrying that track.
*
* Creates a MediaStreamTrack, adds it to mTracks and returns it.
*/
- MediaStreamTrack* CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, const nsString& aLabel);
+ MediaStreamTrack* CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType,
+ const nsString& aLabel,
+ MediaStreamTrackSource* aSource);
// When the initial set of tracks has been added, run
// aCallback->NotifyTracksAvailable.
// It is allowed to do anything, including run script.
// aCallback may run immediately during this call if tracks are already
// available!
// We only care about track additions, we'll fire the notification even if
// some of the tracks have been removed.
@@ -636,18 +637,16 @@ public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOMLOCALMEDIASTREAM_IID)
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void Stop();
- virtual MediaEngineSource* GetMediaEngine(TrackID aTrackID) { return nullptr; }
-
/**
* Create an nsDOMLocalMediaStream whose underlying stream is a SourceMediaStream.
*/
static already_AddRefed<DOMLocalMediaStream>
CreateSourceStream(nsPIDOMWindowInner* aWindow,
MediaStreamGraph* aGraph);
/**
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -136,16 +136,17 @@ GetMediaManagerLog()
using dom::ConstrainDOMStringParameters;
using dom::File;
using dom::GetUserMediaRequest;
using dom::MediaSourceEnum;
using dom::MediaStreamConstraints;
using dom::MediaStreamError;
using dom::MediaStreamTrack;
+using dom::MediaStreamTrackSource;
using dom::MediaTrackConstraints;
using dom::MediaTrackConstraintSet;
using dom::OwningBooleanOrMediaTrackConstraints;
using dom::OwningStringOrStringSequence;
using dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters;
using dom::Promise;
using dom::Sequence;
using media::NewRunnableFrom;
@@ -661,67 +662,39 @@ nsresult AudioDevice::Restart(const dom:
* that need to be cleaned up.
*/
class nsDOMUserMediaStream : public DOMLocalMediaStream
{
public:
static already_AddRefed<nsDOMUserMediaStream>
CreateSourceStream(nsPIDOMWindowInner* aWindow,
GetUserMediaCallbackMediaStreamListener* aListener,
- AudioDevice* aAudioDevice,
- VideoDevice* aVideoDevice,
MediaStreamGraph* aMSG)
{
RefPtr<nsDOMUserMediaStream> stream = new nsDOMUserMediaStream(aWindow,
- aListener,
- aAudioDevice,
- aVideoDevice);
+ aListener);
stream->InitSourceStream(aMSG);
return stream.forget();
}
nsDOMUserMediaStream(nsPIDOMWindowInner* aWindow,
- GetUserMediaCallbackMediaStreamListener* aListener,
- AudioDevice *aAudioDevice,
- VideoDevice *aVideoDevice) :
+ GetUserMediaCallbackMediaStreamListener* aListener) :
DOMLocalMediaStream(aWindow),
- mListener(aListener),
- mAudioDevice(aAudioDevice),
- mVideoDevice(aVideoDevice)
+ mListener(aListener)
{}
virtual ~nsDOMUserMediaStream()
{
StopImpl();
if (GetSourceStream()) {
GetSourceStream()->Destroy();
}
}
- // For gUM streams, we have a trackunion which assigns TrackIDs. However, for a
- // single-source trackunion like we have here, the TrackUnion will assign trackids
- // that match the source's trackids, so we can avoid needing a mapping function.
- // XXX This will not handle more complex cases well.
- void StopTrack(TrackID aTrackID) override
- {
- if (GetSourceStream()) {
- GetSourceStream()->EndTrack(aTrackID);
- // We could override NotifyMediaStreamTrackEnded(), and maybe should, but it's
- // risky to do late in a release since that will affect all track ends, and not
- // just StopTrack()s.
- RefPtr<dom::MediaStreamTrack> ownedTrack = FindOwnedDOMTrack(mOwnedStream, aTrackID);
- if (ownedTrack) {
- mListener->StopTrack(aTrackID);
- } else {
- LOG(("StopTrack(%d) on non-existent track", aTrackID));
- }
- }
- }
-
already_AddRefed<Promise>
ApplyConstraintsToTrack(TrackID aTrackID,
const MediaTrackConstraints& aConstraints,
ErrorResult &aRv) override
{
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
RefPtr<Promise> promise = Promise::Create(go, aRv);
@@ -779,41 +752,25 @@ public:
}
}
DOMLocalMediaStream* AsDOMLocalMediaStream() override
{
return this;
}
- MediaEngineSource* GetMediaEngine(TrackID aTrackID) override
- {
- // MediaEngine supports only one video and on video track now and TrackID is
- // fixed in MediaEngine.
- if (aTrackID == kVideoTrack) {
- return mVideoDevice ? mVideoDevice->GetSource() : nullptr;
- }
- else if (aTrackID == kAudioTrack) {
- return mAudioDevice ? mAudioDevice->GetSource() : nullptr;
- }
-
- return nullptr;
- }
-
SourceMediaStream* GetSourceStream()
{
if (GetInputStream()) {
return GetInputStream()->AsSourceStream();
}
return nullptr;
}
RefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
- RefPtr<AudioDevice> mAudioDevice; // so we can turn on AEC
- RefPtr<VideoDevice> mVideoDevice;
};
void
MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog)
{
MM_LOG(("%s , rv=%d", errorLog, rv));
NS_DispatchToMainThread(do_AddRef(new ReleaseMediaOperationResource(mStream.forget(),
@@ -944,31 +901,70 @@ public:
// It should be possible to pipe the capture stream to anything. CORS is
// not a problem here, we got explicit user content.
domStream->SetPrincipal(window->GetExtantDoc()->NodePrincipal());
stream = msg->CreateSourceStream(nullptr); // Placeholder
msg->RegisterCaptureStreamForWindow(
mWindowID, domStream->GetInputStream()->AsProcessedStream());
window->SetAudioCapture(true);
} else {
+ class LocalTrackSource : public MediaStreamTrackSource
+ {
+ public:
+ LocalTrackSource(GetUserMediaCallbackMediaStreamListener* aListener,
+ const MediaSourceEnum aSource,
+ const TrackID aTrackID)
+ : MediaStreamTrackSource(false), mListener(aListener),
+ mSource(aSource), mTrackID(aTrackID) {}
+
+ MediaSourceEnum GetMediaSource() const override
+ {
+ return mSource;
+ }
+
+ void Stop() override
+ {
+ if (mListener) {
+ mListener->StopTrack(mTrackID);
+ mListener = nullptr;
+ }
+ }
+
+ protected:
+ ~LocalTrackSource() {}
+
+ RefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
+ const MediaSourceEnum mSource;
+ const TrackID mTrackID;
+ };
+
// Normal case, connect the source stream to the track union stream to
// avoid us blocking
domStream = nsDOMUserMediaStream::CreateSourceStream(window, mListener,
- mAudioDevice, mVideoDevice,
msg);
if (mAudioDevice) {
nsString audioDeviceName;
mAudioDevice->GetName(audioDeviceName);
- domStream->CreateOwnDOMTrack(kAudioTrack, MediaSegment::AUDIO, audioDeviceName);
+ const MediaSourceEnum source =
+ mAudioDevice->GetSource()->GetMediaSource();
+ RefPtr<MediaStreamTrackSource> audioSource =
+ new LocalTrackSource(mListener, source, kAudioTrack);
+ domStream->CreateOwnDOMTrack(kAudioTrack, MediaSegment::AUDIO,
+ audioDeviceName, audioSource);
}
if (mVideoDevice) {
nsString videoDeviceName;
mVideoDevice->GetName(videoDeviceName);
- domStream->CreateOwnDOMTrack(kVideoTrack, MediaSegment::VIDEO, videoDeviceName);
+ const MediaSourceEnum source =
+ mVideoDevice->GetSource()->GetMediaSource();
+ RefPtr<MediaStreamTrackSource> videoSource =
+ new LocalTrackSource(mListener, source, kVideoTrack);
+ domStream->CreateOwnDOMTrack(kVideoTrack, MediaSegment::VIDEO,
+ videoDeviceName, videoSource);
}
nsCOMPtr<nsIPrincipal> principal;
if (mPeerIdentity) {
principal = nsNullPrincipal::Create();
domStream->SetPeerIdentity(mPeerIdentity.forget());
} else {
principal = window->GetExtantDoc()->NodePrincipal();
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -14,24 +14,37 @@
#endif
static PRLogModuleInfo* gMediaStreamTrackLog;
#define LOG(type, msg) MOZ_LOG(gMediaStreamTrackLog, type, msg)
namespace mozilla {
namespace dom {
-MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel)
- : mOwningStream(aStream), mTrackID(aTrackID), mLabel(aLabel), mEnded(false), mEnabled(true)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSource)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSource)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSource)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+NS_IMPL_CYCLE_COLLECTION_0(MediaStreamTrackSource)
+
+MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
+ const nsString& aLabel,
+ MediaStreamTrackSource* aSource)
+ : mOwningStream(aStream), mTrackID(aTrackID), mLabel(aLabel), mSource(aSource),
+ mEnded(false), mEnabled(true), mRemote(aSource->IsRemote()), mStopped(false)
{
if (!gMediaStreamTrackLog) {
gMediaStreamTrackLog = PR_NewLogModule("MediaStreamTrack");
}
+ MOZ_RELEASE_ASSERT(mSource);
+ mSource->RegisterSink();
+
nsresult rv;
nsCOMPtr<nsIUUIDGenerator> uuidgen =
do_GetService("@mozilla.org/uuid-generator;1", &rv);
nsID uuid;
memset(&uuid, 0, sizeof(uuid));
if (uuidgen) {
uuidgen->GenerateUUIDInPlace(&uuid);
@@ -41,18 +54,32 @@ MediaStreamTrack::MediaStreamTrack(DOMMe
uuid.ToProvidedString(chars);
mID = NS_ConvertASCIItoUTF16(chars);
}
MediaStreamTrack::~MediaStreamTrack()
{
}
-NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaStreamTrack, DOMEventTargetHelper,
- mOwningStream, mOriginalTrack)
+NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrack)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaStreamTrack,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwningStream)
+ tmp->mSource->UnregisterSink();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalTrack)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamTrack,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwningStream)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalTrack)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamTrack)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
void
MediaStreamTrack::GetId(nsAString& aID) const
@@ -70,17 +97,33 @@ MediaStreamTrack::SetEnabled(bool aEnabl
mOwningStream->SetTrackEnabled(mTrackID, aEnabled);
}
void
MediaStreamTrack::Stop()
{
LOG(LogLevel::Info, ("MediaStreamTrack %p Stop()", this));
- mOwningStream->StopTrack(mTrackID);
+ if (mStopped) {
+ LOG(LogLevel::Warning, ("MediaStreamTrack %p Already stopped", this));
+ return;
+ }
+
+ if (mRemote) {
+ LOG(LogLevel::Warning, ("MediaStreamTrack %p is remote. Can't be stopped.", this));
+ return;
+ }
+
+ if (!mSource) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ mSource->UnregisterSink();
+ mStopped = true;
}
already_AddRefed<Promise>
MediaStreamTrack::ApplyConstraints(const MediaTrackConstraints& aConstraints,
ErrorResult &aRv)
{
if (MOZ_LOG_TEST(gMediaStreamTrackLog, LogLevel::Info)) {
nsString str;
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -2,39 +2,149 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MEDIASTREAMTRACK_H_
#define MEDIASTREAMTRACK_H_
#include "mozilla/DOMEventTargetHelper.h"
+#include "nsError.h"
#include "nsID.h"
#include "StreamBuffer.h"
#include "MediaTrackConstraints.h"
namespace mozilla {
class DOMMediaStream;
+class MediaEnginePhotoCallback;
namespace dom {
class AudioStreamTrack;
class VideoStreamTrack;
/**
+ * Common interface through which a MediaStreamTrack can communicate with its
+ * producer on the main thread.
+ *
+ * Kept alive by a strong ref in all MediaStreamTracks (original and clones)
+ * sharing this source.
+ */
+class MediaStreamTrackSource : public nsISupports
+{
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource)
+
+public:
+ explicit MediaStreamTrackSource(const bool aIsRemote)
+ : mNrSinks(0), mIsRemote(aIsRemote), mStopped(false)
+ {
+ MOZ_COUNT_CTOR(MediaStreamTrackSource);
+ }
+
+ /**
+ * Gets the source's MediaSourceEnum for usage by PeerConnections.
+ */
+ virtual MediaSourceEnum GetMediaSource() const = 0;
+
+ /**
+ * Indicates whether the track is remote or not per the MediaCapture and
+ * Streams spec.
+ */
+ virtual bool IsRemote() const { return mIsRemote; }
+
+ /**
+ * Forwards a photo request to backends that support it. Other backends return
+ * NS_ERROR_NOT_IMPLEMENTED to indicate that a MediaStreamGraph-based fallback
+ * should be used.
+ */
+ virtual nsresult TakePhoto(MediaEnginePhotoCallback*) const { return NS_ERROR_NOT_IMPLEMENTED; }
+
+ /**
+ * Called by the source interface when all registered sinks have unregistered.
+ */
+ virtual void Stop() = 0;
+
+ /**
+ * Called by each MediaStreamTrack clone on initialization.
+ */
+ void RegisterSink()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mStopped) {
+ return;
+ }
+ ++mNrSinks;
+ }
+
+ /**
+ * Called by each MediaStreamTrack clone on track.Stop().
+ */
+ void UnregisterSink()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_ASSERTION(mNrSinks > 0, "Unmatched UnregisterSink()");
+ --mNrSinks;
+ if (mNrSinks == 0 && !IsRemote()) {
+ Stop();
+ mStopped = true;
+ }
+ }
+
+protected:
+ virtual ~MediaStreamTrackSource()
+ {
+ MOZ_COUNT_DTOR(MediaStreamTrackSource);
+ NS_ASSERTION(mNrSinks == 0, "Some sinks did not unregister");
+ }
+
+ // Number of currently registered sinks.
+ size_t mNrSinks;
+
+ // True if this is a remote track source, i.e., a PeerConnection.
+ const bool mIsRemote;
+
+ // True if this source is not remote, all MediaStreamTrack users have
+ // unregistered from this source and Stop() has been called.
+ bool mStopped;
+};
+
+/**
+ * Basic implementation of MediaStreamTrackSource that ignores Stop().
+ */
+class BasicUnstoppableTrackSource : public MediaStreamTrackSource
+{
+public:
+ explicit BasicUnstoppableTrackSource(const MediaSourceEnum aMediaSource =
+ MediaSourceEnum::Other)
+ : MediaStreamTrackSource(true), mMediaSource(aMediaSource) {}
+
+ MediaSourceEnum GetMediaSource() const override { return mMediaSource; }
+
+ void Stop() override {}
+
+protected:
+ ~BasicUnstoppableTrackSource() {}
+
+ const MediaSourceEnum mMediaSource;
+};
+
+/**
* Class representing a track in a DOMMediaStream.
*/
class MediaStreamTrack : public DOMEventTargetHelper {
public:
/**
* aTrackID is the MediaStreamGraph track ID for the track in the
* MediaStream owned by aStream.
*/
- MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel);
+ MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
+ const nsString& aLabel,
+ MediaStreamTrackSource* aSource);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamTrack,
DOMEventTargetHelper)
DOMMediaStream* GetParentObject() const { return mOwningStream; }
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0;
@@ -60,28 +170,38 @@ public:
void Stop();
already_AddRefed<Promise>
ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
bool Ended() const { return mEnded; }
// Notifications from the MediaStreamGraph
void NotifyEnded() { mEnded = true; }
+ MediaStreamTrackSource& GetSource() const
+ {
+ MOZ_RELEASE_ASSERT(mSource, "The track source is only removed on destruction");
+ return *mSource;
+ }
+
// Webrtc allows the remote side to name tracks whatever it wants, and we
// need to surface this to content.
void AssignId(const nsAString& aID) { mID = aID; }
protected:
virtual ~MediaStreamTrack();
RefPtr<DOMMediaStream> mOwningStream;
TrackID mTrackID;
+ TrackID mInputTrackID;
+ RefPtr<MediaStreamTrackSource> mSource;
RefPtr<MediaStreamTrack> mOriginalTrack;
nsString mID;
nsString mLabel;
bool mEnded;
bool mEnabled;
+ const bool mRemote;
+ bool mStopped;
};
} // namespace dom
} // namespace mozilla
#endif /* MEDIASTREAMTRACK_H_ */
--- a/dom/media/VideoStreamTrack.h
+++ b/dom/media/VideoStreamTrack.h
@@ -9,18 +9,20 @@
#include "MediaStreamTrack.h"
#include "DOMMediaStream.h"
namespace mozilla {
namespace dom {
class VideoStreamTrack : public MediaStreamTrack {
public:
- VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel)
- : MediaStreamTrack(aStream, aTrackID, aLabel) {}
+ VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
+ const nsString& aLabel,
+ MediaStreamTrackSource* aSource)
+ : MediaStreamTrack(aStream, aTrackID, aLabel, aSource) {}
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
VideoStreamTrack* AsVideoStreamTrack() override { return this; }
// WebIDL
void GetKind(nsAString& aKind) override { aKind.AssignLiteral("video"); }
};
--- a/dom/media/imagecapture/ImageCapture.cpp
+++ b/dom/media/imagecapture/ImageCapture.cpp
@@ -76,23 +76,24 @@ nsresult
ImageCapture::TakePhotoByMediaEngine()
{
// Callback for TakPhoto(), it also monitor the principal. If principal
// changes, it returns PHOTO_ERROR with security error.
class TakePhotoCallback : public MediaEnginePhotoCallback,
public DOMMediaStream::PrincipalChangeObserver
{
public:
- TakePhotoCallback(DOMMediaStream* aStream, ImageCapture* aImageCapture)
- : mStream(aStream)
+ TakePhotoCallback(VideoStreamTrack* aVideoTrack, ImageCapture* aImageCapture)
+ : mVideoTrack(aVideoTrack)
, mImageCapture(aImageCapture)
, mPrincipalChanged(false)
{
MOZ_ASSERT(NS_IsMainThread());
- mStream->AddPrincipalChangeObserver(this);
+ MOZ_RELEASE_ASSERT(mVideoTrack->GetStream());
+ mVideoTrack->GetStream()->AddPrincipalChangeObserver(this);
}
void PrincipalChanged(DOMMediaStream* aMediaStream) override
{
mPrincipalChanged = true;
}
nsresult PhotoComplete(already_AddRefed<Blob> aBlob) override
@@ -109,35 +110,28 @@ ImageCapture::TakePhotoByMediaEngine()
{
return mImageCapture->PostErrorEvent(ImageCaptureError::PHOTO_ERROR, aRv);
}
protected:
~TakePhotoCallback()
{
MOZ_ASSERT(NS_IsMainThread());
- mStream->RemovePrincipalChangeObserver(this);
+ MOZ_RELEASE_ASSERT(mVideoTrack->GetStream());
+ mVideoTrack->GetStream()->RemovePrincipalChangeObserver(this);
}
- RefPtr<DOMMediaStream> mStream;
+ RefPtr<VideoStreamTrack> mVideoTrack;
RefPtr<ImageCapture> mImageCapture;
bool mPrincipalChanged;
};
- RefPtr<DOMMediaStream> domStream = mVideoStreamTrack->GetStream();
- DOMLocalMediaStream* domLocalStream = domStream->AsDOMLocalMediaStream();
- if (domLocalStream) {
- RefPtr<MediaEngineSource> mediaEngine =
- domLocalStream->GetMediaEngine(mVideoStreamTrack->GetTrackID());
- RefPtr<MediaEnginePhotoCallback> callback =
- new TakePhotoCallback(domStream, this);
- return mediaEngine->TakePhoto(callback);
- }
-
- return NS_ERROR_NOT_IMPLEMENTED;
+ RefPtr<MediaEnginePhotoCallback> callback =
+ new TakePhotoCallback(mVideoStreamTrack, this);
+ return mVideoStreamTrack->GetSource().TakePhoto(callback);
}
void
ImageCapture::TakePhoto(ErrorResult& aResult)
{
// According to spec, VideoStreamTrack.readyState must be "live"; however
// gecko doesn't implement it yet (bug 910249). Instead of readyState, we
// check VideoStreamTrack.enable before bug 910249 is fixed.
--- a/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp
+++ b/dom/media/webaudio/MediaStreamAudioDestinationNode.cpp
@@ -5,16 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaStreamAudioDestinationNode.h"
#include "nsIDocument.h"
#include "mozilla/dom/MediaStreamAudioDestinationNodeBinding.h"
#include "AudioNodeEngine.h"
#include "AudioNodeStream.h"
#include "DOMMediaStream.h"
+#include "MediaStreamTrack.h"
#include "TrackUnionStream.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaStreamAudioDestinationNode, AudioNode, mDOMStream)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamAudioDestinationNode)
@@ -29,17 +30,20 @@ MediaStreamAudioDestinationNode::MediaSt
ChannelCountMode::Explicit,
ChannelInterpretation::Speakers)
, mDOMStream(
DOMAudioNodeMediaStream::CreateTrackUnionStream(GetOwner(),
this,
aContext->Graph()))
{
// Ensure an audio track with the correct ID is exposed to JS
- mDOMStream->CreateOwnDOMTrack(AudioNodeStream::AUDIO_TRACK, MediaSegment::AUDIO, nsString());
+ RefPtr<MediaStreamTrackSource> source =
+ new BasicUnstoppableTrackSource(MediaSourceEnum::AudioCapture);
+ mDOMStream->CreateOwnDOMTrack(AudioNodeStream::AUDIO_TRACK,
+ MediaSegment::AUDIO, nsString(), source);
ProcessedMediaStream* outputStream = mDOMStream->GetInputStream()->AsProcessedStream();
MOZ_ASSERT(!!outputStream);
AudioNodeEngine* engine = new AudioNodeEngine(this);
mStream = AudioNodeStream::Create(aContext, engine,
AudioNodeStream::EXTERNAL_OUTPUT);
mPort = outputStream->AllocateInputPort(mStream, AudioNodeStream::AUDIO_TRACK);
--- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
@@ -844,20 +844,17 @@ MediaPipelineFactory::ConfigureVideoCode
RefPtr<DOMMediaStream> mediastream =
mPCMedia->GetLocalStreamById(aTrack.GetStreamId())->GetMediaStream();
DOMLocalMediaStream* domLocalStream = mediastream->AsDOMLocalMediaStream();
if (!domLocalStream) {
return NS_OK;
}
- MediaEngineSource *engine =
- domLocalStream->GetMediaEngine(videotrack->GetTrackID());
-
- dom::MediaSourceEnum source = engine->GetMediaSource();
+ dom::MediaSourceEnum source = videotrack->GetSource().GetMediaSource();
webrtc::VideoCodecMode mode = webrtc::kRealtimeVideo;
switch (source) {
case dom::MediaSourceEnum::Browser:
case dom::MediaSourceEnum::Screen:
case dom::MediaSourceEnum::Application:
case dom::MediaSourceEnum::Window:
mode = webrtc::kScreensharing;
break;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1874,16 +1874,29 @@ PeerConnectionImpl::SetRemoteDescription
++numNewAudioTracks;
} else if (track->GetMediaType() == SdpMediaSection::kVideo) {
++numNewVideoTracks;
} else {
MOZ_ASSERT(false);
continue;
}
info->AddTrack(track->GetTrackId());
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ RefPtr<MediaStreamTrackSource> source =
+ new BasicUnstoppableTrackSource(MediaSourceEnum::Other);
+ if (track->GetMediaType() == SdpMediaSection::kAudio) {
+ info->GetMediaStream()->CreateOwnDOMTrack(
+ info->GetNumericTrackId(track->GetTrackId()),
+ MediaSegment::AUDIO, nsString(), source);
+ } else {
+ info->GetMediaStream()->CreateOwnDOMTrack(
+ info->GetNumericTrackId(track->GetTrackId()),
+ MediaSegment::VIDEO, nsString(), source);
+ }
+#endif
CSFLogDebug(logTag, "Added remote track %s/%s",
info->GetId().c_str(), track->GetTrackId().c_str());
} else {
++numPreexistingTrackIds;
}
}
// Now that the streams are all set up, notify about track availability.
--- a/media/webrtc/signaling/test/FakeMediaStreams.h
+++ b/media/webrtc/signaling/test/FakeMediaStreams.h
@@ -281,16 +281,24 @@ class Fake_SourceMediaStream : public Fa
bool mStop;
RefPtr<Fake_MediaPeriodic> mPeriodic;
RefPtr<Fake_VideoSink> mSink;
nsCOMPtr<nsITimer> mTimer;
};
class Fake_DOMMediaStream;
+class Fake_MediaStreamTrackSource
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Fake_MediaStreamTrackSource)
+
+protected:
+ virtual ~Fake_MediaStreamTrackSource() {}
+};
+
class Fake_MediaStreamTrack
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Fake_MediaStreamTrack)
Fake_MediaStreamTrack(bool aIsVideo, Fake_DOMMediaStream* aStream) :
mIsVideo (aIsVideo),
mStream (aStream)
@@ -418,17 +426,18 @@ public:
OwnsTrack(const Fake_MediaStreamTrack& aTrack) const
{
return HasTrack(aTrack);
}
void SetTrackEnabled(mozilla::TrackID aTrackID, bool aEnabled) {}
Fake_MediaStreamTrack*
- CreateOwnDOMTrack(mozilla::TrackID aTrackID, mozilla::MediaSegment::Type aType)
+ CreateOwnDOMTrack(mozilla::TrackID aTrackID, mozilla::MediaSegment::Type aType,
+ const nsString& aLabel, Fake_MediaStreamTrackSource* aSource)
{
switch(aType) {
case mozilla::MediaSegment::AUDIO: {
return mAudioTrack;
}
case mozilla::MediaSegment::VIDEO: {
return mVideoTrack;
}
@@ -498,11 +507,15 @@ class Fake_VideoStreamSource : public Fa
namespace mozilla {
typedef Fake_MediaStream MediaStream;
typedef Fake_SourceMediaStream SourceMediaStream;
typedef Fake_MediaStreamListener MediaStreamListener;
typedef Fake_MediaStreamDirectListener MediaStreamDirectListener;
typedef Fake_DOMMediaStream DOMMediaStream;
typedef Fake_DOMMediaStream DOMLocalMediaStream;
+
+namespace dom {
+typedef Fake_MediaStreamTrackSource MediaStreamTrackSource;
+}
}
#endif