Bug 1571493 - part2 : use 'MediaControlAgent' in media element. r=chunmin
We create `MediaControlEventListener` to register itself to the `MediaControlAgent` that is an event source, so that we can receive the media control event from the evnet source and operate media according to different types of event. `MediaControlEventListener` is also used to notify controlled media state to the media controller. When media first starts playing, or leaves bfcache and has created listener before, we would notify `eStarted`. Notify `eStopped` when media destroys, or enter bfcache and has created listener before. When media's playing state changes, we would notifty `ePlayed` or `ePaused` depeding on media's `mPaused`. Differential Revision: https://phabricator.services.mozilla.com/D57571
This commit is contained in:
@@ -62,12 +62,14 @@
|
||||
#include "mozilla/dom/AudioTrack.h"
|
||||
#include "mozilla/dom/AudioTrackList.h"
|
||||
#include "mozilla/dom/BlobURLProtocolHandler.h"
|
||||
#include "mozilla/dom/ContentMediaController.h"
|
||||
#include "mozilla/dom/ElementInlines.h"
|
||||
#include "mozilla/dom/HTMLAudioElement.h"
|
||||
#include "mozilla/dom/HTMLInputElement.h"
|
||||
#include "mozilla/dom/HTMLMediaElementBinding.h"
|
||||
#include "mozilla/dom/HTMLSourceElement.h"
|
||||
#include "mozilla/dom/HTMLVideoElement.h"
|
||||
#include "mozilla/dom/MediaControlUtils.h"
|
||||
#include "mozilla/dom/MediaEncryptedEvent.h"
|
||||
#include "mozilla/dom/MediaErrorBinding.h"
|
||||
#include "mozilla/dom/MediaSource.h"
|
||||
@@ -128,6 +130,12 @@ extern mozilla::LazyLogModule gAutoplayPermissionLog;
|
||||
#define AUTOPLAY_LOG(msg, ...) \
|
||||
MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
|
||||
|
||||
// avoid redefined macro in unified build
|
||||
#undef MEDIACONTROL_LOG
|
||||
#define MEDIACONTROL_LOG(msg, ...) \
|
||||
MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
|
||||
("HTMLMediaElement=%p, " msg, this, ##__VA_ARGS__))
|
||||
|
||||
#define LOG(type, msg) MOZ_LOG(gMediaElementLog, type, msg)
|
||||
#define LOG_EVENT(type, msg) MOZ_LOG(gMediaElementEventsLog, type, msg)
|
||||
|
||||
@@ -369,6 +377,146 @@ class nsSourceErrorEventRunner : public nsMediaEvent {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* We use MediaControlEventListener to listen MediaControlKeysEvent in order to
|
||||
* play and pause media element when user press media control keys and update
|
||||
* media's playback and audible state to the media controller.
|
||||
*
|
||||
* Use `Start()` to start listening event and use `Stop()` to stop listening
|
||||
* event. In addition, notifying any change to media controller MUST be done
|
||||
* after successfully calling `Start()`.
|
||||
*/
|
||||
class HTMLMediaElement::MediaControlEventListener final
|
||||
: public MediaControlKeysEventListener {
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(MediaControlEventListener, override)
|
||||
|
||||
MOZ_INIT_OUTSIDE_CTOR explicit MediaControlEventListener(
|
||||
HTMLMediaElement* aElement)
|
||||
: mElement(aElement) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aElement);
|
||||
}
|
||||
|
||||
void Start() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (IsStarted()) {
|
||||
// We have already been started, do not notify start twice.
|
||||
return;
|
||||
}
|
||||
|
||||
// Fail to init media agent, we are not able to notify the media controller
|
||||
// any update and also are not able to receive media control key events.
|
||||
if (!InitMediaAgent()) {
|
||||
MEDIACONTROL_LOG("Fail to init content media agent!");
|
||||
return;
|
||||
}
|
||||
|
||||
NotifyMediaStateChanged(ControlledMediaState::eStarted);
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!IsStarted()) {
|
||||
// We have already been stopped, do not notify stop twice.
|
||||
return;
|
||||
}
|
||||
NotifyMediaStateChanged(ControlledMediaState::eStopped);
|
||||
|
||||
// Remove ourselves from media agent, which would stop receiving event.
|
||||
mControlAgent->RemoveListener(this);
|
||||
mControlAgent = nullptr;
|
||||
}
|
||||
|
||||
void NotifyMediaStartedPlaying() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsStarted());
|
||||
if (mState == ControlledMediaState::eStarted ||
|
||||
mState == ControlledMediaState::ePaused) {
|
||||
NotifyMediaStateChanged(ControlledMediaState::ePlayed);
|
||||
NotifyAudibleStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyMediaStoppedPlaying() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsStarted());
|
||||
if (mState == ControlledMediaState::ePlayed) {
|
||||
NotifyMediaStateChanged(ControlledMediaState::ePaused);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMediaAudibleState(bool aIsOwnerAudible) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mIsOwnerAudible == aIsOwnerAudible) {
|
||||
return;
|
||||
}
|
||||
mIsOwnerAudible = aIsOwnerAudible;
|
||||
// If media hasn't started playing, it doesn't make sense to update media
|
||||
// audible state. Therefore, in that case we would noitfy the audible state
|
||||
// when media starts playing.
|
||||
if (mState == ControlledMediaState::ePlayed) {
|
||||
NotifyAudibleStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void OnKeyPressed(MediaControlKeysEvent aEvent) override {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsStarted());
|
||||
MEDIACONTROL_LOG("OnKeyPressed '%s'", ToMediaControlKeysEventStr(aEvent));
|
||||
if (aEvent == MediaControlKeysEvent::ePlay) {
|
||||
Owner()->Play();
|
||||
} else if (aEvent == MediaControlKeysEvent::ePause ||
|
||||
aEvent == MediaControlKeysEvent::eStop) {
|
||||
Owner()->Pause();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsStarted() const { return mState != ControlledMediaState::eStopped; }
|
||||
|
||||
private:
|
||||
~MediaControlEventListener() = default;
|
||||
|
||||
bool InitMediaAgent() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsPIDOMWindowInner* window = Owner()->OwnerDoc()->GetInnerWindow();
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mControlAgent = ContentMediaAgent::Get(window->GetBrowsingContext());
|
||||
if (!mControlAgent) {
|
||||
return false;
|
||||
}
|
||||
mControlAgent->AddListener(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
HTMLMediaElement* Owner() const { return mElement.get(); }
|
||||
|
||||
void NotifyMediaStateChanged(ControlledMediaState aState) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mControlAgent);
|
||||
MEDIACONTROL_LOG("NotifyMediaState from state='%s' to state='%s'",
|
||||
ToControlledMediaStateStr(mState),
|
||||
ToControlledMediaStateStr(aState));
|
||||
MOZ_ASSERT(mState != aState, "Should not notify same state again!");
|
||||
mState = aState;
|
||||
mControlAgent->NotifyMediaStateChanged(this, mState);
|
||||
}
|
||||
|
||||
void NotifyAudibleStateChanged() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsStarted());
|
||||
mControlAgent->NotifyAudibleStateChanged(this, mIsOwnerAudible);
|
||||
}
|
||||
|
||||
ControlledMediaState mState = ControlledMediaState::eStopped;
|
||||
WeakPtr<HTMLMediaElement> mElement;
|
||||
RefPtr<ContentMediaAgent> mControlAgent;
|
||||
bool mIsOwnerAudible = false;
|
||||
};
|
||||
|
||||
class HTMLMediaElement::MediaStreamTrackListener
|
||||
: public DOMMediaStream::TrackListener {
|
||||
public:
|
||||
@@ -4034,6 +4182,8 @@ void HTMLMediaElement::Init() {
|
||||
|
||||
mWatchManager.Watch(mPaused, &HTMLMediaElement::UpdateWakeLock);
|
||||
mWatchManager.Watch(mPaused, &HTMLMediaElement::UpdateOutputTracksMuting);
|
||||
mWatchManager.Watch(
|
||||
mPaused, &HTMLMediaElement::NotifyMediaControlPlaybackStateChanged);
|
||||
mWatchManager.Watch(mReadyState, &HTMLMediaElement::UpdateOutputTracksMuting);
|
||||
|
||||
mWatchManager.Watch(mTracksCaptured,
|
||||
@@ -4120,6 +4270,9 @@ HTMLMediaElement::~HTMLMediaElement() {
|
||||
mResumeDelayedPlaybackAgent = nullptr;
|
||||
}
|
||||
|
||||
StopListeningMediaControlEventIfNeeded();
|
||||
mMediaControlEventListener = nullptr;
|
||||
|
||||
WakeLockRelease();
|
||||
ReportPlayedTimeAfterBlockedTelemetry();
|
||||
|
||||
@@ -4318,6 +4471,8 @@ void HTMLMediaElement::PlayInternal(bool aHandlingUserInput) {
|
||||
UpdatePreloadAction();
|
||||
UpdateSrcMediaStreamPlaying();
|
||||
|
||||
StartListeningMediaControlEventIfNeeded();
|
||||
|
||||
// Once play() has been called in a user generated event handler,
|
||||
// it is allowed to autoplay. Note: we can reach here when not in
|
||||
// a user generated event handler if our readyState has not yet
|
||||
@@ -6054,6 +6209,8 @@ void HTMLMediaElement::CheckAutoplayDataReady() {
|
||||
UpdateSrcMediaStreamPlaying();
|
||||
UpdateAudioChannelPlayingState();
|
||||
|
||||
StartListeningMediaControlEventIfNeeded();
|
||||
|
||||
if (mDecoder) {
|
||||
SetPlayedOrSeeked(true);
|
||||
if (mCurrentPlayRangeStart == -1.0) {
|
||||
@@ -6385,6 +6542,7 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement,
|
||||
mEventDeliveryPaused = aSuspendEvents;
|
||||
// We won't want to resume media element from the bfcache.
|
||||
ClearResumeDelayedMediaPlaybackAgentIfNeeded();
|
||||
StopListeningMediaControlEventIfNeeded();
|
||||
} else {
|
||||
if (!mPaused) {
|
||||
mCurrentLoadPlayTime.Start();
|
||||
@@ -6407,6 +6565,16 @@ void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement,
|
||||
!AutoplayPolicy::IsAllowedToPlay(*this)) {
|
||||
MaybeNotifyAutoplayBlocked();
|
||||
}
|
||||
if (mMediaControlEventListener) {
|
||||
MOZ_ASSERT(!mMediaControlEventListener->IsStarted(),
|
||||
"We didn't stop listening event when we were in bfcache?");
|
||||
mMediaControlEventListener->Start();
|
||||
// As resuming media from bfcache won't change `mPaused`, so we have to
|
||||
// update the playback state manually,
|
||||
if (!mPaused) {
|
||||
mMediaControlEventListener->NotifyMediaStartedPlaying();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7250,6 +7418,9 @@ void HTMLMediaElement::NotifyAudioPlaybackChanged(
|
||||
if (mAudioChannelWrapper) {
|
||||
mAudioChannelWrapper->NotifyAudioPlaybackChanged(aReason);
|
||||
}
|
||||
if (mMediaControlEventListener) {
|
||||
mMediaControlEventListener->UpdateMediaAudibleState(IsAudible());
|
||||
}
|
||||
// only request wake lock for audible media.
|
||||
UpdateWakeLock();
|
||||
}
|
||||
@@ -7696,6 +7867,33 @@ void HTMLMediaElement::ClearResumeDelayedMediaPlaybackAgentIfNeeded() {
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLMediaElement::NotifyMediaControlPlaybackStateChanged() {
|
||||
if (!mMediaControlEventListener || !mMediaControlEventListener->IsStarted()) {
|
||||
return;
|
||||
}
|
||||
if (mPaused) {
|
||||
mMediaControlEventListener->NotifyMediaStoppedPlaying();
|
||||
} else {
|
||||
mMediaControlEventListener->NotifyMediaStartedPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLMediaElement::StartListeningMediaControlEventIfNeeded() {
|
||||
if (!mMediaControlEventListener) {
|
||||
mMediaControlEventListener = new MediaControlEventListener(this);
|
||||
}
|
||||
if (!mMediaControlEventListener->IsStarted()) {
|
||||
mMediaControlEventListener->Start();
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLMediaElement::StopListeningMediaControlEventIfNeeded() {
|
||||
if (mMediaControlEventListener && mMediaControlEventListener->IsStarted()) {
|
||||
mMediaControlEventListener->NotifyMediaStoppedPlaying();
|
||||
mMediaControlEventListener->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
Reference in New Issue
Block a user