Bug 1060738 - Switch to using chromium's Thread/tasks in MediaManager. On Windows, use MessagePumpForNonMainUIThreads for the background media thread. r=jesup

This commit is contained in:
Jim Mathies 2014-09-12 09:49:39 -05:00
Родитель 9d266c73b6
Коммит 041794c1f3
3 изменённых файлов: 165 добавлений и 128 удалений

Просмотреть файл

@ -149,12 +149,6 @@
#include "nsHtml5TreeOpExecutor.h" #include "nsHtml5TreeOpExecutor.h"
#include "mozilla/dom/HTMLLinkElement.h" #include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/dom/HTMLMediaElement.h" #include "mozilla/dom/HTMLMediaElement.h"
#ifdef MOZ_MEDIA_NAVIGATOR
#include "mozilla/MediaManager.h"
#endif // MOZ_MEDIA_NAVIGATOR
#ifdef MOZ_WEBRTC
#include "IPeerConnection.h"
#endif // MOZ_WEBRTC
#include "mozAutoDocUpdate.h" #include "mozAutoDocUpdate.h"
#include "nsGlobalWindow.h" #include "nsGlobalWindow.h"
@ -229,6 +223,13 @@
#include "nsWindowMemoryReporter.h" #include "nsWindowMemoryReporter.h"
#include "nsLocation.h" #include "nsLocation.h"
#ifdef MOZ_MEDIA_NAVIGATOR
#include "mozilla/MediaManager.h"
#endif // MOZ_MEDIA_NAVIGATOR
#ifdef MOZ_WEBRTC
#include "IPeerConnection.h"
#endif // MOZ_WEBRTC
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;

Просмотреть файл

