author | Andreas Pehrson <pehrsons@mozilla.com> |
Fri, 16 Feb 2018 11:55:27 +0100 | |
changeset 406788 | 0451fe123f5b9168de34b3f927bb421c8e9864ca |
parent 406787 | 59f19fc034cf7fc035710ff97e5b0a983ca920e6 |
child 406789 | 04b165b560de7746ab20988189619bd213c7c01b |
push id | 33580 |
push user | dluca@mozilla.com |
push date | Tue, 06 Mar 2018 21:54:45 +0000 |
treeherder | mozilla-central@bccdc6842104 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jib |
bugs | 1436694 |
milestone | 60.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -30,16 +30,17 @@ #include "nsICryptoHash.h" #include "nsICryptoHMAC.h" #include "nsIKeyModule.h" #include "nsAppDirectoryServiceDefs.h" #include "nsIInputStream.h" #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" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/File.h" #include "mozilla/dom/MediaStreamBinding.h" @@ -151,49 +152,48 @@ using media::Refcountable; static Atomic<bool> sHasShutdown; 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); } // true if we have stopped mDevice, this is a terminal state. // MainThread only. bool mStopped = false; // 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. // MainThread only. bool mOperationInProgress = false; // true if we are allowed to turn off the underlying source while all tracks // are disabled. // MainThread only. bool mOffWhileDisabled = false; // Timer triggered by a MediaStreamTrackSource signaling that all tracks got // 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. const RefPtr<MediaDevice> mDevice; }; /** * This mimics the capture state from nsIMediaManagerService. @@ -234,16 +234,18 @@ FromCaptureState(CaptureState aState) * don't hold a reference to it during late shutdown. * * There's also a hard reference to the SourceListener through its * SourceStreamListener and the MediaStreamGraph. MediaStreamGraph * clears this on XPCOM_WILL_SHUTDOWN, before MediaManager enters shutdown. */ 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) SourceListener(); /** * Registers this source listener as belonging to the given window listener. */ @@ -252,16 +254,21 @@ public: /** * Marks this listener as active and adds itself as a listener to aStream. */ void Activate(SourceMediaStream* aStream, 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. */ void Stop(); /** * Removes this SourceListener from its associated MediaStream and marks it * removed. Also removes the weak reference to the associated window listener. */ @@ -1369,99 +1376,42 @@ public: mOnSuccess, mWindowID, domStream)))); // Dispatch to the media thread to ask it to start the sources, // 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) { + 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. + + // Only run if the window is still active for our window listener. + if (!(manager->IsWindowStillActive(windowID))) { return; } - - nsGlobalWindowInner* window = - nsGlobalWindowInner::GetInnerWindowWithId(windowID); - if (!window) { - MOZ_ASSERT_UNREACHABLE("Should have window"); - return; + // 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); } - - domStream->OnTracksAvailable(callback->release()); - - nsresult rv = MediaManager::NotifyRecordingStatusChange(window->AsInner()); - if (NS_FAILED(rv)) { - MOZ_ASSERT_UNREACHABLE("Should be able to notify chrome"); - return; - } - - manager->SendPendingGUMRequest(); - })); - return NS_OK; - })); + }); if (!IsPincipalInfoPrivate(mPrincipalInfo)) { // Call GetPrincipalKey again, this time w/persist = true, to promote // deviceIds to persistent, in case they're not already. Fire'n'forget. RefPtr<Pledge<nsCString>> p = media::GetPrincipalKey(mPrincipalInfo, true); } return NS_OK; @@ -3833,16 +3783,119 @@ SourceListener::Activate(SourceMediaStre aVideoDevice, aVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera && Preferences::GetBool("media.getusermedia.camera.off_while_disabled.enabled", true)); } 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() { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread"); if (mStopped) { return; } @@ -3965,17 +4018,16 @@ SourceListener::SetEnabledFor(TrackID aT state.mTrackEnabled = aEnable; if (state.mStopped) { // Device terminally stopped. Updating device state is pointless. 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 // operation. state.mDisableTimer->Cancel(); return; } @@ -3999,17 +4051,17 @@ SourceListener::SetEnabledFor(TrackID aT : "media.getusermedia.camera.off_while_disabled.delay_ms", 3000)); timerPromise = state.mDisableTimer->WaitFor(offDelay, __func__); } 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, "It's our responsibility to reset the inProgress state"); LOG(("SourceListener %p %s %s track %d - starting device operation", this, aEnable ? "enabling" : "disabling", aTrackID == kAudioTrack ? "audio" : "video", @@ -4028,17 +4080,17 @@ SourceListener::SetEnabledFor(TrackID aT return DeviceOperationPromise::CreateAndResolve(NS_OK, __func__); } return MediaManager::PostTask<DeviceOperationPromise>(__func__, [self, device = state.mDevice, 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__, [self, this, &state, aTrackID, aEnable](nsresult aResult) mutable { MOZ_ASSERT(state.mOperationInProgress); state.mOperationInProgress = false; if (state.mStopped) { @@ -4088,17 +4140,17 @@ SourceListener::SetEnabledFor(TrackID aT } // Track state changed during this operation. We'll start over. if (state.mTrackEnabled) { SetEnabledFor(aTrackID, true); } else { SetEnabledFor(aTrackID, false); } - }, [](bool aDummy) { + }, []() { MOZ_ASSERT_UNREACHABLE("Unexpected and unhandled reject"); }); } void SourceListener::StopSharing() { MOZ_ASSERT(NS_IsMainThread());