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:
Andreas Pehrson 2018-02-16 11:55:27 +01:00
Родитель 4327e2bf5c
Коммит c9e4a19d1e
1 изменённых файлов: 139 добавлений и 87 удалений

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

@ -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");
});
}