@ -851,15 +851,13 @@ public:
// Dispatch to the media thread to ask it to start the sources, // Dispatch to the media thread to ask it to start the sources,
// because that can take a while. // because that can take a while.
// Pass ownership of trackunion to the MediaOperationRunnable // Pass ownership of trackunion to the MediaOperationTask
// to ensure it's kept alive until the MediaOperationRunnable runs (at least). // to ensure it's kept alive until the MediaOperationTask runs (at least).
nsIThread *mediaThread = MediaManager::GetThread(); MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
nsRefPtr<MediaOperationRunnable> runnable( new MediaOperationTask(MEDIA_START, mListener, trackunion,
new MediaOperationRunnable(MEDIA_START, mListener, trackunion, tracksAvailableCallback,
tracksAvailableCallback, mAudioSource, mVideoSource, false, mWindowID,
mAudioSource, mVideoSource, false, mWindowID, mError.forget()));
mError.forget()));
mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
// We won't need mError now. // We won't need mError now.
mError = nullptr; mError = nullptr;
@ -1050,10 +1048,10 @@ static SourceSet *
* Do not run this on the main thread. The success and error callbacks *MUST* * Do not run this on the main thread. The success and error callbacks *MUST*
* be dispatched on the main thread! * be dispatched on the main thread!
*/ */
class GetUserMediaRunnable : public nsRunnable class GetUserMediaTask : public Task
{ {
public: public:
GetUserMediaRunnable( GetUserMediaTask(
const MediaStreamConstraints& aConstraints, const MediaStreamConstraints& aConstraints,
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess, already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError, already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
@ -1074,7 +1072,7 @@ public:
* The caller can also choose to provide their own backend instead of * The caller can also choose to provide their own backend instead of
* using the one provided by MediaManager::GetBackend. * using the one provided by MediaManager::GetBackend.
*/ */
GetUserMediaRunnable( GetUserMediaTask(
const MediaStreamConstraints& aConstraints, const MediaStreamConstraints& aConstraints,
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess, already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError, already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
@ -1092,7 +1090,7 @@ public:
, mManager(MediaManager::GetInstance()) , mManager(MediaManager::GetInstance())
{} {}
~GetUserMediaRunnable() { ~GetUserMediaTask() {
} }
void void
@ -1106,7 +1104,7 @@ public:
NS_DispatchToMainThread(runnable); NS_DispatchToMainThread(runnable);
} }
NS_IMETHOD void
Run() Run()
{ {
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
@ -1123,7 +1121,7 @@ public:
if (!mDeviceChosen) { if (!mDeviceChosen) {
nsresult rv = SelectDevice(backend); nsresult rv = SelectDevice(backend);
if (rv != NS_OK) { if (rv != NS_OK) {
return rv; return;
} }
} }
@ -1131,12 +1129,12 @@ public:
if (mConstraints.mPicture && if (mConstraints.mPicture &&
(IsOn(mConstraints.mAudio) || IsOn(mConstraints.mVideo))) { (IsOn(mConstraints.mAudio) || IsOn(mConstraints.mVideo))) {
Fail(NS_LITERAL_STRING("NOT_SUPPORTED_ERR")); Fail(NS_LITERAL_STRING("NOT_SUPPORTED_ERR"));
return NS_OK; return;
} }
if (mConstraints.mPicture) { if (mConstraints.mPicture) {
ProcessGetUserMediaSnapshot(mVideoDevice->GetSource(), 0); ProcessGetUserMediaSnapshot(mVideoDevice->GetSource(), 0);
return NS_OK; return;
} }
// There's a bug in the permission code that can leave us with mAudio but no audio device // There's a bug in the permission code that can leave us with mAudio but no audio device
@ -1144,7 +1142,6 @@ public:
mAudioDevice->GetSource() : nullptr), mAudioDevice->GetSource() : nullptr),
((IsOn(mConstraints.mVideo) && mVideoDevice) ? ((IsOn(mConstraints.mVideo) && mVideoDevice) ?
mVideoDevice->GetSource() : nullptr)); mVideoDevice->GetSource() : nullptr));
return NS_OK;
} }
nsresult nsresult
@ -1334,16 +1331,38 @@ private:
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
}; };
#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
class GetUserMediaRunnableWrapper : public nsRunnable
{
public:
// This object must take ownership of task
GetUserMediaRunnableWrapper(GetUserMediaTask* task) :
mTask(task) {
}
~GetUserMediaRunnableWrapper() {
}
NS_IMETHOD Run() {
mTask->Run();
return NS_OK;
}
private:
nsAutoPtr<GetUserMediaTask> mTask;
};
#endif
/** /**
* Similar to GetUserMediaRunnable, but used for the chrome-only * Similar to GetUserMediaTask, but used for the chrome-only
* GetUserMediaDevices function. Enumerates a list of audio & video devices, * GetUserMediaDevices function. Enumerates a list of audio & video devices,
* wraps them up in nsIMediaDevice objects and returns it to the success * wraps them up in nsIMediaDevice objects and returns it to the success
* callback. * callback.
*/ */
class GetUserMediaDevicesRunnable : public nsRunnable class GetUserMediaDevicesTask : public Task
{ {
public: public:
GetUserMediaDevicesRunnable( GetUserMediaDevicesTask(
const MediaStreamConstraints& aConstraints, const MediaStreamConstraints& aConstraints,
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess, already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError, already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
@ -1357,7 +1376,7 @@ public:
, mLoopbackAudioDevice(aAudioLoopbackDev) , mLoopbackAudioDevice(aAudioLoopbackDev)
, mLoopbackVideoDevice(aVideoLoopbackDev) {} , mLoopbackVideoDevice(aVideoLoopbackDev) {}
NS_IMETHOD void // NS_IMETHOD
Run() Run()
{ {
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
@ -1389,7 +1408,6 @@ public:
final.forget())); final.forget()));
// DeviceSuccessCallbackRunnable should have taken these. // DeviceSuccessCallbackRunnable should have taken these.
MOZ_ASSERT(!mSuccess && !mError); MOZ_ASSERT(!mSuccess && !mError);
return NS_OK;
} }
private: private:
@ -1437,12 +1455,23 @@ NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIObserver)
/* static */ MediaManager* /* static */ MediaManager*
MediaManager::Get() { MediaManager::Get() {
if (!sSingleton) { if (!sSingleton) {
NS_ASSERTION(NS_IsMainThread(), "Only create MediaManager on main thread");
sSingleton = new MediaManager(); sSingleton = new MediaManager();
NS_NewNamedThread("MediaManager", getter_AddRefs(sSingleton->mMediaThread)); sSingleton->mMediaThread = new base::Thread("MediaManager");
base::Thread::Options options;
#if defined(_WIN32)
options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD;
#else
options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINTHREAD;
#endif
if (!sSingleton->mMediaThread->StartWithOptions(options)) {
MOZ_CRASH();
}
LOG(("New Media thread for gum")); LOG(("New Media thread for gum"));
NS_ASSERTION(NS_IsMainThread(), "Only create MediaManager on main thread");
nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) { if (obs) {
obs->AddObserver(sSingleton, "xpcom-shutdown", false); obs->AddObserver(sSingleton, "xpcom-shutdown", false);
@ -1471,6 +1500,15 @@ MediaManager::GetInstance()
return service.forget(); return service.forget();
} }
/* static */
MessageLoop*
MediaManager::GetMessageLoop()
{
NS_ASSERTION(Get(), "MediaManager singleton?");
NS_ASSERTION(Get()->mMediaThread, "No thread yet");
return Get()->mMediaThread->message_loop();
}
/* static */ nsresult /* static */ nsresult
MediaManager::NotifyRecordingStatusChange(nsPIDOMWindow* aWindow, MediaManager::NotifyRecordingStatusChange(nsPIDOMWindow* aWindow,
const nsString& aMsg, const nsString& aMsg,
@ -1606,12 +1644,9 @@ MediaManager::GetUserMedia(bool aPrivileged,
GetActiveWindows()->Put(windowID, listeners); GetActiveWindows()->Put(windowID, listeners);
} }
// Ensure there's a thread for gum to proxy to off main thread
nsIThread *mediaThread = MediaManager::GetThread();
// Create a disabled listener to act as a placeholder // Create a disabled listener to act as a placeholder
GetUserMediaCallbackMediaStreamListener* listener = GetUserMediaCallbackMediaStreamListener* listener =
new GetUserMediaCallbackMediaStreamListener(mediaThread, windowID); new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowID);
// No need for locking because we always do this in the main thread. // No need for locking because we always do this in the main thread.
listeners->AppendElement(listener); listeners->AppendElement(listener);
@ -1663,15 +1698,15 @@ MediaManager::GetUserMedia(bool aPrivileged,
} }
} }
// Pass callbacks and MediaStreamListener along to GetUserMediaRunnable. // Pass callbacks and MediaStreamListener along to GetUserMediaTask.
nsRefPtr<GetUserMediaRunnable> runnable; nsAutoPtr<GetUserMediaTask> task;
if (c.mFake) { if (c.mFake) {
// Fake stream from default backend. // Fake stream from default backend.
runnable = new GetUserMediaRunnable(c, onSuccess.forget(), task = new GetUserMediaTask(c, onSuccess.forget(),
onError.forget(), windowID, listener, mPrefs, new MediaEngineDefault()); onError.forget(), windowID, listener, mPrefs, new MediaEngineDefault());
} else { } else {
// Stream from default device from WebRTC backend. // Stream from default device from WebRTC backend.
runnable = new GetUserMediaRunnable(c, onSuccess.forget(), task = new GetUserMediaTask(c, onSuccess.forget(),
onError.forget(), windowID, listener, mPrefs); onError.forget(), windowID, listener, mPrefs);
} }
@ -1683,10 +1718,10 @@ MediaManager::GetUserMedia(bool aPrivileged,
if (tc.mMediaSource != dom::MediaSourceEnum::Camera) { if (tc.mMediaSource != dom::MediaSourceEnum::Camera) {
if (tc.mMediaSource == dom::MediaSourceEnum::Browser) { if (tc.mMediaSource == dom::MediaSourceEnum::Browser) {
if (!Preferences::GetBool("media.getusermedia.browser.enabled", false)) { if (!Preferences::GetBool("media.getusermedia.browser.enabled", false)) {
return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED")); return task->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
} }
} else if (!Preferences::GetBool("media.getusermedia.screensharing.enabled", false)) { } else if (!Preferences::GetBool("media.getusermedia.screensharing.enabled", false)) {
return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED")); return task->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
} }
/* Deny screensharing if the requesting document is not from a host /* Deny screensharing if the requesting document is not from a host
on the whitelist. */ on the whitelist. */
@ -1704,7 +1739,7 @@ MediaManager::GetUserMedia(bool aPrivileged,
) || ) ||
#endif #endif
(!aPrivileged && !HostHasPermission(*docURI))) { (!aPrivileged && !HostHasPermission(*docURI))) {
return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED")); return task->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
} }
} }
} }
@ -1718,7 +1753,8 @@ MediaManager::GetUserMedia(bool aPrivileged,
#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
if (c.mPicture) { if (c.mPicture) {
// ShowFilePickerForMimeType() must run on the Main Thread! (on Android) // ShowFilePickerForMimeType() must run on the Main Thread! (on Android)
NS_DispatchToMainThread(runnable); // Note, GetUserMediaRunnableWrapper takes ownership of task.
NS_DispatchToMainThread(new GetUserMediaRunnableWrapper(task.forget()));
return NS_OK; return NS_OK;
} }
#endif #endif
@ -1737,7 +1773,7 @@ MediaManager::GetUserMedia(bool aPrivileged,
// XXX No full support for picture in Desktop yet (needs proper UI) // XXX No full support for picture in Desktop yet (needs proper UI)
if (aPrivileged || if (aPrivileged ||
(c.mFake && !Preferences::GetBool("media.navigator.permission.fake"))) { (c.mFake && !Preferences::GetBool("media.navigator.permission.fake"))) {
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL); MediaManager::GetMessageLoop()->PostTask(FROM_HERE, task.forget());
} else { } else {
bool isHTTPS = false; bool isHTTPS = false;
if (docURI) { if (docURI) {
@ -1766,12 +1802,12 @@ MediaManager::GetUserMedia(bool aPrivileged,
if ((!IsOn(c.mAudio) || audioPerm == nsIPermissionManager::DENY_ACTION) && if ((!IsOn(c.mAudio) || audioPerm == nsIPermissionManager::DENY_ACTION) &&
(!IsOn(c.mVideo) || videoPerm == nsIPermissionManager::DENY_ACTION)) { (!IsOn(c.mVideo) || videoPerm == nsIPermissionManager::DENY_ACTION)) {
return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED")); return task->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
} }
// Ask for user permission, and dispatch runnable (or not) when a response // Ask for user permission, and dispatch task (or not) when a response
// is received via an observer notification. Each call is paired with its // is received via an observer notification. Each call is paired with its
// runnable by a GUID. // task by a GUID.
nsCOMPtr<nsIUUIDGenerator> uuidgen = nsCOMPtr<nsIUUIDGenerator> uuidgen =
do_GetService("@mozilla.org/uuid-generator;1", &rv); do_GetService("@mozilla.org/uuid-generator;1", &rv);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
@ -1785,8 +1821,8 @@ MediaManager::GetUserMedia(bool aPrivileged,
id.ToProvidedString(buffer); id.ToProvidedString(buffer);
NS_ConvertUTF8toUTF16 callID(buffer); NS_ConvertUTF8toUTF16 callID(buffer);
// Store the current unarmed runnable w/callbacks. // Store the current unarmed task w/callbacks.
mActiveCallbacks.Put(callID, runnable); mActiveCallbacks.Put(callID, task.forget());
// Add a WindowID cross-reference so OnNavigation can tear things down // Add a WindowID cross-reference so OnNavigation can tear things down
nsTArray<nsString>* array; nsTArray<nsString>* array;
@ -1831,12 +1867,12 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
nsAdoptingCString loopbackVideoDevice = nsAdoptingCString loopbackVideoDevice =
Preferences::GetCString("media.video_loopback_dev"); Preferences::GetCString("media.video_loopback_dev");
nsCOMPtr<nsIRunnable> gUMDRunnable = new GetUserMediaDevicesRunnable( MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
aConstraints, onSuccess.forget(), onError.forget(), new GetUserMediaDevicesTask(
(aInnerWindowID ? aInnerWindowID : aWindow->WindowID()), aConstraints, onSuccess.forget(), onError.forget(),
loopbackAudioDevice, loopbackVideoDevice); (aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
loopbackAudioDevice, loopbackVideoDevice));
mMediaThread->Dispatch(gUMDRunnable, NS_DISPATCH_NORMAL);
return NS_OK; return NS_OK;
} }
@ -2018,8 +2054,7 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
// Note: won't be released immediately as the Observer has a ref to us // Note: won't be released immediately as the Observer has a ref to us
sSingleton = nullptr; sSingleton = nullptr;
if (mMediaThread) { if (mMediaThread) {
mMediaThread->Shutdown(); mMediaThread->Stop();
mMediaThread = nullptr;
} }
mBackend = nullptr; mBackend = nullptr;
} }
@ -2028,11 +2063,11 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
} else if (!strcmp(aTopic, "getUserMedia:response:allow")) { } else if (!strcmp(aTopic, "getUserMedia:response:allow")) {
nsString key(aData); nsString key(aData);
nsRefPtr<GetUserMediaRunnable> runnable; nsAutoPtr<GetUserMediaTask> task;
if (!mActiveCallbacks.Get(key, getter_AddRefs(runnable))) { mActiveCallbacks.RemoveAndForget(key, task);
if (!task) {
return NS_OK; return NS_OK;
} }
mActiveCallbacks.Remove(key);
if (aSubject) { if (aSubject) {
// A particular device or devices were chosen by the user. // A particular device or devices were chosen by the user.
@ -2044,7 +2079,7 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
MOZ_ASSERT(len); MOZ_ASSERT(len);
if (!len) { if (!len) {
// neither audio nor video were selected // neither audio nor video were selected
runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED")); task->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
return NS_OK; return NS_OK;
} }
for (uint32_t i = 0; i < len; i++) { for (uint32_t i = 0; i < len; i++) {
@ -2056,9 +2091,9 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
nsString type; nsString type;
device->GetType(type); device->GetType(type);
if (type.EqualsLiteral("video")) { if (type.EqualsLiteral("video")) {
runnable->SetVideoDevice(static_cast<VideoDevice*>(device.get())); task->SetVideoDevice(static_cast<VideoDevice*>(device.get()));
} else if (type.EqualsLiteral("audio")) { } else if (type.EqualsLiteral("audio")) {
runnable->SetAudioDevice(static_cast<AudioDevice*>(device.get())); task->SetAudioDevice(static_cast<AudioDevice*>(device.get()));
} else { } else {
NS_WARNING("Unknown device type in getUserMedia"); NS_WARNING("Unknown device type in getUserMedia");
} }
@ -2067,7 +2102,7 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
} }
// Reuse the same thread to save memory. // Reuse the same thread to save memory.
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL); MediaManager::GetMessageLoop()->PostTask(FROM_HERE, task.forget());
return NS_OK; return NS_OK;
} else if (!strcmp(aTopic, "getUserMedia:response:deny")) { } else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
@ -2082,12 +2117,11 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
} }
nsString key(aData); nsString key(aData);
nsRefPtr<GetUserMediaRunnable> runnable; nsAutoPtr<GetUserMediaTask> task;
if (!mActiveCallbacks.Get(key, getter_AddRefs(runnable))) { mActiveCallbacks.RemoveAndForget(key, task);
return NS_OK; if (task) {
task->Denied(errorMessage);
} }
mActiveCallbacks.Remove(key);
runnable->Denied(errorMessage);
return NS_OK; return NS_OK;
} else if (!strcmp(aTopic, "getUserMedia:revoke")) { } else if (!strcmp(aTopic, "getUserMedia:revoke")) {
@ -2342,21 +2376,36 @@ MediaManager::StopMediaStreams()
} }
} }
void
GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
uint32_t aEcho,
bool aAgcOn, uint32_t aAGC,
bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay)
{
if (mAudioSource) {
#ifdef MOZ_WEBRTC
mMediaThread->message_loop()->PostTask(FROM_HERE,
NewRunnableMethod(mAudioSource.get(), &MediaEngineSource::Config,
aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn,
aNoise, aPlayoutDelay));
#endif
}
}
// Can be invoked from EITHER MainThread or MSG thread // Can be invoked from EITHER MainThread or MSG thread
void void
GetUserMediaCallbackMediaStreamListener::Invalidate() GetUserMediaCallbackMediaStreamListener::Invalidate()
{ {
nsRefPtr<MediaOperationRunnable> runnable;
// We can't take a chance on blocking here, so proxy this to another // We can't take a chance on blocking here, so proxy this to another
// thread. // thread.
// Pass a ref to us (which is threadsafe) so it can query us for the // Pass a ref to us (which is threadsafe) so it can query us for the
// source stream info. // source stream info.
runnable = new MediaOperationRunnable(MEDIA_STOP, MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
this, nullptr, nullptr, new MediaOperationTask(MEDIA_STOP,
mAudioSource, mVideoSource, this, nullptr, nullptr,
mFinished, mWindowID, nullptr); mAudioSource, mVideoSource,
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL); mFinished, mWindowID, nullptr));
} }
// Doesn't kill audio // Doesn't kill audio
@ -2370,12 +2419,11 @@ GetUserMediaCallbackMediaStreamListener::StopScreenWindowSharing()
mVideoSource->GetMediaSource() == MediaSourceType::Application || mVideoSource->GetMediaSource() == MediaSourceType::Application ||
mVideoSource->GetMediaSource() == MediaSourceType::Window)) { mVideoSource->GetMediaSource() == MediaSourceType::Window)) {
// Stop the whole stream if there's no audio; just the video track if we have both // Stop the whole stream if there's no audio; just the video track if we have both
nsRefPtr<MediaOperationRunnable> runnable( MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
new MediaOperationRunnable(mAudioSource ? MEDIA_STOP_TRACK : MEDIA_STOP, new MediaOperationTask(mAudioSource ? MEDIA_STOP_TRACK : MEDIA_STOP,
this, nullptr, nullptr, this, nullptr, nullptr,
nullptr, mVideoSource, nullptr, mVideoSource,
mFinished, mWindowID, nullptr)); mFinished, mWindowID, nullptr));
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
} }
} }
@ -2389,13 +2437,12 @@ GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aID, bool aIsAudio)
{ {
// XXX to support multiple tracks of a type in a stream, this should key off // XXX to support multiple tracks of a type in a stream, this should key off
// the TrackID and not just the type // the TrackID and not just the type
nsRefPtr<MediaOperationRunnable> runnable( MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
new MediaOperationRunnable(MEDIA_STOP_TRACK, new MediaOperationTask(MEDIA_STOP_TRACK,
this, nullptr, nullptr, this, nullptr, nullptr,
aIsAudio ? mAudioSource : nullptr, aIsAudio ? mAudioSource : nullptr,
!aIsAudio ? mVideoSource : nullptr, !aIsAudio ? mVideoSource : nullptr,
mFinished, mWindowID, nullptr)); mFinished, mWindowID, nullptr));
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
} else { } else {
LOG(("gUM track %d ended, but we don't have type %s", LOG(("gUM track %d ended, but we don't have type %s",
aID, aIsAudio ? "audio" : "video")); aID, aIsAudio ? "audio" : "video"));
@ -2416,12 +2463,11 @@ void
GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph, GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph,
bool aHasListeners) bool aHasListeners)
{ {
nsRefPtr<MediaOperationRunnable> runnable; MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
runnable = new MediaOperationRunnable(MEDIA_DIRECT_LISTENERS, new MediaOperationTask(MEDIA_DIRECT_LISTENERS,
this, nullptr, nullptr, this, nullptr, nullptr,
mAudioSource, mVideoSource, mAudioSource, mVideoSource,
aHasListeners, mWindowID, nullptr); aHasListeners, mWindowID, nullptr));
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
} }
// Called from the MediaStreamGraph thread // Called from the MediaStreamGraph thread

