Bug 1634192 - Implement Seek and SetPosition for MPRIS r=media-playback-reviewers,stransky,alwu,win-reviewers,gstoll

Differential Revision: https://phabricator.services.mozilla.com/D212939
This commit is contained in:
Alex T
2024-08-09 16:39:25 +00:00
parent a31177f356
commit 85e68ce06b
14 changed files with 229 additions and 60 deletions

View File

@@ -54,8 +54,8 @@ interface MediaController : EventTarget {
undefined stop(); undefined stop();
undefined prevTrack(); undefined prevTrack();
undefined nextTrack(); undefined nextTrack();
undefined seekBackward(); undefined seekBackward(double seekOffset);
undefined seekForward(); undefined seekForward(double seekOffset);
undefined skipAd(); undefined skipAd();
undefined seekTo(double seekTime, optional boolean fastSeek = false); undefined seekTo(double seekTime, optional boolean fastSeek = false);
}; };

View File

@@ -120,12 +120,22 @@ void ContentPlaybackController::Pause() {
} }
} }
void ContentPlaybackController::SeekBackward() { void ContentPlaybackController::SeekBackward(double aSeekOffset) {
NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Seekbackward); MediaSessionActionDetails details;
details.mAction = MediaSessionAction::Seekbackward;
details.mSeekOffset.Construct(aSeekOffset);
if (IsMediaSessionActionSupported(details.mAction)) {
NotifyMediaSession(details);
}
} }
void ContentPlaybackController::SeekForward() { void ContentPlaybackController::SeekForward(double aSeekOffset) {
NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Seekforward); MediaSessionActionDetails details;
details.mAction = MediaSessionAction::Seekforward;
details.mSeekOffset.Construct(aSeekOffset);
if (IsMediaSessionActionSupported(details.mAction)) {
NotifyMediaSession(details);
}
} }
void ContentPlaybackController::PreviousTrack() { void ContentPlaybackController::PreviousTrack() {
@@ -195,18 +205,26 @@ void ContentMediaControlKeyHandler::HandleMediaControlAction(
case MediaControlKey::Nexttrack: case MediaControlKey::Nexttrack:
controller.NextTrack(); controller.NextTrack();
return; return;
case MediaControlKey::Seekbackward: case MediaControlKey::Seekbackward: {
controller.SeekBackward(); const SeekDetails& details = *aAction.mDetails;
MOZ_ASSERT(details.mRelativeSeekOffset);
controller.SeekBackward(details.mRelativeSeekOffset.value());
return; return;
case MediaControlKey::Seekforward: }
controller.SeekForward(); case MediaControlKey::Seekforward: {
const SeekDetails& details = *aAction.mDetails;
MOZ_ASSERT(details.mRelativeSeekOffset);
controller.SeekForward(details.mRelativeSeekOffset.value());
return; return;
}
case MediaControlKey::Skipad: case MediaControlKey::Skipad:
controller.SkipAd(); controller.SkipAd();
return; return;
case MediaControlKey::Seekto: { case MediaControlKey::Seekto: {
const SeekDetails& details = *aAction.mDetails; const SeekDetails& details = *aAction.mDetails;
controller.SeekTo(details.mSeekTime, details.mFastSeek); MOZ_ASSERT(details.mAbsolute);
controller.SeekTo(details.mAbsolute->mSeekTime,
details.mAbsolute->mFastSeek);
return; return;
} }
default: default:

View File

@@ -42,8 +42,8 @@ class MOZ_STACK_CLASS ContentPlaybackController {
MOZ_CAN_RUN_SCRIPT_BOUNDARY void Focus(); MOZ_CAN_RUN_SCRIPT_BOUNDARY void Focus();
void Play(); void Play();
void Pause(); void Pause();
void SeekBackward(); void SeekBackward(double aSeekOffset);
void SeekForward(); void SeekForward(double aSeekOffset);
void PreviousTrack(); void PreviousTrack();
void NextTrack(); void NextTrack();
void SkipAd(); void SkipAd();

View File

@@ -35,8 +35,8 @@ struct ParamTraits<mozilla::dom::MediaAudibleState>
mozilla::dom::MediaAudibleState::eAudible> {}; mozilla::dom::MediaAudibleState::eAudible> {};
template <> template <>
struct ParamTraits<mozilla::dom::SeekDetails> { struct ParamTraits<mozilla::dom::AbsoluteSeek> {
typedef mozilla::dom::SeekDetails paramType; typedef mozilla::dom::AbsoluteSeek paramType;
static void Write(MessageWriter* aWriter, const paramType& aParam) { static void Write(MessageWriter* aWriter, const paramType& aParam) {
WriteParam(aWriter, aParam.mSeekTime); WriteParam(aWriter, aParam.mSeekTime);
@@ -52,6 +52,24 @@ struct ParamTraits<mozilla::dom::SeekDetails> {
} }
}; };
template <>
struct ParamTraits<mozilla::dom::SeekDetails> {
typedef mozilla::dom::SeekDetails paramType;
static void Write(MessageWriter* aWriter, const paramType& aParam) {
WriteParam(aWriter, aParam.mAbsolute);
WriteParam(aWriter, aParam.mRelativeSeekOffset);
}
static bool Read(MessageReader* aReader, paramType* aResult) {
if (!ReadParam(aReader, &aResult->mAbsolute) ||
!ReadParam(aReader, &aResult->mRelativeSeekOffset)) {
return false;
}
return true;
}
};
template <> template <>
struct ParamTraits<mozilla::dom::MediaControlAction> { struct ParamTraits<mozilla::dom::MediaControlAction> {
typedef mozilla::dom::MediaControlAction paramType; typedef mozilla::dom::MediaControlAction paramType;

View File

@@ -64,18 +64,26 @@ void MediaControlKeyHandler::OnActionPerformed(
case MediaControlKey::Nexttrack: case MediaControlKey::Nexttrack:
controller->NextTrack(); controller->NextTrack();
return; return;
case MediaControlKey::Seekbackward: case MediaControlKey::Seekbackward: {
controller->SeekBackward(); const SeekDetails& details = *aAction.mDetails;
MOZ_ASSERT(details.mRelativeSeekOffset);
controller->SeekBackward(fmin(details.mRelativeSeekOffset.value(), 10.0));
return; return;
case MediaControlKey::Seekforward: }
controller->SeekForward(); case MediaControlKey::Seekforward: {
const SeekDetails& details = *aAction.mDetails;
MOZ_ASSERT(details.mRelativeSeekOffset);
controller->SeekForward(fmin(details.mRelativeSeekOffset.value(), 10.0));
return; return;
}
case MediaControlKey::Skipad: case MediaControlKey::Skipad:
controller->SkipAd(); controller->SkipAd();
return; return;
case MediaControlKey::Seekto: { case MediaControlKey::Seekto: {
const SeekDetails& details = *aAction.mDetails; const SeekDetails& details = *aAction.mDetails;
controller->SeekTo(details.mSeekTime, details.mFastSeek); MOZ_ASSERT(details.mAbsolute);
controller->SeekTo(details.mAbsolute->mSeekTime,
details.mAbsolute->mFastSeek);
return; return;
} }
case MediaControlKey::Stop: case MediaControlKey::Stop:

View File

@@ -5,6 +5,7 @@
#ifndef DOM_MEDIA_MEDIACONTROL_MEDIACONTROLKEYSOURCE_H_ #ifndef DOM_MEDIA_MEDIACONTROL_MEDIACONTROLKEYSOURCE_H_
#define DOM_MEDIA_MEDIACONTROL_MEDIACONTROLKEYSOURCE_H_ #define DOM_MEDIA_MEDIACONTROL_MEDIACONTROLKEYSOURCE_H_
#include "mozilla/Maybe.h"
#include "mozilla/dom/MediaControllerBinding.h" #include "mozilla/dom/MediaControllerBinding.h"
#include "mozilla/dom/MediaMetadata.h" #include "mozilla/dom/MediaMetadata.h"
#include "mozilla/dom/MediaSession.h" #include "mozilla/dom/MediaSession.h"
@@ -15,15 +16,20 @@
namespace mozilla::dom { namespace mozilla::dom {
// This is used to store seek related properties from MediaSessionActionDetails. // This is used to store seek related properties from MediaSessionActionDetails.
// However, currently we have no plan to support `seekOffset`.
// https://w3c.github.io/mediasession/#the-mediasessionactiondetails-dictionary // https://w3c.github.io/mediasession/#the-mediasessionactiondetails-dictionary
struct AbsoluteSeek {
double mSeekTime;
bool mFastSeek;
};
struct SeekDetails { struct SeekDetails {
Maybe<AbsoluteSeek> mAbsolute;
Maybe<double> mRelativeSeekOffset;
SeekDetails() = default; SeekDetails() = default;
explicit SeekDetails(double aSeekTime) : mSeekTime(aSeekTime) {}
SeekDetails(double aSeekTime, bool aFastSeek) SeekDetails(double aSeekTime, bool aFastSeek)
: mSeekTime(aSeekTime), mFastSeek(aFastSeek) {} : mAbsolute(Some(AbsoluteSeek{aSeekTime, aFastSeek})) {}
double mSeekTime = 0.0; explicit SeekDetails(double aRelativeSeekOffset)
bool mFastSeek = false; : mRelativeSeekOffset(Some(aRelativeSeekOffset)) {}
}; };
struct MediaControlAction { struct MediaControlAction {

View File

@@ -307,11 +307,18 @@ void MediaControlService::GenerateTestMediaControlKey(MediaControlKey aKey) {
if (!StaticPrefs::media_mediacontrol_testingevents_enabled()) { if (!StaticPrefs::media_mediacontrol_testingevents_enabled()) {
return; return;
} }
// Generate a seek details for `seekto` // Generate seek details when necessary
if (aKey == MediaControlKey::Seekto) { switch (aKey) {
case MediaControlKey::Seekto:
mMediaKeysHandler->OnActionPerformed( mMediaKeysHandler->OnActionPerformed(
MediaControlAction(aKey, SeekDetails())); MediaControlAction(aKey, SeekDetails(0.0, false /* fast seek */)));
} else { break;
case MediaControlKey::Seekbackward:
case MediaControlKey::Seekforward:
mMediaKeysHandler->OnActionPerformed(
MediaControlAction(aKey, SeekDetails(0.0)));
break;
default:
mMediaKeysHandler->OnActionPerformed(MediaControlAction(aKey)); mMediaKeysHandler->OnActionPerformed(MediaControlAction(aKey));
} }
} }

View File

@@ -142,16 +142,16 @@ void MediaController::NextTrack() {
MediaControlAction(MediaControlKey::Nexttrack)); MediaControlAction(MediaControlKey::Nexttrack));
} }
void MediaController::SeekBackward() { void MediaController::SeekBackward(double aSeekOffset) {
LOG("Seek Backward"); LOG("Seek Backward");
UpdateMediaControlActionToContentMediaIfNeeded( UpdateMediaControlActionToContentMediaIfNeeded(MediaControlAction(
MediaControlAction(MediaControlKey::Seekbackward)); MediaControlKey::Seekbackward, SeekDetails(aSeekOffset)));
} }
void MediaController::SeekForward() { void MediaController::SeekForward(double aSeekOffset) {
LOG("Seek Forward"); LOG("Seek Forward");
UpdateMediaControlActionToContentMediaIfNeeded( UpdateMediaControlActionToContentMediaIfNeeded(MediaControlAction(
MediaControlAction(MediaControlKey::Seekforward)); MediaControlKey::Seekforward, SeekDetails(aSeekOffset)));
} }
void MediaController::SkipAd() { void MediaController::SkipAd() {

View File

@@ -36,8 +36,8 @@ class IMediaController {
virtual void Stop() = 0; virtual void Stop() = 0;
virtual void PrevTrack() = 0; virtual void PrevTrack() = 0;
virtual void NextTrack() = 0; virtual void NextTrack() = 0;
virtual void SeekBackward() = 0; virtual void SeekBackward(double aSeekOffset) = 0;
virtual void SeekForward() = 0; virtual void SeekForward(double aSeekOffset) = 0;
virtual void SkipAd() = 0; virtual void SkipAd() = 0;
virtual void SeekTo(double aSeekTime, bool aFastSeek) = 0; virtual void SeekTo(double aSeekTime, bool aFastSeek) = 0;
@@ -104,8 +104,8 @@ class MediaController final : public DOMEventTargetHelper,
void Stop() override; void Stop() override;
void PrevTrack() override; void PrevTrack() override;
void NextTrack() override; void NextTrack() override;
void SeekBackward() override; void SeekBackward(double aSeekOffset) override;
void SeekForward() override; void SeekForward(double aSeekOffset) override;
void SkipAd() override; void SkipAd() override;
void SeekTo(double aSeekTime, bool aFastSeek) override; void SeekTo(double aSeekTime, bool aFastSeek) override;

View File

@@ -50,7 +50,7 @@ skip-if = ["verify && os == 'mac'"] # bug 1673509
["browser_media_control_position_state.js"] ["browser_media_control_position_state.js"]
skip-if = ["apple_catalina && !debug"] # Bug 1775892 skip-if = ["apple_catalina && !debug"] # Bug 1775892
["browser_media_control_seekto.js"] ["browser_media_control_seek.js"]
["browser_media_control_stop_timer.js"] ["browser_media_control_stop_timer.js"]

View File

@@ -28,7 +28,7 @@ add_task(async function testSetPositionState() {
seekTime: seektime, seekTime: seektime,
}); });
info(`seek to ${seektime} seconds and set fastseek to boolean`); info(`seek to ${seektime} seconds and set fastseek to true`);
await PerformSeekTo(tab, { await PerformSeekTo(tab, {
seekTime: seektime, seekTime: seektime,
fastSeek: true, fastSeek: true,
@@ -44,6 +44,28 @@ add_task(async function testSetPositionState() {
await tab.close(); await tab.close();
}); });
add_task(async function testSeekRelative() {
info(`open media page`);
const tab = await createLoadedTabWrapper(PAGE_URL);
info(`start media`);
await playMedia(tab, testVideoId);
const seekoffset = 5;
info(`seek forward ${seekoffset} seconds`);
await PerformSeekRelative(tab, "seekforward", {
seekOffset: seekoffset,
});
info(`seek backward ${seekoffset} seconds`);
await PerformSeekRelative(tab, "seekbackward", {
seekOffset: seekoffset,
});
info(`remove tab`);
await tab.close();
});
/** /**
* The following is helper function. * The following is helper function.
*/ */
@@ -82,10 +104,46 @@ async function PerformSeekTo(tab, seekDetails) {
[testVideoId], [testVideoId],
Id => { Id => {
const video = content.document.getElementById(Id); const video = content.document.getElementById(Id);
return new Promise(r => (video.onseeking = r())); return new Promise(r => (video.onseeked = r()));
} }
); );
const { seekTime, fastSeek } = seekDetails; const { seekTime, fastSeek } = seekDetails;
tab.linkedBrowser.browsingContext.mediaController.seekTo(seekTime, fastSeek); tab.linkedBrowser.browsingContext.mediaController.seekTo(seekTime, fastSeek);
await seekPromise; await seekPromise;
} }
async function PerformSeekRelative(tab, mode, seekDetails) {
await SpecialPowers.spawn(
tab.linkedBrowser,
[seekDetails, mode, testVideoId],
(seekDetails, mode, Id) => {
const { seekOffset } = seekDetails;
content.navigator.mediaSession.setActionHandler(mode, details => {
Assert.notEqual(
details.seekOffset,
undefined,
"Seekoffset must be presented"
);
is(seekOffset, details.seekOffset, "Get correct seekoffset");
content.document.getElementById(Id).currentTime +=
mode == "seekForward" ? seekOffset : -seekOffset;
});
}
);
const seekPromise = SpecialPowers.spawn(
tab.linkedBrowser,
[testVideoId],
Id => {
const video = content.document.getElementById(Id);
return new Promise(r => (video.onseeked = r()));
}
);
const { seekOffset } = seekDetails;
if (mode === "seekforward") {
tab.linkedBrowser.browsingContext.mediaController.seekForward(seekOffset);
} else {
tab.linkedBrowser.browsingContext.mediaController.seekBackward(seekOffset);
}
await seekPromise;
}

View File

@@ -49,7 +49,9 @@ static inline Maybe<dom::MediaControlKey> GetMediaControlKey(
{"Pause", dom::MediaControlKey::Pause}, {"Pause", dom::MediaControlKey::Pause},
{"PlayPause", dom::MediaControlKey::Playpause}, {"PlayPause", dom::MediaControlKey::Playpause},
{"Stop", dom::MediaControlKey::Stop}, {"Stop", dom::MediaControlKey::Stop},
{"Play", dom::MediaControlKey::Play}}; {"Play", dom::MediaControlKey::Play},
{"SetPosition", dom::MediaControlKey::Seekto},
{"Seek", dom::MediaControlKey::Seekforward}};
auto it = map.find(aMethodName); auto it = map.find(aMethodName);
return it == map.end() ? Nothing() : Some(it->second); return it == map.end() ? Nothing() : Some(it->second);
@@ -73,8 +75,33 @@ static void HandleMethodCall(GDBusConnection* aConnection, const gchar* aSender,
return; return;
} }
dom::SeekDetails seekDetails{};
if (key.value() == dom::MediaControlKey::Seekto ||
key.value() == dom::MediaControlKey::Seekforward) {
RefPtr<GVariant> child = dont_AddRef(g_variant_get_child_value(
aParameters, key.value() == dom::MediaControlKey::Seekto));
double seekValue;
if (g_variant_is_of_type(child, G_VARIANT_TYPE_INT64)) {
seekValue = (double)g_variant_get_int64(child) / 1000000.0;
} else {
g_dbus_method_invocation_return_error(
aInvocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
"Invalid arguments for %s.%s.%s", aObjectPath, aInterfaceName,
aMethodName);
return;
}
if (key.value() == dom::MediaControlKey::Seekto) {
seekDetails = dom::SeekDetails(seekValue, false /* fast seek */);
} else if (seekValue > 0.0) {
seekDetails = dom::SeekDetails(seekValue);
} else {
key = Some(dom::MediaControlKey::Seekbackward);
seekDetails = dom::SeekDetails(-1 * seekValue);
}
}
MPRISServiceHandler* handler = static_cast<MPRISServiceHandler*>(aUserData); MPRISServiceHandler* handler = static_cast<MPRISServiceHandler*>(aUserData);
if (handler->PressKey(key.value())) { if (handler->PressKey(dom::MediaControlAction(key.value(), seekDetails))) {
g_dbus_method_invocation_return_value(aInvocation, nullptr); g_dbus_method_invocation_return_value(aInvocation, nullptr);
} else { } else {
g_dbus_method_invocation_return_error( g_dbus_method_invocation_return_error(
@@ -100,6 +127,7 @@ enum class Property : uint8_t {
eCanControl, eCanControl,
eGetPlaybackStatus, eGetPlaybackStatus,
eGetMetadata, eGetMetadata,
eGetPosition,
}; };
static inline Maybe<dom::MediaControlKey> GetPairedKey(Property aProperty) { static inline Maybe<dom::MediaControlKey> GetPairedKey(Property aProperty) {
@@ -137,7 +165,8 @@ static inline Maybe<Property> GetProperty(const gchar* aPropertyName) {
{"CanSeek", Property::eCanSeek}, {"CanSeek", Property::eCanSeek},
{"CanControl", Property::eCanControl}, {"CanControl", Property::eCanControl},
{"PlaybackStatus", Property::eGetPlaybackStatus}, {"PlaybackStatus", Property::eGetPlaybackStatus},
{"Metadata", Property::eGetMetadata}}; {"Metadata", Property::eGetMetadata},
{"Position", Property::eGetPosition}};
auto it = map.find(aPropertyName); auto it = map.find(aPropertyName);
return (it == map.end() ? Nothing() : Some(it->second)); return (it == map.end() ? Nothing() : Some(it->second));
@@ -170,16 +199,21 @@ static GVariant* HandleGetProperty(GDBusConnection* aConnection,
return handler->GetPlaybackStatus(); return handler->GetPlaybackStatus();
case Property::eGetMetadata: case Property::eGetMetadata:
return handler->GetMetadataAsGVariant(); return handler->GetMetadataAsGVariant();
case Property::eGetPosition: {
CheckedInt64 position =
CheckedInt64((int64_t)handler->GetPositionSeconds()) * 1000000;
return g_variant_new_int64(position.isValid() ? position.value() : 0);
}
case Property::eIdentity: case Property::eIdentity:
return g_variant_new_string(handler->Identity()); return g_variant_new_string(handler->Identity());
case Property::eDesktopEntry: case Property::eDesktopEntry:
return g_variant_new_string(handler->DesktopEntry()); return g_variant_new_string(handler->DesktopEntry());
case Property::eHasTrackList: case Property::eHasTrackList:
case Property::eCanQuit: case Property::eCanQuit:
case Property::eCanSeek:
return g_variant_new_boolean(false); return g_variant_new_boolean(false);
// Play/Pause would be blocked if CanControl is false // Play/Pause would be blocked if CanControl is false
case Property::eCanControl: case Property::eCanControl:
case Property::eCanSeek:
return g_variant_new_boolean(true); return g_variant_new_boolean(true);
case Property::eCanRaise: case Property::eCanRaise:
case Property::eCanGoNext: case Property::eCanGoNext:
@@ -443,14 +477,16 @@ const char* MPRISServiceHandler::DesktopEntry() const {
return mDesktopEntry.get(); return mDesktopEntry.get();
} }
bool MPRISServiceHandler::PressKey(dom::MediaControlKey aKey) const { bool MPRISServiceHandler::PressKey(
const dom::MediaControlAction& aAction) const {
MOZ_ASSERT(mInitialized); MOZ_ASSERT(mInitialized);
if (!IsMediaKeySupported(aKey)) { if (!IsMediaKeySupported(aAction.mKey.value())) {
LOGMPRIS("%s is not supported", dom::GetEnumString(aKey).get()); LOGMPRIS("%s is not supported",
dom::GetEnumString(aAction.mKey.value()).get());
return false; return false;
} }
LOGMPRIS("Press %s", dom::GetEnumString(aKey).get()); LOGMPRIS("Press %s", dom::GetEnumString(aAction.mKey.value()).get());
EmitEvent(aKey); EmitEvent(aAction);
return true; return true;
} }
@@ -812,9 +848,10 @@ GVariant* MPRISServiceHandler::GetMetadataAsGVariant() const {
return g_variant_builder_end(&builder); return g_variant_builder_end(&builder);
} }
void MPRISServiceHandler::EmitEvent(dom::MediaControlKey aKey) const { void MPRISServiceHandler::EmitEvent(
const dom::MediaControlAction& aAction) const {
for (const auto& listener : mListeners) { for (const auto& listener : mListeners) {
listener->OnActionPerformed(dom::MediaControlAction(aKey)); listener->OnActionPerformed(aAction);
} }
} }
@@ -860,6 +897,18 @@ void MPRISServiceHandler::SetSupportedMediaKeys(
} }
} }
void MPRISServiceHandler::SetPositionState(
const Maybe<dom::PositionState>& aState) {
mPositionState = aState;
}
double MPRISServiceHandler::GetPositionSeconds() const {
if (mPositionState.isSome()) {
return mPositionState.value().CurrentPlaybackPosition();
}
return 0.0;
}
bool MPRISServiceHandler::IsMediaKeySupported(dom::MediaControlKey aKey) const { bool MPRISServiceHandler::IsMediaKeySupported(dom::MediaControlKey aKey) const {
return mSupportedKeys & GetMediaKeyMask(aKey); return mSupportedKeys & GetMediaKeyMask(aKey);
} }

View File

@@ -72,13 +72,16 @@ class MPRISServiceHandler final : public dom::MediaControlKeySource {
const char* Identity() const; const char* Identity() const;
const char* DesktopEntry() const; const char* DesktopEntry() const;
bool PressKey(dom::MediaControlKey aKey) const; bool PressKey(const dom::MediaControlAction& aAction) const;
void SetMediaMetadata(const dom::MediaMetadataBase& aMetadata) override; void SetMediaMetadata(const dom::MediaMetadataBase& aMetadata) override;
GVariant* GetMetadataAsGVariant() const; GVariant* GetMetadataAsGVariant() const;
void SetSupportedMediaKeys(const MediaKeysArray& aSupportedKeys) override; void SetSupportedMediaKeys(const MediaKeysArray& aSupportedKeys) override;
void SetPositionState(const Maybe<dom::PositionState>& aState) override;
double GetPositionSeconds() const;
bool IsMediaKeySupported(dom::MediaControlKey aKey) const; bool IsMediaKeySupported(dom::MediaControlKey aKey) const;
void OwnName(GDBusConnection* aConnection); void OwnName(GDBusConnection* aConnection);
@@ -110,6 +113,8 @@ class MPRISServiceHandler final : public dom::MediaControlKeySource {
// A bitmask indicating what keys are enabled // A bitmask indicating what keys are enabled
uint32_t mSupportedKeys = 0; uint32_t mSupportedKeys = 0;
Maybe<dom::PositionState> mPositionState;
class MPRISMetadata : public dom::MediaMetadataBase { class MPRISMetadata : public dom::MediaMetadataBase {
public: public:
MPRISMetadata() = default; MPRISMetadata() = default;
@@ -168,7 +173,7 @@ class MPRISServiceHandler final : public dom::MediaControlKeySource {
static void OnBusAcquiredStatic(GDBusConnection* aConnection, static void OnBusAcquiredStatic(GDBusConnection* aConnection,
const gchar* aName, gpointer aUserData); const gchar* aName, gpointer aUserData);
void EmitEvent(dom::MediaControlKey aKey) const; void EmitEvent(const dom::MediaControlAction& aAction) const;
bool EmitMetadataChanged() const; bool EmitMetadataChanged() const;

View File

@@ -426,9 +426,9 @@ void WindowsSMTCProvider::OnPositionChangeRequested(double aPosition) const {
} }
for (const auto& listener : mListeners) { for (const auto& listener : mListeners) {
listener->OnActionPerformed( listener->OnActionPerformed(mozilla::dom::MediaControlAction(
mozilla::dom::MediaControlAction(mozilla::dom::MediaControlKey::Seekto, mozilla::dom::MediaControlKey::Seekto,
mozilla::dom::SeekDetails(aPosition))); mozilla::dom::SeekDetails(aPosition, false /* fast seek */)));
} }
} }