0

GMC V2 Add mute button to media notification.

This CL adds SetMute to MediaSession so that we can change mute status
of the player from media session.

Bug: 1238990

Change-Id: Ida26834d006cfb76056c72934d4844f44c0f0a3e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2881119
Commit-Queue: Jazz Xu <jazzhsu@chromium.org>
Reviewed-by: Wez <wez@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Tommy Steimel <steimel@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Tao Wu <wutao@chromium.org>
Reviewed-by: François Beaufort <beaufort.francois@gmail.com>
Reviewed-by: Olga Sharonova <olka@chromium.org>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Sean Topping <seantopping@chromium.org>
Cr-Commit-Position: refs/heads/main@{#913135}
This commit is contained in:
Jazz Xu
2021-08-18 20:26:38 +00:00
committed by Chromium LUCI CQ
parent c724a89f4b
commit 0f11c0f272
41 changed files with 141 additions and 17 deletions

@ -134,6 +134,7 @@ const gfx::VectorIcon& GetVectorIconForMediaAction(MediaSessionAction action) {
case MediaSessionAction::kToggleCamera:
case MediaSessionAction::kHangUp:
case MediaSessionAction::kRaise:
case MediaSessionAction::kSetMute:
NOTREACHED();
break;
}

@ -90,6 +90,7 @@ const gfx::VectorIcon& GetVectorIconForMediaAction(MediaSessionAction action) {
case MediaSessionAction::kToggleCamera:
case MediaSessionAction::kHangUp:
case MediaSessionAction::kRaise:
case MediaSessionAction::kSetMute:
NOTREACHED();
break;
}

@ -68,6 +68,7 @@ void CastMediaSessionController::Send(
case media_session::mojom::MediaSessionAction::kToggleCamera:
case media_session::mojom::MediaSessionAction::kHangUp:
case media_session::mojom::MediaSessionAction::kRaise:
case media_session::mojom::MediaSessionAction::kSetMute:
NOTREACHED();
return;
}

@ -71,8 +71,10 @@ void MediaSessionNotificationItem::MediaSessionInfoChanged(
MaybeUnfreeze();
MaybeHideOrShowNotification();
if (view_ && !frozen_)
if (view_ && !frozen_) {
view_->UpdateWithMediaSessionInfo(session_info_);
view_->UpdateWithMuteStatus(session_info_->muted);
}
}
void MediaSessionNotificationItem::MediaSessionMetadataChanged(
@ -153,6 +155,7 @@ void MediaSessionNotificationItem::SetView(
view_->UpdateWithMediaSessionInfo(session_info_);
view_->UpdateWithMediaMetadata(session_metadata_);
view_->UpdateWithMediaActions(session_actions_);
view_->UpdateWithMuteStatus(session_info_->muted);
if (session_position_.has_value())
view_->UpdateWithMediaPosition(*session_position_);
@ -196,6 +199,11 @@ void MediaSessionNotificationItem::Raise() {
media_controller_remote_->Raise();
}
void MediaSessionNotificationItem::SetMute(bool mute) {
if (!frozen_)
media_controller_remote_->SetMute(mute);
}
void MediaSessionNotificationItem::SetController(
mojo::Remote<media_session::mojom::MediaController> controller,
media_session::mojom::MediaSessionInfoPtr session_info) {
@ -310,6 +318,7 @@ void MediaSessionNotificationItem::Unfreeze() {
view_->UpdateWithMediaSessionInfo(session_info_);
view_->UpdateWithMediaMetadata(session_metadata_);
view_->UpdateWithMediaActions(session_actions_);
view_->UpdateWithMuteStatus(session_info_->muted);
if (session_position_.has_value())
view_->UpdateWithMediaPosition(*session_position_);

@ -92,7 +92,7 @@ class MediaSessionNotificationItem
void Dismiss() override;
media_message_center::SourceType SourceType() override;
void SetVolume(float volume) override {}
void SetMute(bool mute) override {}
void SetMute(bool mute) override;
// Calls |Raise()| on the underlying MediaSession, which will focus the
// WebContents if the MediaSession is associated with one.

@ -61,6 +61,7 @@ class MockMediaSession : public content::MediaSession {
MOCK_METHOD0(ToggleCamera, void());
MOCK_METHOD0(HangUp, void());
MOCK_METHOD0(Raise, void());
MOCK_METHOD1(SetMute, void(bool));
private:
DISALLOW_COPY_AND_ASSIGN(MockMediaSession);

@ -100,6 +100,7 @@ class MediaControllerMock : public media_session::mojom::MediaController {
MOCK_METHOD(void, ToggleCamera, ());
MOCK_METHOD(void, HangUp, ());
MOCK_METHOD(void, Raise, ());
MOCK_METHOD(void, SetMute, (bool mute));
void AddObserver(
mojo::PendingRemote<media_session::mojom::MediaControllerObserver> remote)
override {

@ -58,6 +58,7 @@ class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantMediaSession
void ToggleCamera() override {}
void HangUp() override {}
void Raise() override {}
void SetMute(bool mute) override {}
// Requests/abandons audio focus to the AudioFocusManager.
void RequestAudioFocus(media_session::mojom::AudioFocusType audio_focus_type);

@ -104,6 +104,7 @@ const gfx::VectorIcon* GetVectorIconForMediaAction(MediaSessionAction action) {
case MediaSessionAction::kToggleCamera:
case MediaSessionAction::kHangUp:
case MediaSessionAction::kRaise:
case MediaSessionAction::kSetMute:
NOTREACHED();
break;
}

@ -111,6 +111,7 @@ const gfx::VectorIcon* GetVectorIconForMediaAction(MediaSessionAction action) {
case MediaSessionAction::kToggleCamera:
case MediaSessionAction::kHangUp:
case MediaSessionAction::kRaise:
case MediaSessionAction::kSetMute:
NOTREACHED();
break;
}
@ -154,6 +155,7 @@ const std::u16string GetAccessibleNameForMediaAction(
case MediaSessionAction::kToggleCamera:
case MediaSessionAction::kHangUp:
case MediaSessionAction::kRaise:
case MediaSessionAction::kSetMute:
NOTREACHED();
break;
}
@ -439,20 +441,19 @@ MediaNotificationViewModernImpl::MediaNotificationViewModernImpl(
volume_slider->SetPreferredSize(kVolumeSliderSize);
volume_slider_ =
util_buttons_container->AddChildView(std::move(volume_slider));
auto mute_button =
std::make_unique<views::ToggleImageButton>(base::BindRepeating(
&MediaNotificationViewModernImpl::OnMuteButtonClicked,
base::Unretained(this)));
mute_button->SetPreferredSize(kMuteButtonSize);
mute_button->SetImageHorizontalAlignment(
views::ImageButton::HorizontalAlignment::ALIGN_CENTER);
mute_button->SetImageVerticalAlignment(
views::ImageButton::VerticalAlignment::ALIGN_MIDDLE);
mute_button_ =
util_buttons_container->AddChildView(std::move(mute_button));
}
auto mute_button =
std::make_unique<views::ToggleImageButton>(base::BindRepeating(
&MediaNotificationViewModernImpl::OnMuteButtonClicked,
base::Unretained(this)));
mute_button->SetPreferredSize(kMuteButtonSize);
mute_button->SetImageHorizontalAlignment(
views::ImageButton::HorizontalAlignment::ALIGN_CENTER);
mute_button->SetImageVerticalAlignment(
views::ImageButton::VerticalAlignment::ALIGN_MIDDLE);
mute_button_ = util_buttons_container->AddChildView(std::move(mute_button));
AddChildView(std::move(util_buttons_container));
}

@ -199,6 +199,7 @@ void ActiveMediaSessionController::PerformAction(MediaSessionAction action) {
case MediaSessionAction::kToggleCamera:
case MediaSessionAction::kHangUp:
case MediaSessionAction::kRaise:
case MediaSessionAction::kSetMute:
NOTREACHED();
return;
}
@ -251,6 +252,7 @@ ActiveMediaSessionController::MediaSessionActionToKeyCode(
case MediaSessionAction::kToggleCamera:
case MediaSessionAction::kHangUp:
case MediaSessionAction::kRaise:
case MediaSessionAction::kSetMute:
return absl::nullopt;
}
}

@ -337,6 +337,9 @@ void MediaWebContentsObserver::MediaPlayerObserverHostImpl::
media_web_contents_observer_->web_contents_impl()->MediaMutedStatusChanged(
media_player_id_, muted);
media_web_contents_observer_->session_controllers_manager()
->OnMediaMutedStatusChanged(media_player_id_, muted);
PlayerInfo* player_info = GetPlayerInfo();
if (!player_info)
return;

@ -141,6 +141,14 @@ void MediaSessionController::OnSetAudioSinkId(
->SetAudioSinkId(hashed_sink_id);
}
void MediaSessionController::OnSetMute(int player_id, bool mute) {
DCHECK_EQ(player_id_, player_id);
web_contents_->media_web_contents_observer()
->GetMediaPlayerRemote(id_)
->RequestMute(mute);
}
RenderFrameHost* MediaSessionController::render_frame_host() const {
return RenderFrameHost::FromID(id_.frame_routing_id);
}
@ -185,6 +193,10 @@ void MediaSessionController::OnMediaPositionStateChanged(
media_session_->RebuildAndNotifyMediaPositionChanged();
}
void MediaSessionController::OnMediaMutedStatusChanged(bool mute) {
media_session_->OnMediaMutedStatusChanged(mute);
}
void MediaSessionController::OnPictureInPictureAvailabilityChanged(
bool available) {
is_picture_in_picture_available_ = available;

@ -60,6 +60,7 @@ class CONTENT_EXPORT MediaSessionController
void OnExitPictureInPicture(int player_id) override;
void OnSetAudioSinkId(int player_id,
const std::string& raw_device_id) override;
void OnSetMute(int player_id, bool mute) override;
RenderFrameHost* render_frame_host() const override;
absl::optional<media_session::MediaPosition> GetPosition(
int player_id) const override;
@ -82,6 +83,8 @@ class CONTENT_EXPORT MediaSessionController
void OnMediaPositionStateChanged(
const media_session::MediaPosition& position);
void OnMediaMutedStatusChanged(bool mute);
// Called when the media picture-in-picture availability has changed.
void OnPictureInPictureAvailabilityChanged(bool available);

@ -138,6 +138,8 @@ class TestMediaPlayer : public media::mojom::MediaPlayer {
void RequestExitPictureInPicture() override {}
void RequestMute(bool mute) override {}
void SetVolumeMultiplier(double multiplier) override {
received_volume_multiplier_ = multiplier;
if (run_loop_for_volume_)

@ -104,6 +104,16 @@ void MediaSessionControllersManager::WebContentsMutedStateChanged(bool muted) {
entry.second->WebContentsMutedStateChanged(muted);
}
void MediaSessionControllersManager::OnMediaMutedStatusChanged(
const MediaPlayerId& id,
bool mute) {
if (!IsMediaSessionEnabled())
return;
MediaSessionController* const controller = FindOrCreateController(id);
controller->OnMediaMutedStatusChanged(mute);
}
void MediaSessionControllersManager::OnPictureInPictureAvailabilityChanged(
const MediaPlayerId& id,
bool available) {

@ -67,6 +67,9 @@ class CONTENT_EXPORT MediaSessionControllersManager {
// Called when the WebContents was muted or unmuted.
void WebContentsMutedStateChanged(bool muted);
// Called when the player's mute status changed.
void OnMediaMutedStatusChanged(const MediaPlayerId& id, bool mute);
// Called when picture-in-picture availability for the player |id| has
// changed.
void OnPictureInPictureAvailabilityChanged(const MediaPlayerId& id,

@ -149,6 +149,8 @@ MediaSessionUserAction MediaSessionActionToUserAction(
return MediaSessionUserAction::kHangUp;
case media_session::mojom::MediaSessionAction::kRaise:
return MediaSessionUserAction::kRaise;
case media_session::mojom::MediaSessionAction::kSetMute:
return MediaSessionUserAction::kSetMute;
}
NOTREACHED();
return MediaSessionUserAction::kPlay;
@ -1013,6 +1015,8 @@ MediaSessionImpl::GetMediaSessionInfoSync() {
info->camera_state = media_session::mojom::CameraState::kUnknown;
}
info->muted = is_muted_;
return info;
}
@ -1173,6 +1177,12 @@ void MediaSessionImpl::Raise() {
delegate->ActivateContents(web_contents());
}
void MediaSessionImpl::SetMute(bool mute) {
DCHECK_EQ(normal_players_.size(), 1u);
normal_players_.begin()->first.observer->OnSetMute(
normal_players_.begin()->first.player_id, mute);
}
void MediaSessionImpl::GetMediaImageBitmap(
const media_session::MediaImage& image,
int minimum_size_px,
@ -1481,6 +1491,11 @@ MediaSessionServiceImpl* MediaSessionImpl::ComputeServiceForRouting() {
return best_frame ? services_[best_frame->GetGlobalId()] : nullptr;
}
void MediaSessionImpl::OnMediaMutedStatusChanged(bool mute) {
is_muted_ = mute;
RebuildAndNotifyMediaSessionInfoChanged();
}
void MediaSessionImpl::OnPictureInPictureAvailabilityChanged() {
if (normal_players_.size() != 1)
return;

@ -279,6 +279,9 @@ class MediaSessionImpl : public MediaSession,
// Brings the associated tab into focus.
void Raise() override;
// Mute or unmute the media player.
void SetMute(bool mute) override;
// Downloads the bitmap version of a MediaImage at least |minimum_size_px|
// and closest to |desired_size_px|. If the download failed, was too small or
// the image did not come from the media session then returns a null image.
@ -292,6 +295,8 @@ class MediaSessionImpl : public MediaSession,
return audio_focus_group_id_;
}
void OnMediaMutedStatusChanged(bool mute);
void OnPictureInPictureAvailabilityChanged();
// Called when any of the normal players have switched to a different audio
@ -481,6 +486,8 @@ class MediaSessionImpl : public MediaSession,
// True if the WebContents associated with this MediaSessionImpl is focused.
bool focused_ = false;
bool is_muted_ = false;
// Used to persist audio device selection between navigations on the same
// origin.
url::Origin origin_;

@ -58,6 +58,7 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
MOCK_METHOD1(OnExitPictureInPicture, void(int player_id));
MOCK_METHOD2(OnSetAudioSinkId,
void(int player_id, const std::string& raw_device_id));
MOCK_METHOD2(OnSetMute, void(int player_id, bool mute));
absl::optional<media_session::MediaPosition> GetPosition(
int player_id) const override {

@ -44,6 +44,7 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
void OnExitPictureInPicture(int player_id) override {}
void OnSetAudioSinkId(int player_id,
const std::string& raw_device_id) override {}
void OnSetMute(int player_id, bool mute) override {}
absl::optional<media_session::MediaPosition> GetPosition(
int player_id) const override {

@ -52,6 +52,9 @@ class MediaSessionPlayerObserver {
virtual void OnSetAudioSinkId(int player_id,
const std::string& raw_device_id) = 0;
// The given |player_id| has been requested to mute or unmute.
virtual void OnSetMute(int player_id, bool mute) = 0;
// Returns the position for |player_id|.
virtual absl::optional<media_session::MediaPosition> GetPosition(
int player_id) const = 0;

@ -61,6 +61,7 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
void OnExitPictureInPicture(int player_id) override {}
void OnSetAudioSinkId(int player_id,
const std::string& raw_device_id) override {}
void OnSetMute(int player_id, bool mute) override {}
absl::optional<media_session::MediaPosition> GetPosition(
int player_id) const override {

@ -50,7 +50,8 @@ class CONTENT_EXPORT MediaSessionUmaHelper {
kToggleCamera = 17,
kHangUp = 18,
kRaise = 19,
kMaxValue = kRaise,
kSetMute = 20,
kMaxValue = kSetMute,
};
MediaSessionUmaHelper();

@ -89,6 +89,11 @@ void MockMediaSessionPlayerObserver::OnSetAudioSinkId(
players_[player_id].audio_sink_id_ = raw_device_id;
}
void MockMediaSessionPlayerObserver::OnSetMute(int player_id, bool mute) {
EXPECT_GE(player_id, 0);
EXPECT_GT(players_.size(), static_cast<size_t>(player_id));
}
absl::optional<media_session::MediaPosition>
MockMediaSessionPlayerObserver::GetPosition(int player_id) const {
EXPECT_GE(player_id, 0);

@ -34,6 +34,7 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
void OnExitPictureInPicture(int player_id) override;
void OnSetAudioSinkId(int player_id,
const std::string& raw_device_id) override;
void OnSetMute(int player_id, bool mute) override;
absl::optional<media_session::MediaPosition> GetPosition(
int player_id) const override;
bool IsPictureInPictureAvailable(int player_id) const override;

@ -80,6 +80,8 @@ void PepperPlayerDelegate::OnSetAudioSinkId(int player_id,
NOTREACHED();
}
void PepperPlayerDelegate::OnSetMute(int player_id, bool mute) {}
absl::optional<media_session::MediaPosition> PepperPlayerDelegate::GetPosition(
int player_id) const {
// Pepper does not support position data.

@ -34,6 +34,7 @@ class PepperPlayerDelegate : public MediaSessionPlayerObserver {
void OnExitPictureInPicture(int player_id) override;
void OnSetAudioSinkId(int player_id,
const std::string& raw_device_id) override;
void OnSetMute(int player_id, bool mute) override;
absl::optional<media_session::MediaPosition> GetPosition(
int player_id) const override;
bool IsPictureInPictureAvailable(int player_id) const override;

@ -130,6 +130,7 @@ class PictureInPictureMediaPlayerReceiver : public media::mojom::MediaPlayer {
void RequestSeekTo(base::TimeDelta seek_time) override {}
void RequestEnterPictureInPicture() override {}
void RequestExitPictureInPicture() override {}
void RequestMute(bool mute) override {}
void SetVolumeMultiplier(double multiplier) override {}
void SetPersistentState(bool persistent) override {}
void SetPowerExperimentState(bool enabled) override {}

@ -55,6 +55,8 @@ fuchsia::media::sessions2::PlayerCapabilityFlags ActionToCapabilityFlag(
return {}; // PlayerControl does not support hanging up.
case MediaSessionAction::kRaise:
return {}; // PlayerControl does not support raising.
case MediaSessionAction::kSetMute:
return {}; // TODO(crbug.com/1240811): implement set mute.
}
}

@ -41,6 +41,7 @@ class FakeMediaSession : public content::MediaSession {
MOCK_METHOD0(ToggleCamera, void());
MOCK_METHOD0(HangUp, void());
MOCK_METHOD0(Raise, void());
MOCK_METHOD1(SetMute, void(bool));
// content::MediaSession APIs faked to implement testing behaviour.
MOCK_METHOD1(DidReceiveAction,

@ -9,7 +9,8 @@ import "mojo/public/mojom/base/time.mojom";
import "services/media_session/public/mojom/media_session.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
// Implemented by HTMLMediaElement in the renderer process.
// Implemented by HTMLMediaElement in the renderer process to allow the
// browser to control media playback.
interface MediaPlayer {
// Requests the media player to start or resume media playback.
RequestPlay();
@ -32,6 +33,9 @@ interface MediaPlayer {
// Requests the media player to exit the Picture-in-Picture mode.
RequestExitPictureInPicture();
// Requests the media player to mute or unmute.
RequestMute(bool mute);
// Set the volume multiplier to control audio ducking.
// Output volume should be set to |player_volume| * |multiplier|. The range
// of |multiplier| is [0, 1], where 1 indicates normal (non-ducked) playback.

@ -328,6 +328,13 @@ void MediaController::Raise() {
session_->ipc()->Raise();
}
void MediaController::SetMute(bool mute) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (session_)
session_->ipc()->SetMute(mute);
}
void MediaController::SetMediaSession(AudioFocusRequest* session) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

@ -57,6 +57,7 @@ class MediaController : public mojom::MediaController,
void ToggleCamera() override;
void HangUp() override;
void Raise() override;
void SetMute(bool mute) override;
// mojom::MediaSessionObserver overrides.
void MediaSessionInfoChanged(

@ -168,6 +168,7 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) MockMediaSession
void ToggleCamera() override {}
void HangUp() override {}
void Raise() override {}
void SetMute(bool mute) override {}
void SetIsControllable(bool value);
void SetPreferStop(bool value) { prefer_stop_ = value; }

@ -160,6 +160,7 @@ class COMPONENT_EXPORT(MEDIA_SESSION_TEST_SUPPORT_CPP) TestMediaController
void ToggleCamera() override {}
void HangUp() override {}
void Raise() override {}
void SetMute(bool mute) override {}
int toggle_suspend_resume_count() const {
return toggle_suspend_resume_count_;

@ -58,6 +58,7 @@ void PerformMediaSessionAction(
case mojom::MediaSessionAction::kRaise:
media_controller_remote->Raise();
break;
case mojom::MediaSessionAction::kSetMute:
case mojom::MediaSessionAction::kSkipAd:
case mojom::MediaSessionAction::kSeekTo:
case mojom::MediaSessionAction::kScrubTo:

@ -33,7 +33,7 @@ interface MediaControllerManager {
// session, otherwise media sessions will be associated with a particular media
// session provided to the service when creating the media controller. If the
// media session is not controllable then the commands will be no-ops.
// Next Method ID: 18
// Next Method ID: 19
[Stable]
interface MediaController {
// Suspend the media session.
@ -109,6 +109,9 @@ interface MediaController {
// Display the source of the MediaSession (e.g. show the tab or the
// application).
[MinVersion=2] Raise@17();
// Mute or unmute the media player.
[MinVersion=3] SetMute@18(bool mute);
};
// The observer for observing media controller events. This is different to a

@ -36,6 +36,7 @@ enum MediaSessionAction {
[MinVersion=11] kToggleCamera,
[MinVersion=11] kHangUp,
[MinVersion=12] kRaise,
[MinVersion=13] kSetMute,
};
[Stable, Extensible]
@ -189,6 +190,9 @@ struct MediaSessionInfo {
// Tracks whether the camera is turned on in WebRTC sessions.
[MinVersion=11] CameraState camera_state;
// Tracks whether the media player is muted.
[MinVersion=12] bool muted;
};
// Contains debugging information about a MediaSession. This will be displayed
@ -331,4 +335,7 @@ interface MediaSession {
// Display the source of the MediaSession (e.g. show the tab or the
// application).
[MinVersion=12] Raise@21();
// Mute or unmute the media player.
[MinVersion=13] SetMute@22(bool mute);
};

@ -4644,6 +4644,10 @@ void HTMLMediaElement::RequestSeekTo(base::TimeDelta seek_time) {
setCurrentTime(seek_time.InSecondsF());
}
void HTMLMediaElement::RequestMute(bool mute) {
setMuted(mute);
}
void HTMLMediaElement::SetVolumeMultiplier(double multiplier) {
if (web_media_player_)
web_media_player_->SetVolumeMultiplier(multiplier);

@ -518,6 +518,7 @@ class CORE_EXPORT HTMLMediaElement
void RequestSeekTo(base::TimeDelta seek_time) override;
void RequestEnterPictureInPicture() override {}
void RequestExitPictureInPicture() override {}
void RequestMute(bool mute) override;
void SetVolumeMultiplier(double multiplier) override;
void SetPersistentState(bool persistent) override {}
void SetPowerExperimentState(bool enabled) override;