Просмотреть файл

@ -30,6 +30,10 @@
#include "mtransport/runnable_utils.h" #include "mtransport/runnable_utils.h"
#endif #endif
// Note, these suck in Windows headers, unfortunately.
#include "base/thread.h"
#include "base/task.h"
#ifdef MOZ_WIDGET_GONK #ifdef MOZ_WIDGET_GONK
#include "DOMCameraManager.h" #include "DOMCameraManager.h"
#endif #endif
@ -57,7 +61,7 @@ class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
{ {
public: public:
// Create in an inactive state // Create in an inactive state
GetUserMediaCallbackMediaStreamListener(nsIThread *aThread, GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
uint64_t aWindowID) uint64_t aWindowID)
: mMediaThread(aThread) : mMediaThread(aThread)
, mWindowID(aWindowID) , mWindowID(aWindowID)
@ -144,7 +148,7 @@ public:
mStopped = true; mStopped = true;
} }
// implement in .cpp to avoid circular dependency with MediaOperationRunnable // implement in .cpp to avoid circular dependency with MediaOperationTask
// Can be invoked from EITHER MainThread or MSG thread // Can be invoked from EITHER MainThread or MSG thread
void Invalidate(); void Invalidate();
@ -152,19 +156,7 @@ public:
AudioConfig(bool aEchoOn, uint32_t aEcho, AudioConfig(bool aEchoOn, uint32_t aEcho,
bool aAgcOn, uint32_t aAGC, bool aAgcOn, uint32_t aAGC,
bool aNoiseOn, uint32_t aNoise, bool aNoiseOn, uint32_t aNoise,
int32_t aPlayoutDelay) int32_t aPlayoutDelay);
{
if (mAudioSource) {
#ifdef MOZ_WEBRTC
// Right now these configs are only of use if webrtc is available
RUN_ON_THREAD(mMediaThread,
WrapRunnable(nsRefPtr<MediaEngineSource>(mAudioSource), // threadsafe
&MediaEngineSource::Config,
aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn, aNoise, aPlayoutDelay),
NS_DISPATCH_NORMAL);
#endif
}
}
void void
Remove() Remove()
@ -230,7 +222,7 @@ public:
private: private:
// Set at construction // Set at construction
nsCOMPtr<nsIThread> mMediaThread; base::Thread* mMediaThread;
uint64_t mWindowID; uint64_t mWindowID;
bool mStopped; // MainThread only bool mStopped; // MainThread only
@ -299,7 +291,7 @@ typedef enum {
} MediaOperation; } MediaOperation;
class MediaManager; class MediaManager;
class GetUserMediaRunnable; class GetUserMediaTask;
/** /**
* Send an error back to content. The error is the form a string. * Send an error back to content. The error is the form a string.
@ -340,11 +332,11 @@ private:
// Generic class for running long media operations like Start off the main // Generic class for running long media operations like Start off the main
// thread, and then (because nsDOMMediaStreams aren't threadsafe), // thread, and then (because nsDOMMediaStreams aren't threadsafe),
// ProxyReleases mStream since it's cycle collected. // ProxyReleases mStream since it's cycle collected.
class MediaOperationRunnable : public nsRunnable class MediaOperationTask : public Task
{ {
public: public:
// so we can send Stop without AddRef()ing from the MSG thread // so we can send Stop without AddRef()ing from the MSG thread
MediaOperationRunnable(MediaOperation aType, MediaOperationTask(MediaOperation aType,
GetUserMediaCallbackMediaStreamListener* aListener, GetUserMediaCallbackMediaStreamListener* aListener,
DOMMediaStream* aStream, DOMMediaStream* aStream,
DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback, DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
@ -364,12 +356,13 @@ public:
, mError(aError) , mError(aError)
{} {}
~MediaOperationRunnable() ~MediaOperationTask()
{ {
// MediaStreams can be released on any thread. // MediaStreams can be released on any thread.
} }
nsresult returnAndCallbackError(nsresult rv, const char* errorLog) void
ReturnCallbackError(nsresult rv, const char* errorLog)
{ {
MM_LOG(("%s , rv=%d", errorLog, rv)); MM_LOG(("%s , rv=%d", errorLog, rv));
NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(), NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
@ -380,17 +373,16 @@ public:
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success; nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success;
NS_DispatchToMainThread(new ErrorCallbackRunnable(success, mError, NS_DispatchToMainThread(new ErrorCallbackRunnable(success, mError,
log, mWindowID)); log, mWindowID));
return NS_OK;
} }
NS_IMETHOD void
Run() MOZ_OVERRIDE Run()
{ {
SourceMediaStream *source = mListener->GetSourceStream(); SourceMediaStream *source = mListener->GetSourceStream();
// No locking between these is required as all the callbacks for the // No locking between these is required as all the callbacks for the
// same MediaStream will occur on the same thread. // same MediaStream will occur on the same thread.
if (!source) // means the stream was never Activated() if (!source) // means the stream was never Activated()
return NS_OK; return;
switch (mType) { switch (mType) {
case MEDIA_START: case MEDIA_START:
@ -406,7 +398,8 @@ public:
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
expectedTracks |= DOMMediaStream::HINT_CONTENTS_AUDIO; expectedTracks |= DOMMediaStream::HINT_CONTENTS_AUDIO;
} else { } else {
return returnAndCallbackError(rv, "Starting audio failed"); ReturnCallbackError(rv, "Starting audio failed");
return;
} }
} }
if (mVideoSource) { if (mVideoSource) {
@ -414,7 +407,8 @@ public:
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
expectedTracks |= DOMMediaStream::HINT_CONTENTS_VIDEO; expectedTracks |= DOMMediaStream::HINT_CONTENTS_VIDEO;
} else { } else {
return returnAndCallbackError(rv, "Starting video failed"); ReturnCallbackError(rv, "Starting video failed");
return;
} }
} }
@ -481,7 +475,6 @@ public:
MOZ_ASSERT(false,"invalid MediaManager operation"); MOZ_ASSERT(false,"invalid MediaManager operation");
break; break;
} }
return NS_OK;
} }
private: private:
@ -545,16 +538,13 @@ public:
// thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
// from MediaManager thread. // from MediaManager thread.
static MediaManager* Get(); static MediaManager* Get();
static MessageLoop* GetMessageLoop();
static bool Exists() static bool Exists()
{ {
return !!sSingleton; return !!sSingleton;
} }
static nsIThread* GetThread() {
return Get()->mMediaThread;
}
static nsresult NotifyRecordingStatusChange(nsPIDOMWindow* aWindow, static nsresult NotifyRecordingStatusChange(nsPIDOMWindow* aWindow,
const nsString& aMsg, const nsString& aMsg,
const bool& aIsAudio, const bool& aIsAudio,
@ -621,10 +611,10 @@ private:
// ONLY access from MainThread so we don't need to lock // ONLY access from MainThread so we don't need to lock
WindowTable mActiveWindows; WindowTable mActiveWindows;
nsRefPtrHashtable<nsStringHashKey, GetUserMediaRunnable> mActiveCallbacks; nsClassHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds; nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
// Always exists // Always exists
nsCOMPtr<nsIThread> mMediaThread; nsAutoPtr<base::Thread> mMediaThread;
Mutex mMutex; Mutex mMutex;
// protected with mMutex: // protected with mMutex: