--- a/dom/camera/CameraPreviewMediaStream.cpp
+++ b/dom/camera/CameraPreviewMediaStream.cpp
@@ -1,15 +1,16 @@
/* -*- 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 "CameraPreviewMediaStream.h"
#include "CameraCommon.h"
+#include "MediaStreamListener.h"
/**
* Maximum number of outstanding invalidates before we start to drop frames;
* if we hit this threshold, it is an indicator that the main thread is
* either very busy or the device is busy elsewhere (e.g. encoding or
* persisting video data).
*/
#define MAX_INVALIDATE_PENDING 4
@@ -84,31 +85,31 @@ CameraPreviewMediaStream::AddListener(Me
void
CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
{
MutexAutoLock lock(mMutex);
RefPtr<MediaStreamListener> listener(aListener);
mListeners.RemoveElement(aListener);
- listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamListener::EVENT_REMOVED);
+ listener->NotifyEvent(mFakeMediaStreamGraph, MediaStreamGraphEvent::EVENT_REMOVED);
}
void
CameraPreviewMediaStream::OnPreviewStateChange(bool aActive)
{
if (aActive) {
MutexAutoLock lock(mMutex);
if (!mTrackCreated) {
mTrackCreated = true;
VideoSegment tmpSegment;
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
l->NotifyQueuedTrackChanges(mFakeMediaStreamGraph, TRACK_VIDEO, 0,
- MediaStreamListener::TRACK_EVENT_CREATED,
+ TrackEventCommand::TRACK_EVENT_CREATED,
tmpSegment);
l->NotifyFinishedTrackCreation(mFakeMediaStreamGraph);
}
}
}
}
void
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -62,22 +62,22 @@ public:
if (!mCameraControl) {
return;
}
mCameraControl->TrackCreated(aTrackID);
}
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
- StreamTime aTrackOffset, uint32_t aTrackEvents,
+ StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override
{
- if (aTrackEvents & TRACK_EVENT_CREATED) {
+ if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod<TrackID>(
this, &TrackCreatedListener::DoNotifyTrackCreated, aID));
}
}
protected:
~TrackCreatedListener() {}
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -64,16 +64,17 @@
#include "nsURIHashKey.h"
#include "nsJSUtils.h"
#include "MediaStreamGraph.h"
#include "nsIScriptError.h"
#include "nsHostObjectProtocolHandler.h"
#include "mozilla/dom/MediaSource.h"
#include "MediaMetadataManager.h"
#include "MediaSourceDecoder.h"
+#include "MediaStreamListener.h"
#include "DOMMediaStream.h"
#include "AudioStreamTrack.h"
#include "VideoStreamTrack.h"
#include "MediaTrackList.h"
#include "AudioChannelService.h"
#include "mozilla/dom/power/PowerManagerService.h"
@@ -3328,19 +3329,19 @@ public:
if (aBlocked == BLOCKED) {
event = NewRunnableMethod(this, &StreamListener::DoNotifyBlocked);
} else {
event = NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
}
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
}
virtual void NotifyEvent(MediaStreamGraph* aGraph,
- MediaStreamListener::MediaStreamGraphEvent event) override
+ MediaStreamGraphEvent event) override
{
- if (event == EVENT_FINISHED) {
+ if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
nsCOMPtr<nsIRunnable> event =
NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
}
}
virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) override
{
MutexAutoLock lock(mMutex);
@@ -3390,17 +3391,17 @@ public:
return;
}
RefPtr<HTMLMediaElement> deathGrip = mElement;
mElement->UpdateInitialMediaSize(aSize);
}
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
StreamTime aTrackOffset,
- uint32_t aTrackEvents,
+ TrackEventCommand aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override
{
if (mInitialSizeFound || aQueuedMedia.GetType() != MediaSegment::VIDEO) {
return;
}
const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
--- a/dom/media/AudioCaptureStream.cpp
+++ b/dom/media/AudioCaptureStream.cpp
@@ -1,14 +1,15 @@
/* -*- 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 "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/unused.h"
#include "AudioSegment.h"
#include "mozilla/Logging.h"
#include "mozilla/Attributes.h"
#include "AudioCaptureStream.h"
#include "ImageContainer.h"
@@ -73,17 +74,17 @@ AudioCaptureStream::ProcessInput(GraphTi
uint32_t inputCount = mInputs.Length();
StreamTracks::Track* track = EnsureTrack(mTrackId);
// Notify the DOM everything is in order.
if (!mTrackCreated) {
for (uint32_t i = 0; i < mListeners.Length(); i++) {
MediaStreamListener* l = mListeners[i];
AudioSegment tmp;
l->NotifyQueuedTrackChanges(
- Graph(), mTrackId, 0, MediaStreamListener::TRACK_EVENT_CREATED, tmp);
+ Graph(), mTrackId, 0, TrackEventCommand::TRACK_EVENT_CREATED, tmp);
l->NotifyFinishedTrackCreation(Graph());
}
mTrackCreated = true;
}
if (IsFinishedOnGraphThread()) {
return;
}
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -181,28 +181,28 @@ public:
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,
+ StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override
{
- if (aTrackEvents & TRACK_EVENT_CREATED) {
+ if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod<TrackID, MediaSegment::Type, MediaStream*, TrackID>(
this, &OwnedStreamListener::DoNotifyTrackCreated,
aID, aQueuedMedia.GetType(), aInputStream, aInputTrackID);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
- } else if (aTrackEvents & TRACK_EVENT_ENDED) {
+ } else if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod<MediaStream*, TrackID, TrackID>(
this, &OwnedStreamListener::DoNotifyTrackEnded,
aInputStream, aInputTrackID, aID);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
}
}
@@ -271,22 +271,22 @@ public:
}
mStream->NotifyTracksCreated();
}
// The methods below are called on the MediaStreamGraph thread.
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
- StreamTime aTrackOffset, uint32_t aTrackEvents,
+ StreamTime aTrackOffset, TrackEventCommand aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override
{
- if (aTrackEvents & TRACK_EVENT_ENDED) {
+ if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod<StorensRefPtrPassByPtr<MediaStream>, TrackID>(
this, &PlaybackStreamListener::DoNotifyTrackEnded, aInputStream, aInputTrackID);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
}
}
void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -4,16 +4,17 @@
* 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 "MediaManager.h"
#include "MediaStreamGraph.h"
#include "mozilla/dom/MediaStreamTrack.h"
#include "GetUserMediaRequest.h"
+#include "MediaStreamListener.h"
#include "nsContentUtils.h"
#include "nsHashPropertyBag.h"
#ifdef MOZ_WIDGET_GONK
#include "nsIAudioManager.h"
#endif
#include "nsIEventTarget.h"
#include "nsIUUIDGenerator.h"
#include "nsIScriptGlobalObject.h"
@@ -238,16 +239,263 @@ HostHasPermission(nsIURI &docURI)
}
begin = end + 1;
} while (end < domainWhiteList.Length());
return false;
}
+/**
+ * This class is an implementation of MediaStreamListener. This is used
+ * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
+ * are assigned and deassigned in content.
+ */
+class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
+{
+public:
+ // Create in an inactive state
+ GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
+ uint64_t aWindowID,
+ const PrincipalHandle& aPrincipalHandle)
+ : mMediaThread(aThread)
+ , mMainThreadCheck(nullptr)
+ , mWindowID(aWindowID)
+ , mPrincipalHandle(aPrincipalHandle)
+ , mStopped(false)
+ , mFinished(false)
+ , mRemoved(false)
+ , mAudioStopped(false)
+ , mVideoStopped(false) {}
+
+ ~GetUserMediaCallbackMediaStreamListener()
+ {
+ Unused << mMediaThread;
+ // It's OK to release mStream on any thread; they have thread-safe
+ // refcounts.
+ }
+
+ void Activate(already_AddRefed<SourceMediaStream> aStream,
+ AudioDevice* aAudioDevice,
+ VideoDevice* aVideoDevice)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ mMainThreadCheck = PR_GetCurrentThread();
+ mStream = aStream;
+ mAudioDevice = aAudioDevice;
+ mVideoDevice = aVideoDevice;
+
+ mStream->AddListener(this);
+ }
+
+ MediaStream *Stream() // Can be used to test if Activate was called
+ {
+ return mStream;
+ }
+ SourceMediaStream *GetSourceStream()
+ {
+ NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
+ if (!mStream) {
+ return nullptr;
+ }
+ return mStream->AsSourceStream();
+ }
+
+ void StopSharing();
+
+ void StopTrack(TrackID aID);
+
+ typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
+
+ already_AddRefed<PledgeVoid>
+ ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
+ TrackID aID,
+ const dom::MediaTrackConstraints& aConstraints);
+
+ // mVideo/AudioDevice are set by Activate(), so we assume they're capturing
+ // if set and represent a real capture device.
+ bool CapturingVideo()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mVideoDevice && !mStopped &&
+ !mVideoDevice->GetSource()->IsAvailable() &&
+ mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
+ (!mVideoDevice->GetSource()->IsFake() ||
+ Preferences::GetBool("media.navigator.permission.fake"));
+ }
+ bool CapturingAudio()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mAudioDevice && !mStopped &&
+ !mAudioDevice->GetSource()->IsAvailable() &&
+ (!mAudioDevice->GetSource()->IsFake() ||
+ Preferences::GetBool("media.navigator.permission.fake"));
+ }
+ bool CapturingScreen()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mVideoDevice && !mStopped &&
+ !mVideoDevice->GetSource()->IsAvailable() &&
+ mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
+ }
+ bool CapturingWindow()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mVideoDevice && !mStopped &&
+ !mVideoDevice->GetSource()->IsAvailable() &&
+ mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
+ }
+ bool CapturingApplication()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mVideoDevice && !mStopped &&
+ !mVideoDevice->GetSource()->IsAvailable() &&
+ mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
+ }
+ bool CapturingBrowser()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ return mVideoDevice && !mStopped &&
+ mVideoDevice->GetSource()->IsAvailable() &&
+ mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
+ }
+
+ // implement in .cpp to avoid circular dependency with MediaOperationTask
+ // Can be invoked from EITHER MainThread or MSG thread
+ void Stop();
+
+ void
+ AudioConfig(bool aEchoOn, uint32_t aEcho,
+ bool aAgcOn, uint32_t aAGC,
+ bool aNoiseOn, uint32_t aNoise,
+ int32_t aPlayoutDelay);
+
+ void
+ Remove()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ // allow calling even if inactive (!mStream) for easier cleanup
+ // Caller holds strong reference to us, so no death grip required
+ if (mStream && !mRemoved) {
+ MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
+ mRemoved = true; // RemoveListener is async, avoid races
+ // If it's destroyed, don't call - listener will be removed and we'll be notified!
+ if (!mStream->IsDestroyed()) {
+ mStream->RemoveListener(this);
+ }
+ }
+ }
+
+ // Proxy NotifyPull() to sources
+ void
+ NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
+ {
+ // Currently audio sources ignore NotifyPull, but they could
+ // watch it especially for fake audio.
+ if (mAudioDevice) {
+ mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
+ aDesiredTime, mPrincipalHandle);
+ }
+ if (mVideoDevice) {
+ mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
+ aDesiredTime, mPrincipalHandle);
+ }
+ }
+
+ void
+ NotifyEvent(MediaStreamGraph* aGraph,
+ MediaStreamGraphEvent aEvent) override
+ {
+ nsresult rv;
+ nsCOMPtr<nsIThread> thread;
+
+ switch (aEvent) {
+ case MediaStreamGraphEvent::EVENT_FINISHED:
+ rv = NS_GetMainThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ NS_ASSERTION(false, "Mainthread not available; running on current thread");
+ // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
+ MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
+ NotifyFinished();
+ return;
+ }
+ thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished),
+ NS_DISPATCH_NORMAL);
+ break;
+ case MediaStreamGraphEvent::EVENT_REMOVED:
+ rv = NS_GetMainThread(getter_AddRefs(thread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ NS_ASSERTION(false, "Mainthread not available; running on current thread");
+ // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
+ MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
+ NotifyRemoved();
+ return;
+ }
+ thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved),
+ NS_DISPATCH_NORMAL);
+ break;
+ case MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS:
+ NotifyDirectListeners(aGraph, true);
+ break;
+ case MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS:
+ NotifyDirectListeners(aGraph, false);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void
+ NotifyFinished();
+
+ void
+ NotifyRemoved();
+
+ void
+ NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
+
+ PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
+
+private:
+ // Set at construction
+ base::Thread* mMediaThread;
+ // never ever indirect off this; just for assertions
+ PRThread* mMainThreadCheck;
+
+ uint64_t mWindowID;
+ const PrincipalHandle mPrincipalHandle;
+
+ // true after this listener has sent MEDIA_STOP. MainThread only.
+ bool mStopped;
+
+ // true after the stream this listener is listening to has finished in the
+ // MediaStreamGraph. MainThread only.
+ bool mFinished;
+
+ // true after this listener has been removed from its MediaStream.
+ // MainThread only.
+ bool mRemoved;
+
+ // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
+ // MainThread only.
+ bool mAudioStopped;
+
+ // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
+ // MainThread only.
+ bool mVideoStopped;
+
+ // Set at Activate on MainThread
+
+ // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
+ // No locking needed as they're only addrefed except on the MediaManager thread
+ RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
+ RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
+ RefPtr<SourceMediaStream> mStream; // threadsafe refcnt
+};
+
// Generic class for running long media operations like Start off the main
// thread, and then (because nsDOMMediaStreams aren't threadsafe),
// ProxyReleases mStream since it's cycle collected.
class MediaOperationTask : public Runnable
{
public:
// so we can send Stop without AddRef()ing from the MSG thread
MediaOperationTask(MediaOperation aType,
@@ -3139,24 +3387,24 @@ GetUserMediaCallbackMediaStreamListener:
dom::AudioChannel::Normal);
graph->UnregisterCaptureStreamForWindow(mWindowID);
mStream->Destroy();
}
}
// ApplyConstraints for track
-already_AddRefed<GetUserMediaCallbackMediaStreamListener::PledgeVoid>
+already_AddRefed<media::Pledge<bool, dom::MediaStreamError*>>
GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
nsPIDOMWindowInner* aWindow,
TrackID aTrackID,
const MediaTrackConstraints& aConstraints)
{
MOZ_ASSERT(NS_IsMainThread());
- RefPtr<PledgeVoid> p = new PledgeVoid();
+ RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = new media::Pledge<bool, dom::MediaStreamError*>();
// XXX to support multiple tracks of a type in a stream, this should key off
// the TrackID and not just the type
RefPtr<AudioDevice> audioDevice =
aTrackID == kAudioTrack ? mAudioDevice.get() : nullptr;
RefPtr<VideoDevice> videoDevice =
aTrackID == kVideoTrack ? mVideoDevice.get() : nullptr;
@@ -3199,17 +3447,17 @@ GetUserMediaCallbackMediaStreamListener:
}
NS_DispatchToMainThread(NewRunnableFrom([id, windowId, rv,
badConstraint]() mutable {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<MediaManager> mgr = MediaManager_GetInstance();
if (!mgr) {
return NS_OK;
}
- RefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
+ RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = mgr->mOutstandingVoidPledges.Remove(id);
if (p) {
if (NS_SUCCEEDED(rv)) {
p->Resolve(false);
} else {
auto* window = nsGlobalWindow::GetInnerWindowWithId(windowId);
if (window) {
if (rv == NS_ERROR_NOT_AVAILABLE) {
nsString constraint;
@@ -3315,16 +3563,36 @@ GetUserMediaCallbackMediaStreamListener:
MM_LOG(("Listener removed by DOM Destroy(), mFinished = %d", (int) mFinished));
mRemoved = true;
if (!mFinished) {
NotifyFinished();
}
}
+GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
+ GetUserMediaCallbackMediaStreamListener* aListener,
+ GetUserMediaStatus aStatus,
+ bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
+: mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
+, mIsVideo(aIsVideo), mWindowID(aWindowID) {}
+
+GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
+ GetUserMediaStatus aStatus,
+ already_AddRefed<DOMMediaStream> aStream,
+ OnTracksAvailableCallback* aOnTracksAvailableCallback,
+ bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
+ already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
+: mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
+ mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
+ mOnFailure(aError) {}
+GetUserMediaNotificationEvent::~GetUserMediaNotificationEvent()
+{
+}
+
NS_IMETHODIMP
GetUserMediaNotificationEvent::Run()
{
MOZ_ASSERT(NS_IsMainThread());
// Make sure mStream is cleared and our reference to the DOMMediaStream
// is dropped on the main thread, no matter what happens in this method.
// Otherwise this object might be destroyed off the main thread,
// releasing DOMMediaStream off the main thread, which is not allowed.
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -48,16 +48,20 @@
namespace mozilla {
namespace dom {
struct MediaStreamConstraints;
struct MediaTrackConstraints;
struct MediaTrackConstraintSet;
} // namespace dom
+class MediaManager;
+class GetUserMediaCallbackMediaStreamListener;
+class GetUserMediaTask;
+
extern LogModule* GetMediaManagerLog();
#define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
class MediaDevice : public nsIMediaDevice
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIMEDIADEVICE
@@ -112,289 +116,34 @@ public:
Source* GetSource();
nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs,
const nsACString& aOrigin);
nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
const MediaEnginePrefs &aPrefs);
};
-/**
- * This class is an implementation of MediaStreamListener. This is used
- * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
- * are assigned and deassigned in content.
- */
-class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
-{
-public:
- // Create in an inactive state
- GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
- uint64_t aWindowID,
- const PrincipalHandle& aPrincipalHandle)
- : mMediaThread(aThread)
- , mMainThreadCheck(nullptr)
- , mWindowID(aWindowID)
- , mPrincipalHandle(aPrincipalHandle)
- , mStopped(false)
- , mFinished(false)
- , mRemoved(false)
- , mAudioStopped(false)
- , mVideoStopped(false) {}
-
- ~GetUserMediaCallbackMediaStreamListener()
- {
- Unused << mMediaThread;
- // It's OK to release mStream on any thread; they have thread-safe
- // refcounts.
- }
-
- void Activate(already_AddRefed<SourceMediaStream> aStream,
- AudioDevice* aAudioDevice,
- VideoDevice* aVideoDevice)
- {
- MOZ_ASSERT(NS_IsMainThread());
- mMainThreadCheck = PR_GetCurrentThread();
- mStream = aStream;
- mAudioDevice = aAudioDevice;
- mVideoDevice = aVideoDevice;
-
- mStream->AddListener(this);
- }
-
- MediaStream *Stream() // Can be used to test if Activate was called
- {
- return mStream;
- }
- SourceMediaStream *GetSourceStream()
- {
- NS_ASSERTION(mStream,"Getting stream from never-activated GUMCMSListener");
- if (!mStream) {
- return nullptr;
- }
- return mStream->AsSourceStream();
- }
-
- void StopSharing();
-
- void StopTrack(TrackID aID);
-
- typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
-
- already_AddRefed<PledgeVoid>
- ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
- TrackID aID,
- const dom::MediaTrackConstraints& aConstraints);
-
- // mVideo/AudioDevice are set by Activate(), so we assume they're capturing
- // if set and represent a real capture device.
- bool CapturingVideo()
- {
- MOZ_ASSERT(NS_IsMainThread());
- return mVideoDevice && !mStopped &&
- !mVideoDevice->GetSource()->IsAvailable() &&
- mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
- (!mVideoDevice->GetSource()->IsFake() ||
- Preferences::GetBool("media.navigator.permission.fake"));
- }
- bool CapturingAudio()
- {
- MOZ_ASSERT(NS_IsMainThread());
- return mAudioDevice && !mStopped &&
- !mAudioDevice->GetSource()->IsAvailable() &&
- (!mAudioDevice->GetSource()->IsFake() ||
- Preferences::GetBool("media.navigator.permission.fake"));
- }
- bool CapturingScreen()
- {
- MOZ_ASSERT(NS_IsMainThread());
- return mVideoDevice && !mStopped &&
- !mVideoDevice->GetSource()->IsAvailable() &&
- mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
- }
- bool CapturingWindow()
- {
- MOZ_ASSERT(NS_IsMainThread());
- return mVideoDevice && !mStopped &&
- !mVideoDevice->GetSource()->IsAvailable() &&
- mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
- }
- bool CapturingApplication()
- {
- MOZ_ASSERT(NS_IsMainThread());
- return mVideoDevice && !mStopped &&
- !mVideoDevice->GetSource()->IsAvailable() &&
- mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
- }
- bool CapturingBrowser()
- {
- MOZ_ASSERT(NS_IsMainThread());
- return mVideoDevice && !mStopped &&
- mVideoDevice->GetSource()->IsAvailable() &&
- mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
- }
-
- // implement in .cpp to avoid circular dependency with MediaOperationTask
- // Can be invoked from EITHER MainThread or MSG thread
- void Stop();
-
- void
- AudioConfig(bool aEchoOn, uint32_t aEcho,
- bool aAgcOn, uint32_t aAGC,
- bool aNoiseOn, uint32_t aNoise,
- int32_t aPlayoutDelay);
-
- void
- Remove()
- {
- MOZ_ASSERT(NS_IsMainThread());
- // allow calling even if inactive (!mStream) for easier cleanup
- // Caller holds strong reference to us, so no death grip required
- if (mStream && !mRemoved) {
- MM_LOG(("Listener removed on purpose, mFinished = %d", (int) mFinished));
- mRemoved = true; // RemoveListener is async, avoid races
- // If it's destroyed, don't call - listener will be removed and we'll be notified!
- if (!mStream->IsDestroyed()) {
- mStream->RemoveListener(this);
- }
- }
- }
-
- // Proxy NotifyPull() to sources
- void
- NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) override
- {
- // Currently audio sources ignore NotifyPull, but they could
- // watch it especially for fake audio.
- if (mAudioDevice) {
- mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
- aDesiredTime, mPrincipalHandle);
- }
- if (mVideoDevice) {
- mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
- aDesiredTime, mPrincipalHandle);
- }
- }
-
- void
- NotifyEvent(MediaStreamGraph* aGraph,
- MediaStreamListener::MediaStreamGraphEvent aEvent) override
- {
- nsresult rv;
- nsCOMPtr<nsIThread> thread;
-
- switch (aEvent) {
- case EVENT_FINISHED:
- rv = NS_GetMainThread(getter_AddRefs(thread));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- NS_ASSERTION(false, "Mainthread not available; running on current thread");
- // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
- MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
- NotifyFinished();
- return;
- }
- thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyFinished),
- NS_DISPATCH_NORMAL);
- break;
- case EVENT_REMOVED:
- rv = NS_GetMainThread(getter_AddRefs(thread));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- NS_ASSERTION(false, "Mainthread not available; running on current thread");
- // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
- MOZ_RELEASE_ASSERT(mMainThreadCheck == PR_GetCurrentThread());
- NotifyRemoved();
- return;
- }
- thread->Dispatch(NewRunnableMethod(this, &GetUserMediaCallbackMediaStreamListener::NotifyRemoved),
- NS_DISPATCH_NORMAL);
- break;
- case EVENT_HAS_DIRECT_LISTENERS:
- NotifyDirectListeners(aGraph, true);
- break;
- case EVENT_HAS_NO_DIRECT_LISTENERS:
- NotifyDirectListeners(aGraph, false);
- break;
- default:
- break;
- }
- }
-
- void
- NotifyFinished();
-
- void
- NotifyRemoved();
-
- void
- NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
-
- PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
-
-private:
- // Set at construction
- base::Thread* mMediaThread;
- // never ever indirect off this; just for assertions
- PRThread* mMainThreadCheck;
-
- uint64_t mWindowID;
- const PrincipalHandle mPrincipalHandle;
-
- // true after this listener has sent MEDIA_STOP. MainThread only.
- bool mStopped;
-
- // true after the stream this listener is listening to has finished in the
- // MediaStreamGraph. MainThread only.
- bool mFinished;
-
- // true after this listener has been removed from its MediaStream.
- // MainThread only.
- bool mRemoved;
-
- // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
- // MainThread only.
- bool mAudioStopped;
-
- // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
- // MainThread only.
- bool mVideoStopped;
-
- // Set at Activate on MainThread
-
- // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
- // No locking needed as they're only addrefed except on the MediaManager thread
- RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
- RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
- RefPtr<SourceMediaStream> mStream; // threadsafe refcnt
-};
-
class GetUserMediaNotificationEvent: public Runnable
{
public:
enum GetUserMediaStatus {
STARTING,
STOPPING,
STOPPED_TRACK,
};
GetUserMediaNotificationEvent(GetUserMediaCallbackMediaStreamListener* aListener,
GetUserMediaStatus aStatus,
- bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
- : mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
- , mIsVideo(aIsVideo), mWindowID(aWindowID) {}
+ bool aIsAudio, bool aIsVideo, uint64_t aWindowID);
GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
already_AddRefed<DOMMediaStream> aStream,
OnTracksAvailableCallback* aOnTracksAvailableCallback,
bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
- already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
- : mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
- mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
- mOnFailure(aError) {}
- virtual ~GetUserMediaNotificationEvent()
- {
-
- }
+ already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError);
+ virtual ~GetUserMediaNotificationEvent();
NS_IMETHOD Run() override;
protected:
RefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
RefPtr<DOMMediaStream> mStream;
nsAutoPtr<OnTracksAvailableCallback> mOnTracksAvailableCallback;
GetUserMediaStatus mStatus;
@@ -406,19 +155,16 @@ class GetUserMediaNotificationEvent: pub
typedef enum {
MEDIA_START,
MEDIA_STOP,
MEDIA_STOP_TRACK,
MEDIA_DIRECT_LISTENERS,
} MediaOperation;
-class MediaManager;
-class GetUserMediaTask;
-
class ReleaseMediaOperationResource : public Runnable
{
public:
ReleaseMediaOperationResource(already_AddRefed<DOMMediaStream> aStream,
OnTracksAvailableCallback* aOnTracksAvailableCallback):
mStream(aStream),
mOnTracksAvailableCallback(aOnTracksAvailableCallback) {}
NS_IMETHOD Run() override {return NS_OK;}
@@ -570,17 +316,17 @@ private:
// ONLY accessed from MediaManagerThread
RefPtr<MediaEngine> mBackend;
static StaticRefPtr<MediaManager> sSingleton;
media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
media::CoatCheck<PledgeChar> mOutstandingCharPledges;
- media::CoatCheck<GetUserMediaCallbackMediaStreamListener::PledgeVoid> mOutstandingVoidPledges;
+ media::CoatCheck<media::Pledge<bool, dom::MediaStreamError*>> mOutstandingVoidPledges;
#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
RefPtr<nsDOMCameraManager> mCameraManager;
#endif
public:
media::CoatCheck<media::Pledge<nsCString>> mGetOriginKeyPledges;
UniquePtr<media::Parent<media::NonE10s>> mNonE10sParent;
};
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -17,16 +17,17 @@
#include "mozilla/Logging.h"
#include "mozilla/Attributes.h"
#include "TrackUnionStream.h"
#include "ImageContainer.h"
#include "AudioCaptureStream.h"
#include "AudioChannelService.h"
#include "AudioNodeStream.h"
#include "AudioNodeExternalInputStream.h"
+#include "MediaStreamListener.h"
#include "mozilla/dom/AudioContextBinding.h"
#include "mozilla/media/MediaUtils.h"
#include <algorithm>
#include "GeckoProfiler.h"
#include "mozilla/unused.h"
#include "mozilla/media/MediaUtils.h"
#ifdef MOZ_WEBRTC
#include "AudioOutputObserver.h"
@@ -55,16 +56,22 @@ LazyLogModule gMediaStreamGraphLog("Medi
# define LIFECYCLE_LOG(...) __android_log_print(ANDROID_LOG_INFO, "Gecko - MSG", ## __VA_ARGS__); printf(__VA_ARGS__);printf("\n");
# else
# define LIFECYCLE_LOG(...) printf(__VA_ARGS__);printf("\n");
# endif
#else
# define LIFECYCLE_LOG(...)
#endif
+enum SourceMediaStream::TrackCommands : uint32_t {
+ TRACK_CREATE = TrackEventCommand::TRACK_EVENT_CREATED,
+ TRACK_END = TrackEventCommand::TRACK_EVENT_ENDED,
+ TRACK_UNUSED = TrackEventCommand::TRACK_EVENT_UNUSED,
+};
+
/**
* A hash table containing the graph instances, one per AudioChannel.
*/
static nsDataHashtable<nsUint32HashKey, MediaStreamGraphImpl*> gGraphs;
MediaStreamGraphImpl::~MediaStreamGraphImpl()
{
NS_ASSERTION(IsEmpty(),
@@ -196,17 +203,17 @@ MediaStreamGraphImpl::ExtractPendingInpu
if (data->mCommands) {
MOZ_ASSERT(!(data->mCommands & SourceMediaStream::TRACK_UNUSED));
for (MediaStreamListener* l : aStream->mListeners) {
if (data->mCommands & SourceMediaStream::TRACK_END) {
l->NotifyQueuedAudioData(this, data->mID,
offset, *(static_cast<AudioSegment*>(data->mData.get())));
}
l->NotifyQueuedTrackChanges(this, data->mID,
- offset, data->mCommands, *data->mData);
+ offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
if (data->mCommands & SourceMediaStream::TRACK_CREATE) {
l->NotifyQueuedAudioData(this, data->mID,
offset, *(static_cast<AudioSegment*>(data->mData.get())));
}
}
} else {
for (MediaStreamListener* l : aStream->mListeners) {
l->NotifyQueuedAudioData(this, data->mID,
@@ -216,27 +223,27 @@ MediaStreamGraphImpl::ExtractPendingInpu
}
// Video case.
if (data->mData->GetType() == MediaSegment::VIDEO) {
if (data->mCommands) {
MOZ_ASSERT(!(data->mCommands & SourceMediaStream::TRACK_UNUSED));
for (MediaStreamListener* l : aStream->mListeners) {
l->NotifyQueuedTrackChanges(this, data->mID,
- offset, data->mCommands, *data->mData);
+ offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
}
} else {
// Fixme: This part will be removed in the bug 1201363. It will be
// removed in changeset "Do not copy video segment to StreamTracks in
// TrackUnionStream."
// Dealing with video and not TRACK_CREATE and TRACK_END case.
for (MediaStreamListener* l : aStream->mListeners) {
l->NotifyQueuedTrackChanges(this, data->mID,
- offset, data->mCommands, *data->mData);
+ offset, static_cast<TrackEventCommand>(data->mCommands), *data->mData);
}
}
}
for (TrackBound<MediaStreamTrackListener>& b : aStream->mTrackListeners) {
if (b.mTrackID != data->mID) {
continue;
}
@@ -351,17 +358,17 @@ MediaStreamGraphImpl::UpdateCurrentTimeF
// out.
if (stream->mFinished && !stream->mNotifiedFinished &&
mProcessedTime >=
stream->StreamTimeToGraphTime(stream->GetStreamTracks().GetAllTracksEnd())) {
stream->mNotifiedFinished = true;
SetStreamOrderDirty();
for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
MediaStreamListener* l = stream->mListeners[j];
- l->NotifyEvent(this, MediaStreamListener::EVENT_FINISHED);
+ l->NotifyEvent(this, MediaStreamGraphEvent::EVENT_FINISHED);
}
}
}
}
template<typename C, typename Chunk>
void
MediaStreamGraphImpl::ProcessChunkMetadataForInterval(MediaStream* aStream,
@@ -1996,16 +2003,24 @@ MediaStream::MediaStream()
, mMainThreadDestroyed(false)
, mNrOfMainThreadUsers(0)
, mGraph(nullptr)
, mAudioChannelType(dom::AudioChannel::Normal)
{
MOZ_COUNT_CTOR(MediaStream);
}
+MediaStream::~MediaStream()
+{
+ MOZ_COUNT_DTOR(MediaStream);
+ NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
+ NS_ASSERTION(mMainThreadListeners.IsEmpty(),
+ "All main thread listeners should have been removed");
+}
+
size_t
MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t amount = 0;
// Not owned:
// - mGraph - Not reported here
// - mConsumers - elements
@@ -2100,33 +2115,33 @@ StreamTracks::Track*
MediaStream::EnsureTrack(TrackID aTrackId)
{
StreamTracks::Track* track = mTracks.FindTrack(aTrackId);
if (!track) {
nsAutoPtr<MediaSegment> segment(new AudioSegment());
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
l->NotifyQueuedTrackChanges(Graph(), aTrackId, 0,
- MediaStreamListener::TRACK_EVENT_CREATED,
+ TrackEventCommand::TRACK_EVENT_CREATED,
*segment);
// TODO If we ever need to ensure several tracks at once, we will have to
// change this.
l->NotifyFinishedTrackCreation(Graph());
}
track = &mTracks.AddTrack(aTrackId, 0, segment.forget());
}
return track;
}
void
MediaStream::RemoveAllListenersImpl()
{
for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
RefPtr<MediaStreamListener> listener = mListeners[i].forget();
- listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
+ listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
}
mListeners.Clear();
}
void
MediaStream::DestroyImpl()
{
for (int32_t i = mConsumers.Length() - 1; i >= 0; --i) {
@@ -2376,27 +2391,27 @@ MediaStream::AddListenerImpl(already_Add
// TrackUnionStream guarantees that each of its tracks has an input track.
// Other types do not implement GetInputStreamFor() and will return null.
inputStream = ps->GetInputStreamFor(it->GetID());
MOZ_ASSERT(inputStream);
inputTrackID = ps->GetInputTrackIDFor(it->GetID());
MOZ_ASSERT(IsTrackIDExplicit(inputTrackID));
}
- uint32_t flags = MediaStreamListener::TRACK_EVENT_CREATED;
+ uint32_t flags = TrackEventCommand::TRACK_EVENT_CREATED;
if (it->IsEnded()) {
- flags |= MediaStreamListener::TRACK_EVENT_ENDED;
+ flags |= TrackEventCommand::TRACK_EVENT_ENDED;
}
nsAutoPtr<MediaSegment> segment(it->GetSegment()->CreateEmptyClone());
listener->NotifyQueuedTrackChanges(Graph(), it->GetID(), it->GetEnd(),
- flags, *segment,
+ static_cast<TrackEventCommand>(flags), *segment,
inputStream, inputTrackID);
}
if (mNotifiedFinished) {
- listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
+ listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_FINISHED);
}
if (mNotifiedHasCurrentData) {
listener->NotifyHasCurrentData(GraphImpl());
}
}
void
MediaStream::AddListener(MediaStreamListener* aListener)
@@ -2415,17 +2430,17 @@ MediaStream::AddListener(MediaStreamList
}
void
MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
{
// wouldn't need this if we could do it in the opposite order
RefPtr<MediaStreamListener> listener(aListener);
mListeners.RemoveElement(aListener);
- listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
+ listener->NotifyEvent(GraphImpl(), MediaStreamGraphEvent::EVENT_REMOVED);
}
void
MediaStream::RemoveListener(MediaStreamListener* aListener)
{
class Message : public ControlMessage {
public:
Message(MediaStream* aStream, MediaStreamListener* aListener) :
@@ -2681,16 +2696,26 @@ MediaStream::AddMainThreadListener(MainT
RefPtr<MediaStream> mStream;
};
nsCOMPtr<nsIRunnable> runnable = new NotifyRunnable(this);
NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(runnable.forget())));
}
+SourceMediaStream::SourceMediaStream() :
+ MediaStream(),
+ mMutex("mozilla::media::SourceMediaStream"),
+ mUpdateKnownTracksTime(0),
+ mPullEnabled(false),
+ mUpdateFinished(false),
+ mNeedsMixing(false)
+{
+}
+
nsresult
SourceMediaStream::OpenAudioInput(int aID,
AudioDataListener *aListener)
{
if (GraphImpl()) {
mInputListener = aListener;
return GraphImpl()->OpenAudioInput(aID, aListener);
}
@@ -2746,16 +2771,23 @@ SourceMediaStream::AddTrackInternal(Trac
data->mData = aSegment;
ResampleAudioToGraphSampleRate(data, aSegment);
if (!(aFlags & ADDTRACK_QUEUED) && GraphImpl()) {
GraphImpl()->EnsureNextIteration();
}
}
void
+SourceMediaStream::AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart,
+ AudioSegment* aSegment, uint32_t aFlags)
+{
+ AddTrackInternal(aID, aRate, aStart, aSegment, aFlags);
+}
+
+void
SourceMediaStream::FinishAddTracks()
{
MutexAutoLock lock(mMutex);
mUpdateTracks.AppendElements(Move(mPendingTracks));
LIFECYCLE_LOG("FinishAddTracks: %lu/%lu", mPendingTracks.Length(), mUpdateTracks.Length());
if (GraphImpl()) {
GraphImpl()->EnsureNextIteration();
}
@@ -2843,69 +2875,69 @@ SourceMediaStream::NotifyDirectConsumers
}
StreamTime offset = 0; // FIX! need a separate StreamTime.... or the end of the internal buffer
source.mListener->NotifyRealtimeTrackDataAndApplyTrackDisabling(Graph(), offset, *aSegment);
}
}
// These handle notifying all the listeners of an event
void
-SourceMediaStream::NotifyListenersEventImpl(MediaStreamListener::MediaStreamGraphEvent aEvent)
+SourceMediaStream::NotifyListenersEventImpl(MediaStreamGraphEvent aEvent)
{
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
l->NotifyEvent(GraphImpl(), aEvent);
}
}
void
-SourceMediaStream::NotifyListenersEvent(MediaStreamListener::MediaStreamGraphEvent aNewEvent)
+SourceMediaStream::NotifyListenersEvent(MediaStreamGraphEvent aNewEvent)
{
class Message : public ControlMessage {
public:
- Message(SourceMediaStream* aStream, MediaStreamListener::MediaStreamGraphEvent aEvent) :
+ Message(SourceMediaStream* aStream, MediaStreamGraphEvent aEvent) :
ControlMessage(aStream), mEvent(aEvent) {}
void Run() override
{
mStream->AsSourceStream()->NotifyListenersEventImpl(mEvent);
}
- MediaStreamListener::MediaStreamGraphEvent mEvent;
+ MediaStreamGraphEvent mEvent;
};
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aNewEvent));
}
void
SourceMediaStream::AddDirectListener(DirectMediaStreamListener* aListener)
{
bool wasEmpty;
{
MutexAutoLock lock(mMutex);
wasEmpty = mDirectListeners.IsEmpty();
mDirectListeners.AppendElement(aListener);
}
if (wasEmpty) {
// Async
- NotifyListenersEvent(MediaStreamListener::EVENT_HAS_DIRECT_LISTENERS);
+ NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS);
}
}
void
SourceMediaStream::RemoveDirectListener(DirectMediaStreamListener* aListener)
{
bool isEmpty;
{
MutexAutoLock lock(mMutex);
mDirectListeners.RemoveElement(aListener);
isEmpty = mDirectListeners.IsEmpty();
}
if (isEmpty) {
// Async
- NotifyListenersEvent(MediaStreamListener::EVENT_HAS_NO_DIRECT_LISTENERS);
+ NotifyListenersEvent(MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS);
}
}
void
SourceMediaStream::AddDirectTrackListenerImpl(already_AddRefed<DirectMediaStreamTrackListener> aListener,
TrackID aTrackID)
{
MOZ_ASSERT(IsTrackIDExplicit(aTrackID));
@@ -2974,17 +3006,17 @@ SourceMediaStream::GetEndOfAppendedData(
}
void
SourceMediaStream::EndTrack(TrackID aID)
{
MutexAutoLock lock(mMutex);
TrackData *track = FindDataForTrack(aID);
if (track) {
- track->mCommands |= TRACK_END;
+ track->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
}
if (auto graph = GraphImpl()) {
graph->EnsureNextIteration();
}
}
void
SourceMediaStream::AdvanceKnownTracksTime(StreamTime aKnownTime)
@@ -3033,23 +3065,27 @@ SourceMediaStream::SetTrackEnabledImpl(T
}
void
SourceMediaStream::EndAllTrackAndFinish()
{
MutexAutoLock lock(mMutex);
for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
SourceMediaStream::TrackData* data = &mUpdateTracks[i];
- data->mCommands |= TRACK_END;
+ data->mCommands |= TrackEventCommand::TRACK_EVENT_ENDED;
}
mPendingTracks.Clear();
FinishWithLockHeld();
// we will call NotifyEvent() to let GetUserMedia know
}
+SourceMediaStream::~SourceMediaStream()
+{
+}
+
void
SourceMediaStream::RegisterForAudioMixing()
{
MutexAutoLock lock(mMutex);
mNeedsMixing = true;
}
bool
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -7,24 +7,23 @@
#define MOZILLA_MEDIASTREAMGRAPH_H_
#include "mozilla/LinkedList.h"
#include "mozilla/Mutex.h"
#include "mozilla/TaskQueue.h"
#include "mozilla/dom/AudioChannelBinding.h"
-#include "AudioSegment.h"
#include "AudioStream.h"
#include "nsTArray.h"
#include "nsIRunnable.h"
-#include "StreamTracks.h"
#include "VideoFrameContainer.h"
#include "VideoSegment.h"
#include "MainThreadUtils.h"
+#include "StreamTracks.h"
#include "nsAutoPtr.h"
#include "nsAutoRef.h"
#include <speex/speex_resampler.h>
class nsIRunnable;
template <>
class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
@@ -84,135 +83,16 @@ class AudioNodeStream;
class CameraPreviewMediaStream;
class MediaInputPort;
class MediaStream;
class MediaStreamGraph;
class MediaStreamGraphImpl;
class ProcessedMediaStream;
class SourceMediaStream;
-/**
- * This is a base class for media graph thread listener callbacks.
- * Override methods to be notified of audio or video data or changes in stream
- * state.
- *
- * This can be used by stream recorders or network connections that receive
- * stream input. It could also be used for debugging.
- *
- * All notification methods are called from the media graph thread. Overriders
- * of these methods are responsible for all synchronization. Beware!
- * These methods are called without the media graph monitor held, so
- * reentry into media graph methods is possible, although very much discouraged!
- * You should do something non-blocking and non-reentrant (e.g. dispatch an
- * event to some thread) and return.
- * The listener is not allowed to add/remove any listeners from the stream.
- *
- * When a listener is first attached, we guarantee to send a NotifyBlockingChanged
- * callback to notify of the initial blocking state. Also, if a listener is
- * attached to a stream that has already finished, we'll call NotifyFinished.
- */
-class MediaStreamListener {
-protected:
- // Protected destructor, to discourage deletion outside of Release():
- virtual ~MediaStreamListener() {}
-
-public:
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
-
- /**
- * When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
- * control loop is ready to pull, this gets called. A NotifyPull implementation
- * is allowed to call the SourceMediaStream methods that alter track
- * data. It is not allowed to make other MediaStream API calls, including
- * calls to add or remove MediaStreamListeners. It is not allowed to block
- * for any length of time.
- * aDesiredTime is the stream time we would like to get data up to. Data
- * beyond this point will not be played until NotifyPull runs again, so there's
- * not much point in providing it. Note that if the stream is blocked for
- * some reason, then data before aDesiredTime may not be played immediately.
- */
- virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
-
- enum Blocking {
- BLOCKED,
- UNBLOCKED
- };
- /**
- * Notify that the blocking status of the stream changed. The initial state
- * is assumed to be BLOCKED.
- */
- virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
-
- /**
- * Notify that the stream has data in each track
- * for the stream's current time. Once this state becomes true, it will
- * always be true since we block stream time from progressing to times where
- * there isn't data in each track.
- */
- virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
-
- /**
- * Notify that the stream output is advancing. aCurrentTime is the graph's
- * current time. MediaStream::GraphTimeToStreamTime can be used to get the
- * stream time.
- */
- virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
-
- enum MediaStreamGraphEvent {
- EVENT_FINISHED,
- EVENT_REMOVED,
- EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
- EVENT_HAS_NO_DIRECT_LISTENERS, // transition to no direct listeners
- };
-
- /**
- * Notify that an event has occurred on the Stream
- */
- virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
-
- // maskable flags, not a simple enumerated value
- enum {
- TRACK_EVENT_CREATED = 0x01,
- TRACK_EVENT_ENDED = 0x02,
- TRACK_EVENT_UNUSED = ~(TRACK_EVENT_ENDED | TRACK_EVENT_CREATED),
- };
- /**
- * Notify that changes to one of the stream tracks have been queued.
- * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
- * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
- * at aTrackOffset (relative to the start of the stream).
- * aInputStream and aInputTrackID will be set if the changes originated
- * from an input stream's track. In practice they will only be used for
- * ProcessedMediaStreams.
- */
- virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
- StreamTime aTrackOffset,
- uint32_t aTrackEvents,
- const MediaSegment& aQueuedMedia,
- MediaStream* aInputStream = nullptr,
- TrackID aInputTrackID = TRACK_INVALID) {}
-
- /**
- * Notify queued audio data. Only audio data need to be queued. The video data
- * will be notified by MediaStreamVideoSink::SetCurrentFrame.
- */
- virtual void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
- StreamTime aTrackOffset,
- const AudioSegment& aQueuedMedia,
- MediaStream* aInputStream = nullptr,
- TrackID aInputTrackID = TRACK_INVALID) {}
-
- /**
- * Notify that all new tracks this iteration have been created.
- * This is to ensure that tracks added atomically to MediaStreamGraph
- * are also notified of atomically to MediaStreamListeners.
- */
- virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) {}
-};
-
class AudioDataListenerInterface {
protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~AudioDataListenerInterface() {}
public:
/* These are for cubeb audio input & output streams: */
/**
@@ -242,187 +122,16 @@ protected:
// Protected destructor, to discourage deletion outside of Release():
virtual ~AudioDataListener() {}
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioDataListener)
};
/**
- * This is a base class for media graph thread listener callbacks locked to
- * specific tracks. Override methods to be notified of audio or video data or
- * changes in track state.
- *
- * All notification methods are called from the media graph thread. Overriders
- * of these methods are responsible for all synchronization. Beware!
- * These methods are called without the media graph monitor held, so
- * reentry into media graph methods is possible, although very much discouraged!
- * You should do something non-blocking and non-reentrant (e.g. dispatch an
- * event to some thread) and return.
- * The listener is not allowed to add/remove any listeners from the parent
- * stream.
- *
- * If a listener is attached to a track that has already ended, we guarantee
- * to call NotifyEnded.
- */
-class MediaStreamTrackListener
-{
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamTrackListener)
-
-public:
- virtual void NotifyQueuedChanges(MediaStreamGraph* aGraph,
- StreamTime aTrackOffset,
- const MediaSegment& aQueuedMedia) {}
-
- virtual void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
- const PrincipalHandle& aNewPrincipalHandle) {}
-
- virtual void NotifyEnded() {}
-
- virtual void NotifyRemoved() {}
-
-protected:
- virtual ~MediaStreamTrackListener() {}
-};
-
-
-/**
- * This is a base class for media graph thread listener direct callbacks
- * from within AppendToTrack(). Note that your regular listener will
- * still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
- * you must be careful to ignore them if AddDirectListener was successful.
- */
-class DirectMediaStreamListener : public MediaStreamListener
-{
-public:
- virtual ~DirectMediaStreamListener() {}
-
- /*
- * This will be called on any DirectMediaStreamListener added to a
- * a SourceMediaStream when AppendToTrack() is called. The MediaSegment
- * will be the RawSegment (unresampled) if available in AppendToTrack().
- * Note that NotifyQueuedTrackChanges() calls will also still occur.
- */
- virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
- StreamTime aTrackOffset,
- uint32_t aTrackEvents,
- const MediaSegment& aMedia) {}
-};
-
-/**
- * This is a base class for media graph thread listener direct callbacks from
- * within AppendToTrack(). It is bound to a certain track and can only be
- * installed on audio tracks. Once added to a track on any stream in the graph,
- * the graph will try to install it at that track's source of media data.
- *
- * This works for TrackUnionStreams, which will forward the listener to the
- * track's input track if it exists, or wait for it to be created before
- * forwarding if it doesn't.
- * Once it reaches a SourceMediaStream, it can be successfully installed.
- * Other types of streams will fail installation since they are not supported.
- *
- * Note that this listener and others for the same track will still get
- * NotifyQueuedChanges() callbacks from the MSG tread, so you must be careful
- * to ignore them if this listener was successfully installed.
- */
-class DirectMediaStreamTrackListener : public MediaStreamTrackListener
-{
- friend class SourceMediaStream;
- friend class TrackUnionStream;
-
-public:
- /*
- * This will be called on any DirectMediaStreamTrackListener added to a
- * SourceMediaStream when AppendToTrack() is called for the listener's bound
- * track, using the thread of the AppendToTrack() caller. The MediaSegment
- * will be the RawSegment (unresampled) if available in AppendToTrack().
- * If the track is enabled at the source but has been disabled in one of the
- * streams in between the source and where it was originally added, aMedia
- * will be a disabled version of the one passed to AppendToTrack() as well.
- * Note that NotifyQueuedTrackChanges() calls will also still occur.
- */
- virtual void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
- StreamTime aTrackOffset,
- const MediaSegment& aMedia) {}
-
- /**
- * When a direct listener is processed for installation by the
- * MediaStreamGraph it will be notified with whether the installation was
- * successful or not. The results of this installation are the following:
- * TRACK_NOT_FOUND_AT_SOURCE
- * We found the source stream of media data for this track, but the track
- * didn't exist. This should only happen if you try to install the listener
- * directly to a SourceMediaStream that doesn't contain the given TrackID.
- * TRACK_TYPE_NOT_SUPPORTED
- * This is the failure when you install the listener to a non-audio track.
- * STREAM_NOT_SUPPORTED
- * While looking for the data source of this track, we found a MediaStream
- * that is not a SourceMediaStream or a TrackUnionStream.
- * SUCCESS
- * Installation was successful and this listener will start receiving
- * NotifyRealtimeData on the next AppendToTrack().
- */
- enum class InstallationResult {
- TRACK_NOT_FOUND_AT_SOURCE,
- TRACK_TYPE_NOT_SUPPORTED,
- STREAM_NOT_SUPPORTED,
- SUCCESS
- };
- virtual void NotifyDirectListenerInstalled(InstallationResult aResult) {}
- virtual void NotifyDirectListenerUninstalled() {}
-
-protected:
- virtual ~DirectMediaStreamTrackListener() {}
-
- void MirrorAndDisableSegment(AudioSegment& aFrom, AudioSegment& aTo)
- {
- aTo.Clear();
- aTo.AppendNullData(aFrom.GetDuration());
- }
-
- void NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
- StreamTime aTrackOffset,
- MediaSegment& aMedia)
- {
- if (mDisabledCount == 0) {
- NotifyRealtimeTrackData(aGraph, aTrackOffset, aMedia);
- return;
- }
-
- if (!mMedia) {
- mMedia = aMedia.CreateEmptyClone();
- }
- if (aMedia.GetType() == MediaSegment::AUDIO) {
- MirrorAndDisableSegment(static_cast<AudioSegment&>(aMedia),
- static_cast<AudioSegment&>(*mMedia));
- } else {
- MOZ_CRASH("Unsupported media type");
- }
- NotifyRealtimeTrackData(aGraph, aTrackOffset, *mMedia);
- }
-
- void IncreaseDisabled()
- {
- ++mDisabledCount;
- }
- void DecreaseDisabled()
- {
- --mDisabledCount;
- MOZ_ASSERT(mDisabledCount >= 0, "Double decrease");
- }
-
- // Matches the number of disabled streams to which this listener is attached.
- // The number of streams are those between the stream the listener was added
- // and the SourceMediaStream that is the input of the data.
- Atomic<int32_t> mDisabledCount;
-
- nsAutoPtr<MediaSegment> mMedia;
-};
-
-/**
* This is a base class for main-thread listener callbacks.
* This callback is invoked on the main thread when the main-thread-visible
* state of a stream has changed.
*
* These methods are called with the media graph monitor held, so
* reentry into general media graph methods is not possible.
* You should do something non-blocking and non-reentrant (e.g. dispatch an
* event) and return. DispatchFromMainThreadAfterNextStreamStateUpdate
@@ -442,16 +151,33 @@ struct AudioNodeSizes
{
AudioNodeSizes() : mDomNode(0), mStream(0), mEngine(0), mNodeType() {}
size_t mDomNode;
size_t mStream;
size_t mEngine;
nsCString mNodeType;
};
+class AudioNodeEngine;
+class AudioNodeExternalInputStream;
+class AudioNodeStream;
+class AudioSegment;
+class CameraPreviewMediaStream;
+class DirectMediaStreamListener;
+class DirectMediaStreamTrackListener;
+class MediaInputPort;
+class MediaStreamGraphImpl;
+class MediaStreamListener;
+class MediaStreamTrackListener;
+class ProcessedMediaStream;
+class SourceMediaStream;
+
+enum MediaStreamGraphEvent : uint32_t;
+enum TrackEventCommand : uint32_t;
+
/**
* Helper struct for binding a track listener to a specific TrackID.
*/
template<typename Listener>
struct TrackBound
{
RefPtr<Listener> mListener;
TrackID mTrackID;
@@ -531,23 +257,17 @@ class MediaStream : public mozilla::Link
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
MediaStream();
protected:
// Protected destructor, to discourage deletion outside of Release():
- virtual ~MediaStream()
- {
- MOZ_COUNT_DTOR(MediaStream);
- NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
- NS_ASSERTION(mMainThreadListeners.IsEmpty(),
- "All main thread listeners should have been removed");
- }
+ virtual ~MediaStream();
public:
/**
* Returns the graph that owns this stream.
*/
MediaStreamGraphImpl* GraphImpl();
MediaStreamGraph* Graph();
/**
@@ -859,17 +579,17 @@ protected:
// Client-set volume of this stream
struct AudioOutput {
explicit AudioOutput(void* aKey) : mKey(aKey), mVolume(1.0f) {}
void* mKey;
float mVolume;
};
nsTArray<AudioOutput> mAudioOutputs;
- nsTArray<RefPtr<VideoFrameContainer> > mVideoOutputs;
+ nsTArray<RefPtr<VideoFrameContainer>> mVideoOutputs;
// We record the last played video frame to avoid playing the frame again
// with a different frame id.
VideoFrame mLastPlayedVideoFrame;
nsTArray<RefPtr<MediaStreamListener> > mListeners;
nsTArray<TrackBound<MediaStreamTrackListener>> mTrackListeners;
nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners;
nsTArray<TrackID> mDisabledTrackIDs;
@@ -948,24 +668,17 @@ protected:
* This is a stream into which a decoder can write audio and video.
*
* Audio and video can be written on any thread, but you probably want to
* always write from the same thread to avoid unexpected interleavings.
*/
class SourceMediaStream : public MediaStream
{
public:
- explicit SourceMediaStream() :
- MediaStream(),
- mMutex("mozilla::media::SourceMediaStream"),
- mUpdateKnownTracksTime(0),
- mPullEnabled(false),
- mUpdateFinished(false),
- mNeedsMixing(false)
- {}
+ explicit SourceMediaStream();
SourceMediaStream* AsSourceStream() override { return this; }
// Media graph thread only
// Users of audio inputs go through the stream so it can track when the
// last stream referencing an input goes away, so it can close the cubeb
// input. Also note: callable on any thread (though it bounces through
@@ -987,18 +700,18 @@ public:
*/
void SetPullEnabled(bool aEnabled);
/**
* These add/remove DirectListeners, which allow bypassing the graph and any
* synchronization delays for e.g. PeerConnection, which wants the data ASAP
* and lets the far-end handle sync and playout timing.
*/
- void NotifyListenersEventImpl(MediaStreamListener::MediaStreamGraphEvent aEvent);
- void NotifyListenersEvent(MediaStreamListener::MediaStreamGraphEvent aEvent);
+ void NotifyListenersEventImpl(MediaStreamGraphEvent aEvent);
+ void NotifyListenersEvent(MediaStreamGraphEvent aEvent);
void AddDirectListener(DirectMediaStreamListener* aListener);
void RemoveDirectListener(DirectMediaStreamListener* aListener);
enum {
ADDTRACK_QUEUED = 0x01 // Queue track add until FinishAddTracks()
};
/**
* Add a new track to the stream starting at the given base time (which
@@ -1011,20 +724,17 @@ public:
{
AddTrackInternal(aID, GraphRate(), aStart, aSegment, aFlags);
}
/**
* Like AddTrack, but resamples audio from aRate to the graph rate.
*/
void AddAudioTrack(TrackID aID, TrackRate aRate, StreamTime aStart,
- AudioSegment* aSegment, uint32_t aFlags = 0)
- {
- AddTrackInternal(aID, aRate, aStart, aSegment, aFlags);
- }
+ AudioSegment* aSegment, uint32_t aFlags = 0);
/**
* Call after a series of AddTrack or AddAudioTrack calls to implement
* any pending track adds.
*/
void FinishAddTracks();
/**
@@ -1090,21 +800,20 @@ public:
*/
bool HasPendingAudioTrack();
// XXX need a Reset API
friend class MediaStreamGraphImpl;
protected:
- enum TrackCommands {
- TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,
- TRACK_END = MediaStreamListener::TRACK_EVENT_ENDED,
- TRACK_UNUSED = MediaStreamListener::TRACK_EVENT_UNUSED,
- };
+ enum TrackCommands : uint32_t;
+
+ virtual ~SourceMediaStream();
+
/**
* Data for each track that hasn't ended.
*/
struct TrackData {
TrackID mID;
// Sample rate of the input data.
TrackRate mInputRate;
// Resampler if the rate of the input track does not match the
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaStreamListener.cpp
@@ -0,0 +1,58 @@
+/* -*- 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 "MediaStreamListener.h"
+
+#include "AudioSegment.h"
+#include "VideoSegment.h"
+#include "StreamTracks.h"
+
+namespace mozilla {
+
+void
+DirectMediaStreamTrackListener::MirrorAndDisableSegment(AudioSegment& aFrom,
+ AudioSegment& aTo)
+{
+ aTo.Clear();
+ aTo.AppendNullData(aFrom.GetDuration());
+}
+
+void
+DirectMediaStreamTrackListener::MirrorAndDisableSegment(VideoSegment& aFrom,
+ VideoSegment& aTo)
+{
+ aTo.Clear();
+ for (VideoSegment::ChunkIterator it(aFrom); !it.IsEnded(); it.Next()) {
+ aTo.AppendFrame(do_AddRef(it->mFrame.GetImage()), it->GetDuration(),
+ it->mFrame.GetIntrinsicSize(), it->GetPrincipalHandle(), true);
+ }
+}
+
+void
+DirectMediaStreamTrackListener::NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
+ StreamTime aTrackOffset,
+ MediaSegment& aMedia)
+{
+ if (mDisabledCount == 0) {
+ NotifyRealtimeTrackData(aGraph, aTrackOffset, aMedia);
+ return;
+ }
+
+ if (!mMedia) {
+ mMedia = aMedia.CreateEmptyClone();
+ }
+ if (aMedia.GetType() == MediaSegment::AUDIO) {
+ MirrorAndDisableSegment(static_cast<AudioSegment&>(aMedia),
+ static_cast<AudioSegment&>(*mMedia));
+ } else if (aMedia.GetType() == MediaSegment::VIDEO) {
+ MirrorAndDisableSegment(static_cast<VideoSegment&>(aMedia),
+ static_cast<VideoSegment&>(*mMedia));
+ } else {
+ MOZ_CRASH("Unsupported media type");
+ }
+ NotifyRealtimeTrackData(aGraph, aTrackOffset, *mMedia);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaStreamListener.h
@@ -0,0 +1,289 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 MOZILLA_MEDIASTREAMLISTENER_h_
+#define MOZILLA_MEDIASTREAMLISTENER_h_
+
+namespace mozilla {
+
+class MediaStream;
+class MediaStreamGraph;
+
+enum MediaStreamGraphEvent : uint32_t {
+ EVENT_FINISHED,
+ EVENT_REMOVED,
+ EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
+ EVENT_HAS_NO_DIRECT_LISTENERS, // transition to no direct listeners
+};
+
+// maskable flags, not a simple enumerated value
+enum TrackEventCommand : uint32_t {
+ TRACK_EVENT_NONE = 0x00,
+ TRACK_EVENT_CREATED = 0x01,
+ TRACK_EVENT_ENDED = 0x02,
+ TRACK_EVENT_UNUSED = ~(TRACK_EVENT_ENDED | TRACK_EVENT_CREATED),
+};
+
+/**
+ * This is a base class for media graph thread listener callbacks.
+ * Override methods to be notified of audio or video data or changes in stream
+ * state.
+ *
+ * This can be used by stream recorders or network connections that receive
+ * stream input. It could also be used for debugging.
+ *
+ * All notification methods are called from the media graph thread. Overriders
+ * of these methods are responsible for all synchronization. Beware!
+ * These methods are called without the media graph monitor held, so
+ * reentry into media graph methods is possible, although very much discouraged!
+ * You should do something non-blocking and non-reentrant (e.g. dispatch an
+ * event to some thread) and return.
+ * The listener is not allowed to add/remove any listeners from the stream.
+ *
+ * When a listener is first attached, we guarantee to send a NotifyBlockingChanged
+ * callback to notify of the initial blocking state. Also, if a listener is
+ * attached to a stream that has already finished, we'll call NotifyFinished.
+ */
+class MediaStreamListener {
+protected:
+ // Protected destructor, to discourage deletion outside of Release():
+ virtual ~MediaStreamListener() {}
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
+
+ /**
+ * When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
+ * control loop is ready to pull, this gets called. A NotifyPull implementation
+ * is allowed to call the SourceMediaStream methods that alter track
+ * data. It is not allowed to make other MediaStream API calls, including
+ * calls to add or remove MediaStreamListeners. It is not allowed to block
+ * for any length of time.
+ * aDesiredTime is the stream time we would like to get data up to. Data
+ * beyond this point will not be played until NotifyPull runs again, so there's
+ * not much point in providing it. Note that if the stream is blocked for
+ * some reason, then data before aDesiredTime may not be played immediately.
+ */
+ virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
+
+ enum Blocking {
+ BLOCKED,
+ UNBLOCKED
+ };
+ /**
+ * Notify that the blocking status of the stream changed. The initial state
+ * is assumed to be BLOCKED.
+ */
+ virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
+
+ /**
+ * Notify that the stream has data in each track
+ * for the stream's current time. Once this state becomes true, it will
+ * always be true since we block stream time from progressing to times where
+ * there isn't data in each track.
+ */
+ virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
+
+ /**
+ * Notify that the stream output is advancing. aCurrentTime is the graph's
+ * current time. MediaStream::GraphTimeToStreamTime can be used to get the
+ * stream time.
+ */
+ virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
+
+ /**
+ * Notify that an event has occurred on the Stream
+ */
+ virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
+
+ /**
+ * Notify that changes to one of the stream tracks have been queued.
+ * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
+ * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
+ * at aTrackOffset (relative to the start of the stream).
+ * aInputStream and aInputTrackID will be set if the changes originated
+ * from an input stream's track. In practice they will only be used for
+ * ProcessedMediaStreams.
+ */
+ virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
+ StreamTime aTrackOffset,
+ TrackEventCommand aTrackEvents,
+ const MediaSegment& aQueuedMedia,
+ MediaStream* aInputStream = nullptr,
+ TrackID aInputTrackID = TRACK_INVALID) {}
+
+ /**
+ * Notify queued audio data. Only audio data need to be queued. The video data
+ * will be notified by MediaStreamVideoSink::SetCurrentFrame.
+ */
+ virtual void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
+ StreamTime aTrackOffset,
+ const AudioSegment& aQueuedMedia,
+ MediaStream* aInputStream = nullptr,
+ TrackID aInputTrackID = TRACK_INVALID) {}
+
+ /**
+ * Notify that all new tracks this iteration have been created.
+ * This is to ensure that tracks added atomically to MediaStreamGraph
+ * are also notified of atomically to MediaStreamListeners.
+ */
+ virtual void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) {}
+};
+
+/**
+ * This is a base class for media graph thread listener callbacks locked to
+ * specific tracks. Override methods to be notified of audio or video data or
+ * changes in track state.
+ *
+ * All notification methods are called from the media graph thread. Overriders
+ * of these methods are responsible for all synchronization. Beware!
+ * These methods are called without the media graph monitor held, so
+ * reentry into media graph methods is possible, although very much discouraged!
+ * You should do something non-blocking and non-reentrant (e.g. dispatch an
+ * event to some thread) and return.
+ * The listener is not allowed to add/remove any listeners from the parent
+ * stream.
+ *
+ * If a listener is attached to a track that has already ended, we guarantee
+ * to call NotifyEnded.
+ */
+class MediaStreamTrackListener
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamTrackListener)
+
+public:
+ virtual void NotifyQueuedChanges(MediaStreamGraph* aGraph,
+ StreamTime aTrackOffset,
+ const MediaSegment& aQueuedMedia) {}
+
+ virtual void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
+ const PrincipalHandle& aNewPrincipalHandle) {}
+
+ virtual void NotifyEnded() {}
+
+ virtual void NotifyRemoved() {}
+
+protected:
+ virtual ~MediaStreamTrackListener() {}
+};
+
+
+/**
+ * This is a base class for media graph thread listener direct callbacks
+ * from within AppendToTrack(). Note that your regular listener will
+ * still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
+ * you must be careful to ignore them if AddDirectListener was successful.
+ */
+class DirectMediaStreamListener : public MediaStreamListener
+{
+public:
+ virtual ~DirectMediaStreamListener() {}
+
+ /*
+ * This will be called on any DirectMediaStreamListener added to a
+ * a SourceMediaStream when AppendToTrack() is called. The MediaSegment
+ * will be the RawSegment (unresampled) if available in AppendToTrack().
+ * Note that NotifyQueuedTrackChanges() calls will also still occur.
+ */
+ virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
+ StreamTime aTrackOffset,
+ uint32_t aTrackEvents,
+ const MediaSegment& aMedia) {}
+};
+
+/**
+ * This is a base class for media graph thread listener direct callbacks from
+ * within AppendToTrack(). It is bound to a certain track and can only be
+ * installed on audio tracks. Once added to a track on any stream in the graph,
+ * the graph will try to install it at that track's source of media data.
+ *
+ * This works for TrackUnionStreams, which will forward the listener to the
+ * track's input track if it exists, or wait for it to be created before
+ * forwarding if it doesn't.
+ * Once it reaches a SourceMediaStream, it can be successfully installed.
+ * Other types of streams will fail installation since they are not supported.
+ *
+ * Note that this listener and others for the same track will still get
+ * NotifyQueuedChanges() callbacks from the MSG tread, so you must be careful
+ * to ignore them if this listener was successfully installed.
+ */
+class DirectMediaStreamTrackListener : public MediaStreamTrackListener
+{
+ friend class SourceMediaStream;
+ friend class TrackUnionStream;
+
+public:
+ /*
+ * This will be called on any DirectMediaStreamTrackListener added to a
+ * SourceMediaStream when AppendToTrack() is called for the listener's bound
+ * track, using the thread of the AppendToTrack() caller. The MediaSegment
+ * will be the RawSegment (unresampled) if available in AppendToTrack().
+ * If the track is enabled at the source but has been disabled in one of the
+ * streams in between the source and where it was originally added, aMedia
+ * will be a disabled version of the one passed to AppendToTrack() as well.
+ * Note that NotifyQueuedTrackChanges() calls will also still occur.
+ */
+ virtual void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
+ StreamTime aTrackOffset,
+ const MediaSegment& aMedia) {}
+
+ /**
+ * When a direct listener is processed for installation by the
+ * MediaStreamGraph it will be notified with whether the installation was
+ * successful or not. The results of this installation are the following:
+ * TRACK_NOT_FOUND_AT_SOURCE
+ * We found the source stream of media data for this track, but the track
+ * didn't exist. This should only happen if you try to install the listener
+ * directly to a SourceMediaStream that doesn't contain the given TrackID.
+ * TRACK_TYPE_NOT_SUPPORTED
+ * This is the failure when you install the listener to a
+ * non-(audio or video) track.
+ * STREAM_NOT_SUPPORTED
+ * While looking for the data source of this track, we found a MediaStream
+ * that is not a SourceMediaStream or a TrackUnionStream.
+ * SUCCESS
+ * Installation was successful and this listener will start receiving
+ * NotifyRealtimeData on the next AppendToTrack().
+ */
+ enum class InstallationResult {
+ TRACK_NOT_FOUND_AT_SOURCE,
+ TRACK_TYPE_NOT_SUPPORTED,
+ STREAM_NOT_SUPPORTED,
+ SUCCESS
+ };
+ virtual void NotifyDirectListenerInstalled(InstallationResult aResult) {}
+ virtual void NotifyDirectListenerUninstalled() {}
+
+protected:
+ virtual ~DirectMediaStreamTrackListener() {}
+
+ void MirrorAndDisableSegment(AudioSegment& aFrom, AudioSegment& aTo);
+ void MirrorAndDisableSegment(VideoSegment& aFrom, VideoSegment& aTo);
+ void NotifyRealtimeTrackDataAndApplyTrackDisabling(MediaStreamGraph* aGraph,
+ StreamTime aTrackOffset,
+ MediaSegment& aMedia);
+
+ void IncreaseDisabled()
+ {
+ ++mDisabledCount;
+ }
+ void DecreaseDisabled()
+ {
+ --mDisabledCount;
+ MOZ_ASSERT(mDisabledCount >= 0, "Double decrease");
+ }
+
+ // Matches the number of disabled streams to which this listener is attached.
+ // The number of streams are those between the stream the listener was added
+ // and the SourceMediaStream that is the input of the data.
+ Atomic<int32_t> mDisabledCount;
+
+ nsAutoPtr<MediaSegment> mMedia;
+};
+
+} // namespace mozilla
+
+#endif // MOZILLA_MEDIASTREAMLISTENER_h_
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -1,14 +1,15 @@
/* -*- 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 "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/unused.h"
#include "AudioSegment.h"
#include "VideoSegment.h"
#include "nsContentUtils.h"
#include "nsIAppShell.h"
#include "nsIObserver.h"
@@ -195,17 +196,17 @@ TrackUnionStream::TrackUnionStream() :
// samples in our input stream to go just beyond the destination time.
StreamTime outputStart = GraphTimeToStreamTimeWithBlocking(aFrom);
nsAutoPtr<MediaSegment> segment;
segment = aTrack->GetSegment()->CreateEmptyClone();
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
l->NotifyQueuedTrackChanges(Graph(), id, outputStart,
- MediaStreamListener::TRACK_EVENT_CREATED,
+ TrackEventCommand::TRACK_EVENT_CREATED,
*segment,
aPort->GetSource(), aTrack->GetID());
}
segment->AppendNullData(outputStart);
StreamTracks::Track* track =
&mTracks.AddTrack(id, outputStart, segment.forget());
STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p added track %d for input stream %p track %d, start ticks %lld",
this, track->GetID(), aPort->GetSource(), aTrack->GetID(),
@@ -251,17 +252,17 @@ TrackUnionStream::TrackUnionStream() :
return;
STREAM_LOG(LogLevel::Debug, ("TrackUnionStream %p ending track %d", this, outputTrack->GetID()));
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
StreamTime offset = outputTrack->GetSegment()->GetDuration();
nsAutoPtr<MediaSegment> segment;
segment = outputTrack->GetSegment()->CreateEmptyClone();
l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(), offset,
- MediaStreamListener::TRACK_EVENT_ENDED,
+ TrackEventCommand::TRACK_EVENT_ENDED,
*segment,
mTrackMap[aIndex].mInputPort->GetSource(),
mTrackMap[aIndex].mInputTrackID);
}
for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
if (b.mTrackID == outputTrack->GetID()) {
b.mListener->NotifyEnded();
}
@@ -329,17 +330,17 @@ TrackUnionStream::TrackUnionStream() :
l->NotifyQueuedAudioData(Graph(), outputTrack->GetID(),
outputStart,
*static_cast<AudioSegment*>(segment),
map->mInputPort->GetSource(),
map->mInputTrackID);
} else {
// This part will be removed in bug 1201363.
l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
- outputStart, 0, *segment,
+ outputStart, TrackEventCommand::TRACK_EVENT_NONE, *segment,
map->mInputPort->GetSource(),
map->mInputTrackID);
}
}
for (TrackBound<MediaStreamTrackListener>& b : mTrackListeners) {
if (b.mTrackID != outputTrack->GetID()) {
continue;
}
--- a/dom/media/encoder/MediaEncoder.cpp
+++ b/dom/media/encoder/MediaEncoder.cpp
@@ -61,25 +61,25 @@ MediaEncoder::NotifyRealtimeData(MediaSt
}
}
}
void
MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
TrackID aID,
StreamTime aTrackOffset,
- uint32_t aTrackEvents,
+ TrackEventCommand aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID)
{
if (!mDirectConnected) {
NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, aQueuedMedia);
} else {
- if (aTrackEvents != 0) {
+ if (aTrackEvents != TrackEventCommand::TRACK_EVENT_NONE) {
// forward events (TRACK_EVENT_ENDED) but not the media
if (aQueuedMedia.GetType() == MediaSegment::VIDEO) {
VideoSegment segment;
NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, segment);
} else {
AudioSegment segment;
NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, segment);
}
@@ -120,17 +120,17 @@ MediaEncoder::NotifyQueuedAudioData(Medi
mSuspended = RECORD_NOT_SUSPENDED; // no video
}
}
}
}
void
MediaEncoder::NotifyEvent(MediaStreamGraph* aGraph,
- MediaStreamListener::MediaStreamGraphEvent event)
+ MediaStreamGraphEvent event)
{
// In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
LOG(LogLevel::Debug, ("NotifyRemoved in [MediaEncoder]."));
if (mAudioEncoder) {
mAudioEncoder->NotifyEvent(aGraph, event);
}
if (mVideoEncoder) {
mVideoEncoder->NotifyEvent(aGraph, event);
--- a/dom/media/encoder/MediaEncoder.h
+++ b/dom/media/encoder/MediaEncoder.h
@@ -5,16 +5,17 @@
#ifndef MediaEncoder_h_
#define MediaEncoder_h_
#include "mozilla/DebugOnly.h"
#include "TrackEncoder.h"
#include "ContainerWriter.h"
#include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
#include "nsAutoPtr.h"
#include "nsIMemoryReporter.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Atomics.h"
namespace mozilla {
/**
@@ -120,17 +121,17 @@ public :
const MediaSegment& aRealtimeMedia) override;
/**
* Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
* track data in form of MediaSegment.
*/
void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
StreamTime aTrackOffset,
- uint32_t aTrackEvents,
+ TrackEventCommand aTrackEvents,
const MediaSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override;
/**
* Notifed by the control loop of MediaStreamGraph; aQueueMedia is the audio
* data in the form of an AudioSegment.
*/
@@ -139,17 +140,17 @@ public :
const AudioSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override;
/**
* * Notified the stream is being removed.
*/
void NotifyEvent(MediaStreamGraph* aGraph,
- MediaStreamListener::MediaStreamGraphEvent event) override;
+ MediaStreamGraphEvent event) override;
/**
* Creates an encoder with a given MIME type. Returns null if we are unable
* to create the encoder. For now, default aMIMEType to "audio/ogg" and use
* Ogg+Opus if it is empty.
*/
static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
uint32_t aAudioBitrate, uint32_t aVideoBitrate,
--- a/dom/media/encoder/TrackEncoder.cpp
+++ b/dom/media/encoder/TrackEncoder.cpp
@@ -1,15 +1,16 @@
/* -*- 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 "TrackEncoder.h"
#include "AudioChannelFormat.h"
#include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
#include "mozilla/Logging.h"
#include "VideoUtils.h"
#undef LOG
#ifdef MOZ_WIDGET_GONK
#include <android/log.h>
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args);
#else
@@ -36,16 +37,24 @@ TrackEncoder::TrackEncoder()
, mInitialized(false)
, mEndOfStream(false)
, mCanceled(false)
, mInitCounter(0)
, mNotInitDuration(0)
{
}
+void TrackEncoder::NotifyEvent(MediaStreamGraph* aGraph,
+ MediaStreamGraphEvent event)
+{
+ if (event == MediaStreamGraphEvent::EVENT_REMOVED) {
+ NotifyEndOfStream();
+ }
+}
+
void
AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
TrackID aID,
StreamTime aTrackOffset,
uint32_t aTrackEvents,
const MediaSegment& aQueuedMedia)
{
if (mCanceled) {
@@ -86,17 +95,17 @@ AudioTrackEncoder::NotifyQueuedTrackChan
}
}
// Append and consume this raw segment.
AppendAudioSegment(audio);
// The stream has stopped and reached the end of track.
- if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
+ if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
LOG("[AudioTrackEncoder]: Receive TRACK_EVENT_ENDED .");
NotifyEndOfStream();
}
}
void
AudioTrackEncoder::NotifyEndOfStream()
{
@@ -227,17 +236,17 @@ VideoTrackEncoder::NotifyQueuedTrackChan
NotifyEndOfStream();
return;
}
}
AppendVideoSegment(video);
// The stream has stopped and reached the end of track.
- if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
+ if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
LOG("[VideoTrackEncoder]: Receive TRACK_EVENT_ENDED .");
NotifyEndOfStream();
}
}
nsresult
VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment)
--- a/dom/media/encoder/TrackEncoder.h
+++ b/dom/media/encoder/TrackEncoder.h
@@ -43,22 +43,17 @@ public:
uint32_t aTrackEvents,
const MediaSegment& aQueuedMedia) = 0;
/**
* Notified by the same callback of MediaEncoder when it has been removed from
* MediaStreamGraph. Called on the MediaStreamGraph thread.
*/
void NotifyEvent(MediaStreamGraph* aGraph,
- MediaStreamListener::MediaStreamGraphEvent event)
- {
- if (event == MediaStreamListener::MediaStreamGraphEvent::EVENT_REMOVED) {
- NotifyEndOfStream();
- }
- }
+ MediaStreamGraphEvent event);
/**
* Creates and sets up meta data for a specific codec, called on the worker
* thread.
*/
virtual already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
/**
--- a/dom/media/gtest/TestVideoTrackEncoder.cpp
+++ b/dom/media/gtest/TestVideoTrackEncoder.cpp
@@ -4,16 +4,17 @@
#include "gtest/gtest.h"
#include <algorithm>
#include "mozilla/ArrayUtils.h"
#include "VP8TrackEncoder.h"
#include "ImageContainer.h"
#include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
#include "WebMWriter.h" // TODO: it's weird to include muxer header to get the class definition of VP8 METADATA
using ::testing::TestWithParam;
using ::testing::Values;
using namespace mozilla::layers;
using namespace mozilla;
@@ -291,16 +292,16 @@ TEST(VP8VideoTrackEncoder, EncodeComplet
{
// Initiate VP8 encoder
TestVP8TrackEncoder encoder;
InitParam param = {true, 640, 480, 90000};
encoder.TestInit(param);
// track end notification.
VideoSegment segment;
- encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, MediaStreamListener::TRACK_EVENT_ENDED, segment);
+ encoder.NotifyQueuedTrackChanges(nullptr, 0, 0, TrackEventCommand::TRACK_EVENT_ENDED, segment);
// Pull Encoded Data back from encoder. Since we have sent
// EOS to encoder, encoder.GetEncodedTrack should return
// NS_OK immidiately.
EncodedFrameContainer container;
EXPECT_TRUE(NS_SUCCEEDED(encoder.GetEncodedTrack(container)));
}
--- a/dom/media/imagecapture/CaptureTask.h
+++ b/dom/media/imagecapture/CaptureTask.h
@@ -3,16 +3,17 @@
/* 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 CAPTURETASK_H
#define CAPTURETASK_H
#include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
#include "PrincipalChangeObserver.h"
namespace mozilla {
namespace dom {
class Blob;
class ImageCapture;
class MediaStreamTrack;
--- a/dom/media/mediasink/DecodedStream.cpp
+++ b/dom/media/mediasink/DecodedStream.cpp
@@ -8,16 +8,17 @@
#include "mozilla/gfx/Point.h"
#include "mozilla/SyncRunnable.h"
#include "AudioSegment.h"
#include "DecodedStream.h"
#include "MediaData.h"
#include "MediaQueue.h"
#include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
#include "OutputStreamManager.h"
#include "SharedBuffer.h"
#include "VideoSegment.h"
#include "VideoUtils.h"
namespace mozilla {
/*
@@ -25,17 +26,16 @@ namespace mozilla {
* way to DecodedStreamGraphListener from DecodedStream.
*/
struct PlaybackInfoInit {
int64_t mStartTime;
MediaInfo mInfo;
};
class DecodedStreamGraphListener : public MediaStreamListener {
- typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
public:
DecodedStreamGraphListener(MediaStream* aStream,
MozPromiseHolder<GenericPromise>&& aPromise)
: mMutex("DecodedStreamGraphListener::mMutex")
, mStream(aStream)
, mLastOutputTime(aStream->StreamTimeToMicroseconds(aStream->GetCurrentTime()))
{
mFinishPromise = Move(aPromise);
@@ -47,17 +47,17 @@ public:
if (mStream) {
mLastOutputTime = mStream->StreamTimeToMicroseconds(
mStream->GraphTimeToStreamTime(aCurrentTime));
}
}
void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
{
- if (event == EVENT_FINISHED) {
+ if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
nsCOMPtr<nsIRunnable> event =
NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
}
}
void DoNotifyFinished()
{
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -122,16 +122,17 @@ EXPORTS += [
'MediaPrefs.h',
'MediaQueue.h',
'MediaRecorder.h',
'MediaResource.h',
'MediaResourceCallback.h',
'MediaSegment.h',
'MediaStatistics.h',
'MediaStreamGraph.h',
+ 'MediaStreamListener.h',
'MediaTimer.h',
'MediaTrack.h',
'MediaTrackList.h',
'MP3Decoder.h',
'MP3Demuxer.h',
'MP3FrameParser.h',
'NextFrameSeekTask.h',
'nsIDocumentActivity.h',
@@ -233,16 +234,17 @@ UNIFIED_SOURCES += [
'MediaInfo.cpp',
'MediaManager.cpp',
'MediaPrefs.cpp',
'MediaRecorder.cpp',
'MediaResource.cpp',
'MediaShutdownManager.cpp',
'MediaStreamError.cpp',
'MediaStreamGraph.cpp',
+ 'MediaStreamListener.cpp',
'MediaStreamTrack.cpp',
'MediaTimer.cpp',
'MediaTrack.cpp',
'MediaTrackList.cpp',
'MP3Decoder.cpp',
'MP3Demuxer.cpp',
'MP3FrameParser.cpp',
'NextFrameSeekTask.cpp',
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -1,16 +1,17 @@
/* -*- 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 "AudioNodeStream.h"
#include "MediaStreamGraphImpl.h"
+#include "MediaStreamListener.h"
#include "AudioNodeEngine.h"
#include "ThreeDPoint.h"
#include "AudioChannelFormat.h"
#include "AudioParamTimeline.h"
#include "AudioContext.h"
#include "nsMathUtils.h"
using namespace mozilla::dom;
@@ -642,32 +643,32 @@ AudioNodeStream::AdvanceOutputSegment()
}
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
AudioChunk copyChunk = mLastChunks[0].AsAudioChunk();
AudioSegment tmpSegment;
tmpSegment.AppendAndConsumeChunk(©Chunk);
l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
- segment->GetDuration(), 0, tmpSegment);
+ segment->GetDuration(), TrackEventCommand::TRACK_EVENT_NONE, tmpSegment);
}
}
void
AudioNodeStream::FinishOutput()
{
StreamTracks::Track* track = EnsureTrack(AUDIO_TRACK);
track->SetEnded();
for (uint32_t j = 0; j < mListeners.Length(); ++j) {
MediaStreamListener* l = mListeners[j];
AudioSegment emptySegment;
l->NotifyQueuedTrackChanges(Graph(), AUDIO_TRACK,
track->GetSegment()->GetDuration(),
- MediaStreamListener::TRACK_EVENT_ENDED, emptySegment);
+ TrackEventCommand::TRACK_EVENT_ENDED, emptySegment);
}
}
void
AudioNodeStream::AddInput(MediaInputPort* aPort)
{
ProcessedMediaStream::AddInput(aPort);
AudioNodeStream* ns = aPort->GetSource()->AsAudioNodeStream();
--- a/dom/media/webspeech/recognition/SpeechStreamListener.cpp
+++ b/dom/media/webspeech/recognition/SpeechStreamListener.cpp
@@ -80,15 +80,15 @@ SpeechStreamListener::ConvertAndDispatch
int16_t* to = static_cast<int16_t*>(samples->Data());
ConvertAudioSamplesWithScale(aData, to, aDuration, aVolume);
mRecognition->FeedAudioData(samples.forget(), aDuration, this, aTrackRate);
}
void
SpeechStreamListener::NotifyEvent(MediaStreamGraph* aGraph,
- MediaStreamListener::MediaStreamGraphEvent event)
+ MediaStreamGraphEvent event)
{
// TODO dispatch SpeechEnd event so services can be informed
}
} // namespace dom
} // namespace mozilla
--- a/dom/media/webspeech/recognition/SpeechStreamListener.h
+++ b/dom/media/webspeech/recognition/SpeechStreamListener.h
@@ -3,16 +3,17 @@
/* 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 mozilla_dom_SpeechStreamListener_h
#define mozilla_dom_SpeechStreamListener_h
#include "MediaStreamGraph.h"
+#include "MediaStreamListener.h"
#include "AudioSegment.h"
namespace mozilla {
class AudioSegment;
namespace dom {
@@ -26,17 +27,17 @@ public:
void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
StreamTime aTrackOffset,
const AudioSegment& aQueuedMedia,
MediaStream* aInputStream,
TrackID aInputTrackID) override;
void NotifyEvent(MediaStreamGraph* aGraph,
- MediaStreamListener::MediaStreamGraphEvent event) override;
+ MediaStreamGraphEvent event) override;
private:
template<typename SampleFormatType>
void ConvertAndDispatchAudioChunk(int aDuration, float aVolume, SampleFormatType* aData, TrackRate aTrackRate);
RefPtr<SpeechRecognition> mRecognition;
};
} // namespace dom
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -2,18 +2,20 @@
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 "AudioChannelAgent.h"
#include "AudioChannelService.h"
#include "AudioSegment.h"
+#include "MediaStreamListener.h"
#include "nsSpeechTask.h"
#include "nsSynthVoiceRegistry.h"
+#include "SharedBuffer.h"
#include "SpeechSynthesis.h"
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
// GetTickCount() and conflicts with nsSpeechTask::GetCurrentTime().
#ifdef GetCurrentTime
#undef GetCurrentTime
#endif
@@ -48,34 +50,34 @@ public:
{
if (mSpeechTask) {
mSpeechTask->DispatchEndInner(mSpeechTask->GetCurrentTime(),
mSpeechTask->GetCurrentCharOffset());
}
}
void NotifyEvent(MediaStreamGraph* aGraph,
- MediaStreamListener::MediaStreamGraphEvent event) override
+ MediaStreamGraphEvent event) override
{
switch (event) {
- case EVENT_FINISHED:
+ case MediaStreamGraphEvent::EVENT_FINISHED:
{
if (!mStarted) {
mStarted = true;
nsCOMPtr<nsIRunnable> startRunnable =
NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(startRunnable.forget());
}
nsCOMPtr<nsIRunnable> endRunnable =
NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
aGraph->DispatchToMainThreadAfterStreamStateUpdate(endRunnable.forget());
}
break;
- case EVENT_REMOVED:
+ case MediaStreamGraphEvent::EVENT_REMOVED:
mSpeechTask = nullptr;
// Dereference MediaStream to destroy safety
mStream = nullptr;
break;
default:
break;
}
}
--- a/dom/media/webspeech/synth/nsSpeechTask.h
+++ b/dom/media/webspeech/synth/nsSpeechTask.h
@@ -8,16 +8,19 @@
#define mozilla_dom_nsSpeechTask_h
#include "MediaStreamGraph.h"
#include "SpeechSynthesisUtterance.h"
#include "nsIAudioChannelAgent.h"
#include "nsISpeechService.h"
namespace mozilla {
+
+class SharedBuffer;
+
namespace dom {
class SpeechSynthesisUtterance;
class SpeechSynthesis;
class SynthStreamListener;
class nsSpeechTask : public nsISpeechTask
, public nsIAudioChannelAgentCallback
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -19,16 +19,17 @@
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
#include "VideoSegment.h"
#include "Layers.h"
#include "LayersLogging.h"
#include "ImageTypes.h"
#include "ImageContainer.h"
#include "DOMMediaStream.h"
#include "MediaStreamTrack.h"
+#include "MediaStreamListener.h"
#include "VideoUtils.h"
#ifdef WEBRTC_GONK
#include "GrallocImages.h"
#include "mozilla/layers/GrallocTextureClient.h"
#endif
#endif
#include "nsError.h"
--- a/media/webrtc/signaling/test/FakeMediaStreams.h
+++ b/media/webrtc/signaling/test/FakeMediaStreams.h
@@ -45,16 +45,19 @@ class MediaStreamGraph;
static MediaStreamGraph* gGraph;
struct AudioChannel {
enum {
Normal
};
};
+enum MediaStreamGraphEvent : uint32_t;
+enum TrackEventCommand : uint32_t;
+
class MediaStreamGraph {
public:
// Keep this in sync with the enum in MediaStreamGraph.h
enum GraphDriverType {
AUDIO_THREAD_DRIVER,
SYSTEM_THREAD_DRIVER,
OFFLINE_THREAD_DRIVER
};
@@ -84,17 +87,17 @@ class Fake_SourceMediaStream;
class Fake_MediaStreamListener
{
protected:
virtual ~Fake_MediaStreamListener() {}
public:
virtual void NotifyQueuedTrackChanges(mozilla::MediaStreamGraph* aGraph, mozilla::TrackID aID,
mozilla::StreamTime aTrackOffset,
- uint32_t aTrackEvents,
+ mozilla::TrackEventCommand aTrackEvents,
const mozilla::MediaSegment& aQueuedMedia,
Fake_MediaStream* aInputStream,
mozilla::TrackID aInputTrackID) {}
virtual void NotifyPull(mozilla::MediaStreamGraph* aGraph, mozilla::StreamTime aDesiredTime) = 0;
virtual void NotifyQueuedAudioData(mozilla::MediaStreamGraph* aGraph, mozilla::TrackID aID,
mozilla::StreamTime aTrackOffset,
const mozilla::AudioSegment& aQueuedMedia,
Fake_MediaStream* aInputStream,
--- a/media/webrtc/signaling/test/FakeMediaStreamsImpl.h
+++ b/media/webrtc/signaling/test/FakeMediaStreamsImpl.h
@@ -123,17 +123,17 @@ void Fake_AudioStreamSource::Periodic()
AUDIO_BUFFER_SIZE,
PRINCIPAL_HANDLE_NONE);
for(std::set<RefPtr<Fake_MediaStreamListener>>::iterator it = mListeners.begin();
it != mListeners.end(); ++it) {
(*it)->NotifyQueuedTrackChanges(nullptr, // Graph
0, // TrackID
0, // Offset TODO(ekr@rtfm.com) fix
- 0, // ???
+ static_cast<mozilla::TrackEventCommand>(0), // ???
segment,
nullptr, // Input stream
-1); // Input track id
}
for(std::vector<BoundTrackListener>::iterator it = mTrackListeners.begin();
it != mTrackListeners.end(); ++it) {
it->mListener->NotifyQueuedChanges(nullptr, // Graph
0, // Offset TODO(ekr@rtfm.com) fix