Files
palemoon27/dom/media/DOMMediaStream.cpp
T
roytam1 8cdf8ee29c import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1170958 - Feed a SourceMediaStream-backed dom stream instead of a raw SourceMediaStream in MediaManager. r=jesup (8670ff2711)
- Bug 1103188 - Remove identical override nsDOMUserMediaStream::Stop(). r=jib (54831f9b18)
- Bug 1103188 - Deprecate DOMMediaStream::Stop(). r=jib (36112afe82)
- Bug 1186813 - Replace nsBaseHashtable::EnumerateRead() calls in dom/media/ with iterators r=cpearce (cd0c4a34e8)
- Bug 1190337 - Log GPS status and SVs status if the 'gDebug_isLoggingEnabled' is true. r=garvank (c269f6f31d)
- Bug 1154435 - [Stumbler] FxOS Geo Stumbling for Mozilla Location Service. r=jdm (1a86f4dda5)
- Bug 1199395 - FxOS Stumbling gzip the stumbles to store more data. r=jdm (4d108665d9)
- Bug 1175860 - Add some documentation to UploadLastDir to make its workings clearer. r=baku (cdac9a7849)
- Bug 1210517 - Create nsVariant directly rather than via do_CreateInstance(). r=froydnj (df420cba8e)
- Bug 953265: make getUserMedia fake audio tones configurable in frequency via pref r=jib (67793ee005)
- Bug 1166293 - Use AsyncShutdown API to shut down media thread in non-e10s. r= jesup (1245d20b7e)
- Bug 1103188 - MediaStream WebIDL update with addTrack/removeTrack. r=smaug,jib (697791fd6f)
- Bug 1103188 - MediaStream::AddTrack/RemoveTrack implementation. r=roc (c8b02beb45)
- Bug 1170958 - Improve logging of MediaStreams and playback. r=roc (5fcb40437e)
- Bug 1170958 - Add DOMMediaStream::OwnedStreamListener. r=roc (afff077f93)
- Bug 1103188 - Break out MediaTrackListListener to an interface. r=roc (298b665f27)
- Bug 1198435 - Call RemoveMediaElementFromURITable before modifying mLoadingSrc, so that a future LookupMediaElementURITable won't access this element anymore. r=rillian (f2805c8dba)
- Bug 1141875 - Add flag to init gl_Position. - r=kamidphish (eeb333c02b)
- Bug 1128044 - Enforce packing restrictions for varyings. - r=kamidphish (17b9596a3d)
- Bug 1128044 - Only pack varyings that have static use in both shaders. - r=warnings-as-errors (f41708642a)
- Bug 1128044 - Use nsTArray since android doesn't support std::vector::data(). - r=bustage (be88a80844)
- Bug 1128044 - nsTArray::AppendElement doesn't accept init lists. - r=bustage (cdeafa867b)
- bit of Bug 1019209 - Allow GL initialization without Android bridge (3dba5dffa2)
- some reporter (3049ad6f6d)
- Bug 1206030 - Remove nsIDOMHTMLCanvasElement::MozFetchAsStream() f=Ms2ger r=jst (95e773b79f)
- Bug 1187174 - Use 'webgl2' not 'experimental-webgl2'. - r=kamidphish (a6c21752fc)
- Bug 1190777 - Add null checks to prevent bad dereferences. r=kamidphish (f67f0125ce)
- Bug 709490 - Part 1: Let ImageBridge transfer CanvasClient async. r=nical (a46ac7e71c)
- Bug 1150762 - Add pref for activating all ANGLE options. - r=kamidphish (6ab4d39827)
- Bug 1195401 - Use gfxPrefs (threadsafe) rather than crashing on debug builds for off-main-thread pref access. r=snorp (0d29cea59c)
- Bug 709490 - Part 2: Introduce OffscreenCanvas and let WebGL context work on workers. r=nical, r=jgilbert, r=jrmuizel, sr=ehsan (842aaa8328)
- Bug 709490 - Part 3: Transfer OffscreenCanvas from mainthread to workers. r=baku, r=sfink (91c24b0e08)
- Bug 709490 - Part 4: Mochitests for offscreencanvas. r=baku, r=jgilbert (4c439fd376)
- Bug 1173544 - Add tests for Canvas CSS/SVG Filters. r=mstange (04c01f1c11)
- fix (9c7ab9d870)
- Bug 709490 - Part 5: Add interfaces test. r=ehsan (2993581c89)
- Bug 709490 - Part 6: Add frame ID to CanvasClient so compositor could update frame correctly. r=roc (3e6554af1e)
- Bug 709490 - Part 7: If layer is not available, fallback to BasicCanvasLayer. r=roc (c0c0d04468)
- Bug 709490 - Part 8: Copy to a temp texture when readback from IOSurface. r=jgilbert (d1a4879a39)
- Bug 709490 - Part 9: Readback without blocking main thread. r=jgilbert (2430c6e2a5)
- Bug 709490 - Part 10: Using mechanism in RuntimeService to get pref in worker thread instead of gfxPref. r=baku (85d6dc2744)
- Bug 709490 - Part 11: Diabled test_offscreencanvas_many.html on gonk, android, windows and linux. r=jgilbert (5cd8f28063)
- Bug 1212663 - Use doxygen style comments in jsapi, r=Waldo (0e67283edf)
- Bug 1000922 - Use nsMainThreadPtrHandle instead of already_AddRefed and forget for callbacks in NativeOSFileInternals.cpp r=jdm (4a128db7a6)
- Bug 1169740 - Implement a TDZ-like behavior for |this| in derived class constructors. (r=jandem, r=jorendorff, inputs on nit resoulution from Waldo) (6d7df317e3)
- Bug 1211949 - check for allocation failure. r=nbp (94b8aac5e3)
- Bug 1209497 - OOM-crash if a consistent object table is impossible. r=jandem (e8ded0c3cb)
- Bug 1141863 - Part 1: Make |this| object creation account for new.target. (r=jandem, r=jorendorff) (9b4ec25d47)
- Bug 1141863 - Part 2: Implement ES6 SuperCall. (r=jandem, r=jorendorff) (1bbd2ba712)
- Bug 1141863 - Followup: Clean up proxy get traps to handle new |this| creation semantics. (rs=Waldo) CLOSED TREE (e7cd48b43c)
- Bug 1141863 - Last followup fix for a couple jstest failures. r=orange in a CLOSED TREE (8a9cff881a)
- Bug 1141863 - Followfollowfollowup: Remove redundant assert causing rooting hazards. (r=Waldo over IRC) CLOSED TREE (338b64ca87)
- Bug 1141863 - Tests. (r=jorendorff) (3957511169)
- Bug 1105463 - Implement default constructors for ES6 class definitions. (r=jorendorff) (8ead7f33a5)
- Bug 1105463 - Follow up: Fix erroneous syntax test. (r=theSheriffMadeMeDoIt) (425e678cf2)
- Bug 1212794 - Remove decompile-body functionality. r=till (9b87e5c0e4)
- Bug 1214970 - Don't emit nullptr atoms for class expressions with default constructors. (r=Waldo) (80ae19d6dc)
- Bug 1215744 - Unnamed class expressions shouldn't get a name property. (r=arai) (0ce0a96be4)
- Bug 1208747 - Move most of Stopwatch-related code to XPCOM-land (JSAPI-level);r=jandem (e28fa2f859)
- Bug 1184486 - Let PerformanceStats.jsm play nicer with process-per-tab. r=mconley (f0cf0d0eae)
- Bug 1198167 - nsPerformanceStatsService should wait for profile-before-change, not profile-before-shutdown. r=yoric (5ba3c98109)
- Bug 1199603 - Don't wait for shutdown to update nsPerformanceStats Telemetry. r=Mossop (110813977b)
- Bug 1205154 - Use channel->Open2() in js/xpconnect/src/XPCJSRuntime.cpp (r=sicking) (8efd629889)
- Bug 1208747 - Move most of Stopwatch-related code to XPCOM-land (XPCOM-level + XPConnect-level);r=froydnj (a1b1e83549)
- with some fixes
2022-10-20 11:04:02 +08:00

