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:
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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 */)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user