Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
#ifndef DOM_MEDIA_MEDIACONTROL_MEDIACONTROLLER_H_
#define DOM_MEDIA_MEDIACONTROL_MEDIACONTROLLER_H_
#include "MediaEventSource.h"
#include "MediaPlaybackStatus.h"
#include "MediaStatusManager.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/MediaControllerBinding.h"
#include "mozilla/dom/MediaSession.h"
#include "mozilla/LinkedList.h"
#include "nsISupportsImpl.h"
#include "nsITimer.h"
namespace mozilla::dom {
class BrowsingContext;
/**
* IMediaController is an interface which includes control related methods and
* methods used to know its playback state.
*/
class IMediaController {
public:
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
// Focus the window currently playing media.
virtual void Focus() = 0;
virtual void Play() = 0;
virtual void Pause() = 0;
virtual void Stop() = 0;
virtual void PrevTrack() = 0;
virtual void NextTrack() = 0;
virtual void SeekBackward(double aSeekOffset) = 0;
virtual void SeekForward(double aSeekOffset) = 0;
virtual void SkipAd() = 0;
virtual void SeekTo(double aSeekTime, bool aFastSeek) = 0;
// Return the ID of the top level browsing context within a tab.
virtual uint64_t Id() const = 0;
virtual bool IsAudible() const = 0;
virtual bool IsPlaying() const = 0;
virtual bool IsActive() const = 0;
};
/**
* MediaController is a class, which is used to control all media within a tab.
* It can only be used in Chrome process and the controlled media are usually
* in the content process (unless we disable e10s).
*
* Each tab would have only one media controller, they are 1-1 corresponding
* relationship, we use tab's top-level browsing context ID to initialize the
* controller and use that as its ID.
*
* The controller would be activated when its controlled media starts and
* becomes audible. After the controller is activated, then we can use its
* controlling methods, such as `Play()`, `Pause()` to control the media within
* the tab.
*
* If there is at least one controlled media playing in the tab, then we would
* say the controller is `playing`. If there is at least one controlled media is
* playing and audible, then we would say the controller is `audible`.
*
* Note that, if we don't enable audio competition, then we might have multiple
* tabs playing media at the same time, we can use the ID to query the specific
* controller from `MediaControlService`.
*/
class MediaController final : public DOMEventTargetHelper,
public IMediaController,
public LinkedListElement<RefPtr<MediaController>>,
public MediaStatusManager,
public nsITimerCallback,
public nsINamed {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSINAMED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MediaController,
DOMEventTargetHelper)
explicit MediaController(uint64_t aBrowsingContextId);
// WebIDL methods
nsISupports* GetParentObject() const;
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
void GetSupportedKeys(nsTArray<MediaControlKey>& aRetVal) const;
void GetMetadata(MediaMetadataInit& aMetadata, ErrorResult& aRv);
IMPL_EVENT_HANDLER(activated);
IMPL_EVENT_HANDLER(deactivated);
IMPL_EVENT_HANDLER(metadatachange);
IMPL_EVENT_HANDLER(supportedkeyschange);
IMPL_EVENT_HANDLER(playbackstatechange);
IMPL_EVENT_HANDLER(positionstatechange);
// IMediaController's methods
void Focus() override;
void Play() override;
void Pause() override;
void Stop() override;
void PrevTrack() override;
void NextTrack() override;
void SeekBackward(double aSeekOffset) override;
void SeekForward(double aSeekOffset) override;
void SkipAd() override;
void SeekTo(double aSeekTime, bool aFastSeek) override;
uint64_t Id() const override;
bool IsAudible() const override;
bool IsPlaying() const override;
bool IsActive() const override;
// IMediaInfoUpdater's methods
void NotifyMediaPlaybackChanged(uint64_t aBrowsingContextId,
MediaPlaybackState aState) override;
void NotifyMediaAudibleChanged(uint64_t aBrowsingContextId,
MediaAudibleState aState) override;
void SetIsInPictureInPictureMode(uint64_t aBrowsingContextId,
bool aIsInPictureInPictureMode) override;
void NotifyMediaFullScreenState(uint64_t aBrowsingContextId,
bool aIsInFullScreen) override;
// Calling this method explicitly would mark this controller as deprecated,
// then calling any its method won't take any effect.
void Shutdown();
// This event would be notified media controller's supported media keys
// change.
MediaEventSource<nsTArray<MediaControlKey>>& SupportedKeysChangedEvent() {
return mSupportedKeysChangedEvent;
}
MediaEventSource<bool>& FullScreenChangedEvent() {
return mFullScreenChangedEvent;
}
MediaEventSource<bool>& PictureInPictureModeChangedEvent() {
return mPictureInPictureModeChangedEvent;
}
CopyableTArray<MediaControlKey> GetSupportedMediaKeys() const;
bool IsBeingUsedInPIPModeOrFullscreen() const;
// These methods are used to select/unselect the media controller as a main
// controller.
void Select() const;
void Unselect() const;
private:
~MediaController();
void HandleActualPlaybackStateChanged();
void UpdateMediaControlActionToContentMediaIfNeeded(
const MediaControlAction& aAction);
void HandleSupportedMediaSessionActionsChanged(
const nsTArray<MediaSessionAction>& aSupportedAction);
void HandlePositionStateChanged(const Maybe<PositionState>& aState);
void HandleMetadataChanged(const MediaMetadataBase& aMetadata);
// This would register controller to the media control service that takes a
// responsibility to manage all active controllers.
void Activate();
// This would unregister controller from the media control service.
void Deactivate();
void UpdateActivatedStateIfNeeded();
bool ShouldActivateController() const;
bool ShouldDeactivateController() const;
void UpdateDeactivationTimerIfNeeded();
void DispatchAsyncEvent(const nsAString& aName);
void DispatchAsyncEvent(already_AddRefed<Event> aEvent);
bool IsMainController() const;
void ForceToBecomeMainControllerIfNeeded();
bool ShouldRequestForMainController() const;
bool ShouldPropagateActionToAllContexts(
const MediaControlAction& aAction) const;
bool mIsActive = false;
bool mShutdown = false;
bool mIsInPictureInPictureMode = false;
bool mIsInFullScreenMode = false;
// We would monitor the change of media session actions and convert them to
// the media keys, then determine the supported media keys.
MediaEventListener mSupportedActionsChangedListener;
MediaEventProducer<nsTArray<MediaControlKey>> mSupportedKeysChangedEvent;
MediaEventListener mPlaybackChangedListener;
MediaEventListener mPositionStateChangedListener;
MediaEventListener mMetadataChangedListener;
MediaEventProducer<bool> mFullScreenChangedEvent;
MediaEventProducer<bool> mPictureInPictureModeChangedEvent;
// Use copyable array so that we can use the result as a parameter for the
// media event.
CopyableTArray<MediaControlKey> mSupportedKeys;
// Timer to deactivate the controller if the time of being paused exceeds the
// threshold of time.
nsCOMPtr<nsITimer> mDeactivationTimer;
};
} // namespace mozilla::dom
#endif