1061 lines
30 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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/. */
#include "DOMMediaStream.h"
#include "nsContentUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsIScriptError.h"
#include "nsIUUIDGenerator.h"
#include "nsPIDOMWindow.h"
#include "mozilla/dom/MediaStreamBinding.h"
#include "mozilla/dom/LocalMediaStreamBinding.h"
#include "mozilla/dom/AudioNode.h"
#include "AudioChannelAgent.h"
#include "mozilla/dom/AudioTrack.h"
#include "mozilla/dom/AudioTrackList.h"
#include "mozilla/dom/VideoTrack.h"
#include "mozilla/dom/VideoTrackList.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "MediaStreamGraph.h"
#include "AudioStreamTrack.h"
#include "VideoStreamTrack.h"
#include "Layers.h"
#ifdef LOG
#undef LOG
#endif
static PRLogModuleInfo* gMediaStreamLog;
#define LOG(type, msg) MOZ_LOG(gMediaStreamLog, type, msg)
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::layers;
const TrackID TRACK_VIDEO_PRIMARY = 1;
/**
* 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.
*
* The input port can be shared between several TrackPorts. This is the case
* for DOMMediaStream's mPlaybackPort which forwards all tracks in its
* mOwnedStream automatically.
*
* If the MediaStreamTrack is owned by another DOMMediaStream (called A) than
* the one owning the TrackPort (called B), the input port (locked to the
* MediaStreamTrack's TrackID) connects A's mOwnedStream to B's mPlaybackStream.
*
* A TrackPort may never leave the DOMMediaStream it was created in. Internal
* use only.
*/
class DOMMediaStream::TrackPort
{
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(TrackPort)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(TrackPort)
enum class InputPortOwnership {
OWNED = 1,
EXTERNAL
};
TrackPort(MediaInputPort* aInputPort,
MediaStreamTrack* aTrack,
const InputPortOwnership aOwnership)
: mInputPort(aInputPort)
, mTrack(aTrack)
, mOwnership(aOwnership)
{
MOZ_ASSERT(mInputPort);
MOZ_ASSERT(mTrack);
MOZ_COUNT_CTOR(TrackPort);
}
protected:
virtual ~TrackPort()
{
MOZ_COUNT_DTOR(TrackPort);
if (mOwnership == InputPortOwnership::OWNED && mInputPort) {
mInputPort->Destroy();
mInputPort = nullptr;
}
}
public:
/**
* Returns the source stream of the input port.
*/
MediaStream* GetSource() const { return mInputPort ? mInputPort->GetSource()
: nullptr; }
/**
* Returns the track ID this track is locked to in the source stream of the
* input port.
*/
TrackID GetSourceTrackId() const { return mInputPort ? mInputPort->GetSourceTrackId()
: TRACK_INVALID; }
MediaInputPort* GetInputPort() const { return mInputPort; }
MediaStreamTrack* GetTrack() const { return mTrack; }
/**
* Blocks aTrackId from going into mInputPort unless the port has been
* destroyed.
*/
void BlockTrackId(TrackID aTrackId)
{
if (mInputPort) {
mInputPort->BlockTrackId(aTrackId);
}
}
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;
};
NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackPort, mTrack)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMediaStream::TrackPort, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMediaStream::TrackPort, Release)
/**
* Listener registered on the Owned stream to detect added and ended owned
* tracks for keeping the list of MediaStreamTracks in sync with the tracks
* added and ended directly at the source.
*/
class DOMMediaStream::OwnedStreamListener : public MediaStreamListener {
public:
explicit OwnedStreamListener(DOMMediaStream* aStream)
: mStream(aStream)
{}
void Forget() { mStream = nullptr; }
void DoNotifyTrackCreated(TrackID aTrackId, MediaSegment::Type aType)
{
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;
}
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);
}
void DoNotifyTrackEnded(TrackID aTrackId)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mStream) {
return;
}
RefPtr<MediaStreamTrack> track =
mStream->FindOwnedDOMTrack(mStream->GetOwnedStream(), aTrackId);
NS_ASSERTION(track, "Owned MediaStreamTracks must be known by the DOMMediaStream");
if (track) {
LOG(LogLevel::Debug, ("DOMMediaStream %p MediaStreamTrack %p ended at the source. Marking it ended.",
mStream, track.get()));
track->NotifyEnded();
}
}
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
StreamTime aTrackOffset, uint32_t aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override
{
if (aTrackEvents & TRACK_EVENT_CREATED) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethodWithArgs<TrackID, MediaSegment::Type>(
this, &OwnedStreamListener::DoNotifyTrackCreated,
aID, aQueuedMedia.GetType());
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
} else if (aTrackEvents & TRACK_EVENT_ENDED) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethodWithArgs<TrackID>(
this, &OwnedStreamListener::DoNotifyTrackEnded, aID);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
}
}
private:
// These fields may only be accessed on the main thread
DOMMediaStream* mStream;
};
/**
* Listener registered on the Playback stream to detect when tracks end and when
* all new tracks this iteration have been created - for when several tracks are
* queued by the source and committed all at once.
*/
class DOMMediaStream::PlaybackStreamListener : public MediaStreamListener {
public:
explicit PlaybackStreamListener(DOMMediaStream* aStream)
: mStream(aStream)
{}
void Forget()
{
MOZ_ASSERT(NS_IsMainThread());
mStream = nullptr;
}
void DoNotifyTrackEnded(MediaStream* aInputStream,
TrackID aInputTrackID)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mStream) {
return;
}
LOG(LogLevel::Debug, ("DOMMediaStream %p Track %u of stream %p ended",
mStream, aInputTrackID, aInputStream));
RefPtr<MediaStreamTrack> track =
mStream->FindPlaybackDOMTrack(aInputStream, aInputTrackID);
if (track) {
LOG(LogLevel::Debug, ("DOMMediaStream %p Playback track; notifying stream listeners.",
mStream));
mStream->NotifyTrackRemoved(track);
} else {
LOG(LogLevel::Debug, ("DOMMediaStream %p Not a playback track.", mStream));
}
}
void DoNotifyFinishedTrackCreation()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mStream) {
return;
}
mStream->NotifyTracksCreated();
}
// The methods below are called on the MediaStreamGraph thread.
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
StreamTime aTrackOffset, uint32_t aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override
{
if (aTrackEvents & TRACK_EVENT_ENDED) {
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethodWithArgs<StorensRefPtrPassByPtr<MediaStream>, TrackID>(
this, &PlaybackStreamListener::DoNotifyTrackEnded, aInputStream, aInputTrackID);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
}
}
void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override
{
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
}
private:
// These fields may only be accessed on the main thread
DOMMediaStream* mStream;
};
NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaStream)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream,
DOMEventTargetHelper)
tmp->Destroy();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwnedTracks)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwnedTracks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(DOMMediaStream, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMMediaStream)
NS_INTERFACE_MAP_ENTRY(DOMMediaStream)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(DOMLocalMediaStream, DOMMediaStream)
NS_IMPL_RELEASE_INHERITED(DOMLocalMediaStream, DOMMediaStream)
NS_INTERFACE_MAP_BEGIN(DOMLocalMediaStream)
NS_INTERFACE_MAP_ENTRY(DOMLocalMediaStream)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream,
mStreamNode)
NS_IMPL_ADDREF_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
DOMMediaStream::DOMMediaStream()
: mLogicalStreamStartTime(0), mInputStream(nullptr), mOwnedStream(nullptr),
mPlaybackStream(nullptr), mOwnedPort(nullptr), mPlaybackPort(nullptr),
mTracksCreated(false), mNotifiedOfMediaStreamGraphShutdown(false),
mCORSMode(CORS_NONE)
{
nsresult rv;
nsCOMPtr<nsIUUIDGenerator> uuidgen =
do_GetService("@mozilla.org/uuid-generator;1", &rv);
if (!gMediaStreamLog) {
gMediaStreamLog = PR_NewLogModule("MediaStream");
}
if (NS_SUCCEEDED(rv) && uuidgen) {
nsID uuid;
memset(&uuid, 0, sizeof(uuid));
rv = uuidgen->GenerateUUIDInPlace(&uuid);
if (NS_SUCCEEDED(rv)) {
char buffer[NSID_LENGTH];
uuid.ToProvidedString(buffer);
mID = NS_ConvertASCIItoUTF16(buffer);
}
}
}
DOMMediaStream::~DOMMediaStream()
{
Destroy();
}
void
DOMMediaStream::Destroy()
{
LOG(LogLevel::Debug, ("DOMMediaStream %p Being destroyed.", this));
if (mOwnedListener) {
mOwnedListener->Forget();
mOwnedListener = nullptr;
}
if (mPlaybackListener) {
mPlaybackListener->Forget();
mPlaybackListener = nullptr;
}
if (mPlaybackPort) {
mPlaybackPort->Destroy();
mPlaybackPort = nullptr;
}
if (mOwnedPort) {
mOwnedPort->Destroy();
mOwnedPort = nullptr;
}
if (mPlaybackStream) {
mPlaybackStream->Destroy();
mPlaybackStream = nullptr;
}
if (mOwnedStream) {
mOwnedStream->Destroy();
mOwnedStream = nullptr;
}
if (mInputStream) {
mInputStream->Destroy();
mInputStream = nullptr;
}
}
JSObject*
DOMMediaStream::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return dom::MediaStreamBinding::Wrap(aCx, this, aGivenProto);
}
double
DOMMediaStream::CurrentTime()
{
if (!mPlaybackStream) {
return 0.0;
}
return mPlaybackStream->
StreamTimeToSeconds(mPlaybackStream->GetCurrentTime() - mLogicalStreamStartTime);
}
void
DOMMediaStream::GetId(nsAString& aID) const
{
aID = mID;
}
void
DOMMediaStream::GetAudioTracks(nsTArray<RefPtr<AudioStreamTrack> >& aTracks)
{
for (const RefPtr<TrackPort>& info : mTracks) {
AudioStreamTrack* t = info->GetTrack()->AsAudioStreamTrack();
if (t) {
aTracks.AppendElement(t);
}
}
}
void
DOMMediaStream::GetVideoTracks(nsTArray<RefPtr<VideoStreamTrack> >& aTracks)
{
for (const RefPtr<TrackPort>& info : mTracks) {
VideoStreamTrack* t = info->GetTrack()->AsVideoStreamTrack();
if (t) {
aTracks.AppendElement(t);
}
}
}
void
DOMMediaStream::GetTracks(nsTArray<RefPtr<MediaStreamTrack> >& aTracks)
{
for (const RefPtr<TrackPort>& info : mTracks) {
aTracks.AppendElement(info->GetTrack());
}
}
void
DOMMediaStream::AddTrack(MediaStreamTrack& aTrack)
{
RefPtr<ProcessedMediaStream> dest = mPlaybackStream->AsProcessedStream();
MOZ_ASSERT(dest);
if (!dest) {
return;
}
LOG(LogLevel::Info, ("DOMMediaStream %p Adding track %p (from stream %p with ID %d)",
this, &aTrack, aTrack.GetStream(), aTrack.GetTrackID()));
if (HasTrack(aTrack)) {
LOG(LogLevel::Debug, ("DOMMediaStream %p already contains track %p", this, &aTrack));
return;
}
RefPtr<DOMMediaStream> addedDOMStream = aTrack.GetStream();
MOZ_RELEASE_ASSERT(addedDOMStream);
RefPtr<MediaStream> owningStream = addedDOMStream->GetOwnedStream();
MOZ_RELEASE_ASSERT(owningStream);
CombineWithPrincipal(addedDOMStream->mPrincipal);
// Hook up the underlying track with our underlying playback stream.
RefPtr<MediaInputPort> inputPort =
GetPlaybackStream()->AllocateInputPort(owningStream, aTrack.GetTrackID());
RefPtr<TrackPort> trackPort =
new TrackPort(inputPort, &aTrack, TrackPort::InputPortOwnership::OWNED);
mTracks.AppendElement(trackPort.forget());
NotifyTrackAdded(&aTrack);
LOG(LogLevel::Debug, ("DOMMediaStream %p Added track %p", this, &aTrack));
}
void
DOMMediaStream::RemoveTrack(MediaStreamTrack& aTrack)
{
LOG(LogLevel::Info, ("DOMMediaStream %p Removing track %p (from stream %p with ID %d)",
this, &aTrack, aTrack.GetStream(), aTrack.GetTrackID()));
RefPtr<TrackPort> toRemove = FindPlaybackTrackPort(aTrack);
if (!toRemove) {
LOG(LogLevel::Debug, ("DOMMediaStream %p does not contain track %p", this, &aTrack));
return;
}
// 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.
toRemove->BlockTrackId(aTrack.GetTrackID());
DebugOnly<bool> removed = mTracks.RemoveElement(toRemove);
MOZ_ASSERT(removed);
LOG(LogLevel::Debug, ("DOMMediaStream %p Removed track %p", this, &aTrack));
}
bool
DOMMediaStream::HasTrack(const MediaStreamTrack& aTrack) const
{
return !!FindPlaybackTrackPort(aTrack);
}
bool
DOMMediaStream::OwnsTrack(const MediaStreamTrack& aTrack) const
{
return (aTrack.GetStream() == this) && HasTrack(aTrack);
}
bool
DOMMediaStream::IsFinished()
{
return !mPlaybackStream || mPlaybackStream->IsFinished();
}
void
DOMMediaStream::InitSourceStream(nsIDOMWindow* aWindow,
MediaStreamGraph* aGraph)
{
mWindow = aWindow;
InitStreamCommon(aGraph->CreateSourceStream(nullptr), aGraph);
}
void
DOMMediaStream::InitTrackUnionStream(nsIDOMWindow* aWindow,
MediaStreamGraph* aGraph)
{
mWindow = aWindow;
InitStreamCommon(aGraph->CreateTrackUnionStream(nullptr), aGraph);
}
void
DOMMediaStream::InitAudioCaptureStream(nsIDOMWindow* aWindow,
MediaStreamGraph* aGraph)
{
mWindow = aWindow;
const TrackID AUDIO_TRACK = 1;
InitStreamCommon(aGraph->CreateAudioCaptureStream(this, AUDIO_TRACK), aGraph);
CreateOwnDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO);
}
void
DOMMediaStream::InitStreamCommon(MediaStream* aStream,
MediaStreamGraph* aGraph)
{
mInputStream = aStream;
// We pass null as the wrapper since it is only used to signal finished
// streams. This is only needed for the playback stream.
mOwnedStream = aGraph->CreateTrackUnionStream(nullptr);
mOwnedStream->SetAutofinish(true);
mOwnedPort = mOwnedStream->AllocateInputPort(mInputStream);
mPlaybackStream = aGraph->CreateTrackUnionStream(this);
mPlaybackStream->SetAutofinish(true);
mPlaybackPort = mPlaybackStream->AllocateInputPort(mOwnedStream);
LOG(LogLevel::Debug, ("DOMMediaStream %p Initiated with mInputStream=%p, mOwnedStream=%p, mPlaybackStream=%p",
this, mInputStream, mOwnedStream, mPlaybackStream));
// Setup track listeners
mOwnedListener = new OwnedStreamListener(this);
mOwnedStream->AddListener(mOwnedListener);
mPlaybackListener = new PlaybackStreamListener(this);
mPlaybackStream->AddListener(mPlaybackListener);
}
already_AddRefed<DOMMediaStream>
DOMMediaStream::CreateSourceStream(nsIDOMWindow* aWindow,
MediaStreamGraph* aGraph)
{
RefPtr<DOMMediaStream> stream = new DOMMediaStream();
stream->InitSourceStream(aWindow, aGraph);
return stream.forget();
}
already_AddRefed<DOMMediaStream>
DOMMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
MediaStreamGraph* aGraph)
{
RefPtr<DOMMediaStream> stream = new DOMMediaStream();
stream->InitTrackUnionStream(aWindow, aGraph);
return stream.forget();
}
already_AddRefed<DOMMediaStream>
DOMMediaStream::CreateAudioCaptureStream(nsIDOMWindow* aWindow,
MediaStreamGraph* aGraph)
{
RefPtr<DOMMediaStream> stream = new DOMMediaStream();
stream->InitAudioCaptureStream(aWindow, aGraph);
return stream.forget();
}
void
DOMMediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled)
{
if (mOwnedStream) {
mOwnedStream->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)
{
return nullptr;
}
bool
DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
{
bool changed =
nsContentUtils::CombineResourcePrincipals(&mPrincipal, aPrincipal);
if (changed) {
NotifyPrincipalChanged();
}
return changed;
}
void
DOMMediaStream::SetPrincipal(nsIPrincipal* aPrincipal)
{
mPrincipal = aPrincipal;
NotifyPrincipalChanged();
}
void
DOMMediaStream::SetCORSMode(CORSMode aCORSMode)
{
MOZ_ASSERT(NS_IsMainThread());
mCORSMode = aCORSMode;
}
CORSMode
DOMMediaStream::GetCORSMode()
{
MOZ_ASSERT(NS_IsMainThread());
return mCORSMode;
}
void
DOMMediaStream::NotifyPrincipalChanged()
{
for (uint32_t i = 0; i < mPrincipalChangeObservers.Length(); ++i) {
mPrincipalChangeObservers[i]->PrincipalChanged(this);
}
}
bool
DOMMediaStream::AddPrincipalChangeObserver(PrincipalChangeObserver* aObserver)
{
return mPrincipalChangeObservers.AppendElement(aObserver) != nullptr;
}
bool
DOMMediaStream::RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver)
{
return mPrincipalChangeObservers.RemoveElement(aObserver);
}
MediaStreamTrack*
DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
{
MOZ_ASSERT(FindOwnedDOMTrack(GetOwnedStream(), aTrackID) == nullptr);
MediaStreamTrack* track;
switch (aType) {
case MediaSegment::AUDIO:
track = new AudioStreamTrack(this, aTrackID);
break;
case MediaSegment::VIDEO:
track = new VideoStreamTrack(this, aTrackID);
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 =
new TrackPort(mOwnedPort, track, TrackPort::InputPortOwnership::EXTERNAL);
mOwnedTracks.AppendElement(ownedTrackPort.forget());
RefPtr<TrackPort> playbackTrackPort =
new TrackPort(mPlaybackPort, track, TrackPort::InputPortOwnership::EXTERNAL);
mTracks.AppendElement(playbackTrackPort.forget());
NotifyTrackAdded(track);
return track;
}
MediaStreamTrack*
DOMMediaStream::FindOwnedDOMTrack(MediaStream* aOwningStream, TrackID aTrackID) const
{
if (aOwningStream != mOwnedStream) {
return nullptr;
}
for (const RefPtr<TrackPort>& info : mOwnedTracks) {
if (info->GetTrack()->GetTrackID() == aTrackID) {
return info->GetTrack();
}
}
return nullptr;
}
MediaStreamTrack*
DOMMediaStream::FindPlaybackDOMTrack(MediaStream* aInputStream, TrackID aInputTrackID) const
{
for (const RefPtr<TrackPort>& info : mTracks) {
if (info->GetInputPort() == mPlaybackPort &&
aInputStream == mOwnedStream &&
aInputTrackID == info->GetTrack()->GetTrackID()) {
// This track is in our owned and playback streams.
return info->GetTrack();
}
if (info->GetInputPort()->GetSource() == aInputStream &&
info->GetSourceTrackId() == aInputTrackID) {
// This track is owned externally but in our playback stream.
MOZ_ASSERT(aInputTrackID != TRACK_NONE);
MOZ_ASSERT(aInputTrackID != TRACK_INVALID);
MOZ_ASSERT(aInputTrackID != TRACK_ANY);
return info->GetTrack();
}
}
return nullptr;
}
DOMMediaStream::TrackPort*
DOMMediaStream::FindPlaybackTrackPort(const MediaStreamTrack& aTrack) const
{
for (const RefPtr<TrackPort>& info : mTracks) {
if (info->GetTrack() == &aTrack) {
return info;
}
}
return nullptr;
}
void
DOMMediaStream::NotifyMediaStreamGraphShutdown()
{
// No more tracks will ever be added, so just clear these callbacks now
// to prevent leaks.
mNotifiedOfMediaStreamGraphShutdown = true;
mRunOnTracksAvailable.Clear();
mTrackListeners.Clear();
mConsumersToKeepAlive.Clear();
}
void
DOMMediaStream::NotifyStreamFinished()
{
MOZ_ASSERT(IsFinished());
mConsumersToKeepAlive.Clear();
}
void
DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable)
{
if (mNotifiedOfMediaStreamGraphShutdown) {
// No more tracks will ever be added, so just delete the callback now.
delete aRunnable;
return;
}
mRunOnTracksAvailable.AppendElement(aRunnable);
CheckTracksAvailable();
}
void
DOMMediaStream::NotifyTracksCreated()
{
mTracksCreated = true;
CheckTracksAvailable();
}
void
DOMMediaStream::CheckTracksAvailable()
{
if (!mTracksCreated) {
return;
}
nsTArray<nsAutoPtr<OnTracksAvailableCallback> > callbacks;
callbacks.SwapElements(mRunOnTracksAvailable);
for (uint32_t i = 0; i < callbacks.Length(); ++i) {
callbacks[i]->NotifyTracksAvailable(this);
}
}
void
DOMMediaStream::RegisterTrackListener(TrackListener* aListener)
{
MOZ_ASSERT(NS_IsMainThread());
if (mNotifiedOfMediaStreamGraphShutdown) {
// No more tracks will ever be added, so just do nothing.
return;
}
mTrackListeners.AppendElement(aListener);
}
void
DOMMediaStream::UnregisterTrackListener(TrackListener* aListener)
{
MOZ_ASSERT(NS_IsMainThread());
mTrackListeners.RemoveElement(aListener);
}
void
DOMMediaStream::NotifyTrackAdded(
const RefPtr<MediaStreamTrack>& aTrack)
{
MOZ_ASSERT(NS_IsMainThread());
for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
const RefPtr<TrackListener>& listener = mTrackListeners[i];
listener->NotifyTrackAdded(aTrack);
}
}
void
DOMMediaStream::NotifyTrackRemoved(
const RefPtr<MediaStreamTrack>& aTrack)
{
MOZ_ASSERT(NS_IsMainThread());
for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
const RefPtr<TrackListener>& listener = mTrackListeners[i];
listener->NotifyTrackRemoved(aTrack);
}
}
void
DOMMediaStream::CreateAndAddPlaybackStreamListener(MediaStream* aStream)
{
MOZ_ASSERT(GetCameraStream(), "I'm a hack. Only DOMCameraControl may use me.");
mPlaybackListener = new PlaybackStreamListener(this);
aStream->AddListener(mPlaybackListener);
}
DOMLocalMediaStream::~DOMLocalMediaStream()
{
if (mInputStream) {
// Make sure Listeners of this stream know it's going away
StopImpl();
}
}
JSObject*
DOMLocalMediaStream::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return dom::LocalMediaStreamBinding::Wrap(aCx, this, aGivenProto);
}
void
DOMLocalMediaStream::Stop()
{
nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(GetParentObject());
nsIDocument* document = pWindow ? pWindow->GetExtantDoc() : nullptr;
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("Media"),
document,
nsContentUtils::eDOM_PROPERTIES,
"MediaStreamStopDeprecatedWarning");
StopImpl();
}
void
DOMLocalMediaStream::StopImpl()
{
if (mInputStream && mInputStream->AsSourceStream()) {
mInputStream->AsSourceStream()->EndAllTrackAndFinish();
}
}
already_AddRefed<DOMLocalMediaStream>
DOMLocalMediaStream::CreateSourceStream(nsIDOMWindow* aWindow,
MediaStreamGraph* aGraph)
{
RefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream();
stream->InitSourceStream(aWindow, aGraph);
return stream.forget();
}
already_AddRefed<DOMLocalMediaStream>
DOMLocalMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
MediaStreamGraph* aGraph)
{
RefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream();
stream->InitTrackUnionStream(aWindow, aGraph);
return stream.forget();
}
already_AddRefed<DOMLocalMediaStream>
DOMLocalMediaStream::CreateAudioCaptureStream(nsIDOMWindow* aWindow,
MediaStreamGraph* aGraph)
{
RefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream();
stream->InitAudioCaptureStream(aWindow, aGraph);
return stream.forget();
}
DOMAudioNodeMediaStream::DOMAudioNodeMediaStream(AudioNode* aNode)
: mStreamNode(aNode)
{
}
DOMAudioNodeMediaStream::~DOMAudioNodeMediaStream()
{
}
already_AddRefed<DOMAudioNodeMediaStream>
DOMAudioNodeMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
AudioNode* aNode,
MediaStreamGraph* aGraph)
{
RefPtr<DOMAudioNodeMediaStream> stream = new DOMAudioNodeMediaStream(aNode);
stream->InitTrackUnionStream(aWindow, aGraph);
return stream.forget();
}
DOMHwMediaStream::DOMHwMediaStream()
{
#ifdef MOZ_WIDGET_GONK
mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS_OVERLAY);
RefPtr<Image> img = mImageContainer->CreateImage(ImageFormat::OVERLAY_IMAGE);
mOverlayImage = static_cast<layers::OverlayImage*>(img.get());
nsAutoTArray<ImageContainer::NonOwningImage,1> images;
images.AppendElement(ImageContainer::NonOwningImage(img));
mImageContainer->SetCurrentImages(images);
#endif
}
DOMHwMediaStream::~DOMHwMediaStream()
{
}
already_AddRefed<DOMHwMediaStream>
DOMHwMediaStream::CreateHwStream(nsIDOMWindow* aWindow)
{
RefPtr<DOMHwMediaStream> stream = new DOMHwMediaStream();
MediaStreamGraph* graph =
MediaStreamGraph::GetInstance(MediaStreamGraph::SYSTEM_THREAD_DRIVER,
AudioChannel::Normal);
stream->InitSourceStream(aWindow, graph);
stream->Init(stream->GetInputStream());
return stream.forget();
}
void
DOMHwMediaStream::Init(MediaStream* stream)
{
SourceMediaStream* srcStream = stream->AsSourceStream();
if (srcStream) {
VideoSegment segment;
#ifdef MOZ_WIDGET_GONK
const StreamTime delta = STREAM_TIME_MAX; // Because MediaStreamGraph will run out frames in non-autoplay mode,
// we must give it bigger frame length to cover this situation.
mImageData.mOverlayId = DEFAULT_IMAGE_ID;
mImageData.mSize.width = DEFAULT_IMAGE_WIDTH;
mImageData.mSize.height = DEFAULT_IMAGE_HEIGHT;
mOverlayImage->SetData(mImageData);
RefPtr<Image> image = static_cast<Image*>(mOverlayImage.get());
mozilla::gfx::IntSize size = image->GetSize();
segment.AppendFrame(image.forget(), delta, size);
#endif
srcStream->AddTrack(TRACK_VIDEO_PRIMARY, 0, new VideoSegment());
srcStream->AppendToTrack(TRACK_VIDEO_PRIMARY, &segment);
srcStream->FinishAddTracks();
srcStream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
}
}
int32_t
DOMHwMediaStream::RequestOverlayId()
{
#ifdef MOZ_WIDGET_GONK
return mOverlayImage->GetOverlayId();
#else
return -1;
#endif
}
void
DOMHwMediaStream::SetImageSize(uint32_t width, uint32_t height)
{
#ifdef MOZ_WIDGET_GONK
OverlayImage::Data imgData;
imgData.mOverlayId = mOverlayImage->GetOverlayId();
imgData.mSize = IntSize(width, height);
mOverlayImage->SetData(imgData);
#endif
SourceMediaStream* srcStream = GetInputStream()->AsSourceStream();
StreamBuffer::Track* track = srcStream->FindTrack(TRACK_VIDEO_PRIMARY);
if (!track || !track->GetSegment()) {
return;
}
#ifdef MOZ_WIDGET_GONK
// Clear the old segment.
// Changing the existing content of segment is a Very BAD thing, and this way will
// confuse consumers of MediaStreams.
// It is only acceptable for DOMHwMediaStream
// because DOMHwMediaStream doesn't have consumers of TV streams currently.
track->GetSegment()->Clear();
// Change the image size.
const StreamTime delta = STREAM_TIME_MAX;
RefPtr<Image> image = static_cast<Image*>(mOverlayImage.get());
mozilla::gfx::IntSize size = image->GetSize();
VideoSegment segment;
segment.AppendFrame(image.forget(), delta, size);
srcStream->AppendToTrack(TRACK_VIDEO_PRIMARY, &segment);
#endif
}
void
DOMHwMediaStream::SetOverlayId(int32_t aOverlayId)
{
#ifdef MOZ_WIDGET_GONK
OverlayImage::Data imgData;
imgData.mOverlayId = aOverlayId;
imgData.mSize = mOverlayImage->GetSize();
mOverlayImage->SetData(imgData);
#endif
}