зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1436694 - MozPromisify device initialization and move it to SourceListener. r=jib
This so that SourceListener can keep its internal state in sync with the result of the start operation. MozReview-Commit-ID: Cgl5TFnpCeW --HG-- extra : rebase_source : e2cec60544efcc27c9c8c077fbdb013a8f3b848c extra : source : 6c38cc382d2114199842dddb14097be8b6d9a961 extra : histedit_source : 00ef8da067eb484b8c5926d008f00f1e9f006e6f
This commit is contained in:
Родитель
4327e2bf5c
Коммит
c9e4a19d1e
|
@ -35,6 +35,7 @@
|
|||
#include "nsILineInputStream.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Types.h"
|
||||
#include "mozilla/PeerIdentity.h"
|
||||
|
@ -156,7 +157,6 @@ typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
|
|||
struct DeviceState {
|
||||
DeviceState(const RefPtr<MediaDevice>& aDevice, bool aOffWhileDisabled)
|
||||
: mOffWhileDisabled(aOffWhileDisabled)
|
||||
, mDisableTimer(new MediaTimer())
|
||||
, mDevice(aDevice)
|
||||
{
|
||||
MOZ_ASSERT(mDevice);
|
||||
|
@ -168,11 +168,11 @@ struct DeviceState {
|
|||
|
||||
// true if mDevice is currently enabled, i.e., turned on and capturing.
|
||||
// MainThread only.
|
||||
bool mDeviceEnabled = true;
|
||||
bool mDeviceEnabled = false;
|
||||
|
||||
// true if the application has currently enabled mDevice.
|
||||
// MainThread only.
|
||||
bool mTrackEnabled = true;
|
||||
bool mTrackEnabled = false;
|
||||
|
||||
// true if an operation to Start() or Stop() mDevice has been dispatched to
|
||||
// the media thread and is not finished yet.
|
||||
|
@ -188,7 +188,7 @@ struct DeviceState {
|
|||
// disabled. When the timer fires we initiate Stop()ing mDevice.
|
||||
// If set we allow dynamically stopping and starting mDevice.
|
||||
// Any thread.
|
||||
const RefPtr<MediaTimer> mDisableTimer;
|
||||
const RefPtr<MediaTimer> mDisableTimer = new MediaTimer();
|
||||
|
||||
// The underlying device we keep state for. Always non-null.
|
||||
// Threadsafe access, but see method declarations for individual constraints.
|
||||
|
@ -239,6 +239,8 @@ FromCaptureState(CaptureState aState)
|
|||
*/
|
||||
class SourceListener : public SupportsWeakPtr<SourceListener> {
|
||||
public:
|
||||
typedef MozPromise<bool /* aIgnored */, RefPtr<MediaMgrError>, true> InitPromise;
|
||||
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(SourceListener)
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(SourceListener)
|
||||
|
||||
|
@ -256,6 +258,11 @@ public:
|
|||
MediaDevice* aAudioDevice,
|
||||
MediaDevice* aVideoDevice);
|
||||
|
||||
/**
|
||||
* Posts a task to initialize and start all associated devices.
|
||||
*/
|
||||
RefPtr<InitPromise> InitializeAsync();
|
||||
|
||||
/**
|
||||
* Stops all live tracks, finishes the associated MediaStream and cleans up.
|
||||
*/
|
||||
|
@ -1374,89 +1381,32 @@ public:
|
|||
// because that can take a while.
|
||||
// Pass ownership of domStream through the lambda to the nested chrome
|
||||
// notification lambda to ensure it's kept alive until that lambda runs or is discarded.
|
||||
RefPtr<GetUserMediaStreamRunnable> self = this;
|
||||
MediaManager::PostTask(NewTaskFrom([self, domStream, callback]() mutable {
|
||||
MOZ_ASSERT(MediaManager::IsInMediaThread());
|
||||
RefPtr<SourceMediaStream> source =
|
||||
self->mSourceListener->GetSourceStream();
|
||||
|
||||
RefPtr<MediaMgrError> error = nullptr;
|
||||
if (self->mAudioDevice) {
|
||||
nsresult rv = self->mAudioDevice->SetTrack(source,
|
||||
kAudioTrack,
|
||||
self->mSourceListener->GetPrincipalHandle());
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = self->mAudioDevice->Start();
|
||||
} else {
|
||||
nsString log;
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
log.AssignASCII("Concurrent mic process limit.");
|
||||
error = new MediaMgrError(NS_LITERAL_STRING("NotReadableError"), log);
|
||||
} else {
|
||||
log.AssignASCII("Starting audio failed");
|
||||
error = new MediaMgrError(NS_LITERAL_STRING("InternalError"), log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!error && self->mVideoDevice) {
|
||||
nsresult rv = self->mVideoDevice->SetTrack(source,
|
||||
kVideoTrack,
|
||||
self->mSourceListener->GetPrincipalHandle());
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = self->mVideoDevice->Start();
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
nsString log;
|
||||
log.AssignASCII("Starting video failed");
|
||||
error = new MediaMgrError(NS_LITERAL_STRING("InternalError"), log);
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
// Dispatch the error callback on main thread.
|
||||
NS_DispatchToMainThread(MakeAndAddRef<ErrorCallbackRunnable>(
|
||||
self->mOnFailure, *error, self->mWindowID));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Start() queued the tracks to be added synchronously to avoid races
|
||||
source->FinishAddTracks();
|
||||
|
||||
source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
|
||||
|
||||
LOG(("started all sources"));
|
||||
|
||||
// onTracksAvailableCallback must be added to domStream on the main thread.
|
||||
uint64_t windowID = self->mWindowID;
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction("MediaManager::NotifyChromeOfStart",
|
||||
[source, domStream, callback, windowID]() mutable {
|
||||
source->SetPullEnabled(true);
|
||||
|
||||
MediaManager* manager = MediaManager::GetIfExists();
|
||||
if (!manager) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsGlobalWindowInner* window =
|
||||
nsGlobalWindowInner::GetInnerWindowWithId(windowID);
|
||||
if (!window) {
|
||||
MOZ_ASSERT_UNREACHABLE("Should have window");
|
||||
return;
|
||||
}
|
||||
|
||||
mSourceListener->InitializeAsync()->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[manager = mManager, domStream, callback,
|
||||
windowListener = mWindowListener]()
|
||||
{
|
||||
// Initiating and starting devices succeeded.
|
||||
// onTracksAvailableCallback must be added to domStream on main thread.
|
||||
domStream->OnTracksAvailable(callback->release());
|
||||
windowListener->ChromeAffectingStateChanged();
|
||||
manager->SendPendingGUMRequest();
|
||||
},[manager = mManager, windowID = mWindowID,
|
||||
onFailure = Move(mOnFailure)](const RefPtr<MediaMgrError>& error)
|
||||
{
|
||||
// Initiating and starting devices failed.
|
||||
|
||||
nsresult rv = MediaManager::NotifyRecordingStatusChange(window->AsInner());
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_ASSERT_UNREACHABLE("Should be able to notify chrome");
|
||||
// Only run if the window is still active for our window listener.
|
||||
if (!(manager->IsWindowStillActive(windowID))) {
|
||||
return;
|
||||
}
|
||||
|
||||
manager->SendPendingGUMRequest();
|
||||
}));
|
||||
return NS_OK;
|
||||
}));
|
||||
// This is safe since we're on main-thread, and the windowlist can only
|
||||
// be invalidated from the main-thread (see OnNavigation)
|
||||
if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(windowID)) {
|
||||
auto streamError = MakeRefPtr<MediaStreamError>(window->AsInner(), *error);
|
||||
onFailure->OnError(streamError);
|
||||
}
|
||||
});
|
||||
|
||||
if (!IsPincipalInfoPrivate(mPrincipalInfo)) {
|
||||
// Call GetPrincipalKey again, this time w/persist = true, to promote
|
||||
|
@ -3838,6 +3788,109 @@ SourceListener::Activate(SourceMediaStream* aStream,
|
|||
mStream->AddListener(mStreamListener);
|
||||
}
|
||||
|
||||
RefPtr<SourceListener::InitPromise>
|
||||
SourceListener::InitializeAsync()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mStopped);
|
||||
|
||||
RefPtr<InitPromise> init = MediaManager::PostTask<InitPromise>(__func__,
|
||||
[ stream = mStream
|
||||
, principal = GetPrincipalHandle()
|
||||
, audioDevice = mAudioDeviceState ? mAudioDeviceState->mDevice : nullptr
|
||||
, videoDevice = mVideoDeviceState ? mVideoDeviceState->mDevice : nullptr
|
||||
](MozPromiseHolder<InitPromise>& aHolder)
|
||||
{
|
||||
if (audioDevice) {
|
||||
nsresult rv = audioDevice->SetTrack(stream, kAudioTrack, principal);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = audioDevice->Start();
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
nsString log;
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
log.AssignASCII("Concurrent mic process limit.");
|
||||
aHolder.Reject(MakeRefPtr<MediaMgrError>(
|
||||
NS_LITERAL_STRING("NotReadableError"), log), __func__);
|
||||
return;
|
||||
}
|
||||
log.AssignASCII("Starting audio failed");
|
||||
aHolder.Reject(MakeRefPtr<MediaMgrError>(
|
||||
NS_LITERAL_STRING("InternalError"), log), __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (videoDevice) {
|
||||
nsresult rv = videoDevice->SetTrack(stream, kVideoTrack, principal);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = videoDevice->Start();
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
if (audioDevice) {
|
||||
if (NS_WARN_IF(NS_FAILED(audioDevice->Stop()))) {
|
||||
MOZ_ASSERT_UNREACHABLE("Stopping audio failed");
|
||||
}
|
||||
}
|
||||
nsString log;
|
||||
log.AssignASCII("Starting video failed");
|
||||
aHolder.Reject(MakeRefPtr<MediaMgrError>(NS_LITERAL_STRING("InternalError"), log), __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Start() queued the tracks to be added synchronously to avoid races
|
||||
stream->FinishAddTracks();
|
||||
stream->AdvanceKnownTracksTime(STREAM_TIME_MAX);
|
||||
LOG(("started all sources"));
|
||||
|
||||
aHolder.Resolve(true, __func__);
|
||||
});
|
||||
|
||||
return init->Then(GetMainThreadSerialEventTarget(), __func__,
|
||||
[self = RefPtr<SourceListener>(this), this]()
|
||||
{
|
||||
if (mStopped) {
|
||||
// We were shut down during the async init
|
||||
return InitPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
|
||||
mStream->SetPullEnabled(true);
|
||||
|
||||
for (DeviceState* state : {mAudioDeviceState.get(),
|
||||
mVideoDeviceState.get()}) {
|
||||
if (!state) {
|
||||
continue;
|
||||
}
|
||||
MOZ_DIAGNOSTIC_ASSERT(!state->mTrackEnabled);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!state->mDeviceEnabled);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!state->mStopped);
|
||||
|
||||
state->mDeviceEnabled = true;
|
||||
state->mTrackEnabled = true;
|
||||
}
|
||||
return InitPromise::CreateAndResolve(true, __func__);
|
||||
}, [self = RefPtr<SourceListener>(this), this](RefPtr<MediaMgrError>&& aResult)
|
||||
{
|
||||
if (mStopped) {
|
||||
return InitPromise::CreateAndReject(Move(aResult), __func__);
|
||||
}
|
||||
|
||||
for (DeviceState* state : {mAudioDeviceState.get(),
|
||||
mVideoDeviceState.get()}) {
|
||||
if (!state) {
|
||||
continue;
|
||||
}
|
||||
MOZ_DIAGNOSTIC_ASSERT(!state->mTrackEnabled);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!state->mDeviceEnabled);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!state->mStopped);
|
||||
|
||||
state->mStopped = true;
|
||||
}
|
||||
return InitPromise::CreateAndReject(Move(aResult), __func__);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
SourceListener::Stop()
|
||||
{
|
||||
|
@ -3970,7 +4023,6 @@ SourceListener::SetEnabledFor(TrackID aTrackID, bool aEnable)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if (state.mOperationInProgress) {
|
||||
// If a timer is in progress, it needs to be canceled now so the next
|
||||
// DisableTrack() gets a fresh start. Canceling will trigger another
|
||||
|
@ -4004,7 +4056,7 @@ SourceListener::SetEnabledFor(TrackID aTrackID, bool aEnable)
|
|||
typedef MozPromise<nsresult, bool, /* IsExclusive = */ true> DeviceOperationPromise;
|
||||
RefPtr<SourceListener> self = this;
|
||||
timerPromise->Then(GetMainThreadSerialEventTarget(), __func__,
|
||||
[self, this, &state, aTrackID, aEnable](bool aDummy) mutable {
|
||||
[self, this, &state, aTrackID, aEnable]() mutable {
|
||||
MOZ_ASSERT(state.mDeviceEnabled != aEnable,
|
||||
"Device operation hasn't started");
|
||||
MOZ_ASSERT(state.mOperationInProgress,
|
||||
|
@ -4033,7 +4085,7 @@ SourceListener::SetEnabledFor(TrackID aTrackID, bool aEnable)
|
|||
(MozPromiseHolder<DeviceOperationPromise>& h) {
|
||||
h.Resolve(aEnable ? device->Start() : device->Stop(), __func__);
|
||||
});
|
||||
}, [](bool aDummy) {
|
||||
}, []() {
|
||||
// Timer was canceled by us. We signal this with NS_ERROR_ABORT.
|
||||
return DeviceOperationPromise::CreateAndResolve(NS_ERROR_ABORT, __func__);
|
||||
})->Then(GetMainThreadSerialEventTarget(), __func__,
|
||||
|
@ -4093,7 +4145,7 @@ SourceListener::SetEnabledFor(TrackID aTrackID, bool aEnable)
|
|||
} else {
|
||||
SetEnabledFor(aTrackID, false);
|
||||
}
|
||||
}, [](bool aDummy) {
|
||||
}, []() {
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected and unhandled reject");
|
||||
});
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче