зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
9d266c73b6
Коммит
041794c1f3
|
@ -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:
|
||||||
|
|
Загрузка…
Ссылка в новой задаче