Commit 442effe1 authored by Noah Rose Ledesma's avatar Noah Rose Ledesma Committed by Commit Bot

Add a MediaSessionAction for audio output device switching.

This change adds an action to the MediaSessionAction enum for signaling
if audio output device switching is possible for that session. A session
supports switching audio devices if and only if the session has at
least one player and all of the players support audio device switching.

When the audio output device cannot be changed, the Global Media
Controls UI should not display a device picker. This change is a part
of a series of changes for appropriately hiding this UI.

Bug: 1120620
Change-Id: If03ef110ff32fa0db3463c63a6a1730da60d2278
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2369440
Commit-Queue: Noah Rose Ledesma <noahrose@google.com>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarFabrice de Gans-Riberi <fdegans@chromium.org>
Reviewed-by: default avatarNasko Oskov <nasko@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Reviewed-by: default avatarBecca Hughes <beccahughes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#802666}
parent 303d7c60
......@@ -128,6 +128,7 @@ const gfx::VectorIcon& GetVectorIconForMediaAction(MediaSessionAction action) {
case MediaSessionAction::kScrubTo:
case MediaSessionAction::kEnterPictureInPicture:
case MediaSessionAction::kExitPictureInPicture:
case MediaSessionAction::kSwitchAudioDevice:
NOTREACHED();
break;
}
......
......@@ -63,6 +63,7 @@ void CastMediaSessionController::Send(
case media_session::mojom::MediaSessionAction::kScrubTo:
case media_session::mojom::MediaSessionAction::kEnterPictureInPicture:
case media_session::mojom::MediaSessionAction::kExitPictureInPicture:
case media_session::mojom::MediaSessionAction::kSwitchAudioDevice:
NOTREACHED();
return;
}
......
......@@ -81,6 +81,7 @@ const gfx::VectorIcon* GetVectorIconForMediaAction(MediaSessionAction action) {
case MediaSessionAction::kSkipAd:
case MediaSessionAction::kSeekTo:
case MediaSessionAction::kScrubTo:
case MediaSessionAction::kSwitchAudioDevice:
NOTREACHED();
break;
}
......
......@@ -126,13 +126,11 @@ void HardwareKeyMediaController::PerformAction(MediaSessionAction action) {
return;
case MediaSessionAction::kPause:
media_controller_remote_->Suspend();
ui::RecordMediaHardwareKeyAction(
ui::MediaHardwareKeyAction::kPause);
ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPause);
return;
case MediaSessionAction::kNextTrack:
media_controller_remote_->NextTrack();
ui::RecordMediaHardwareKeyAction(
ui::MediaHardwareKeyAction::kNextTrack);
ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kNextTrack);
return;
case MediaSessionAction::kStop:
media_controller_remote_->Stop();
......@@ -145,6 +143,7 @@ void HardwareKeyMediaController::PerformAction(MediaSessionAction action) {
case MediaSessionAction::kScrubTo:
case MediaSessionAction::kEnterPictureInPicture:
case MediaSessionAction::kExitPictureInPicture:
case MediaSessionAction::kSwitchAudioDevice:
NOTREACHED();
return;
}
......@@ -192,6 +191,7 @@ HardwareKeyMediaController::MediaSessionActionToKeyCode(
case MediaSessionAction::kScrubTo:
case MediaSessionAction::kEnterPictureInPicture:
case MediaSessionAction::kExitPictureInPicture:
case MediaSessionAction::kSwitchAudioDevice:
return base::nullopt;
}
}
......
......@@ -166,7 +166,10 @@ void MediaSessionController::OnAudioOutputSinkChanged(
media_session_->OnAudioOutputSinkIdChanged();
}
void MediaSessionController::OnAudioOutputSinkChangingDisabled() {}
void MediaSessionController::OnAudioOutputSinkChangingDisabled() {
supports_audio_output_device_switching_ = false;
media_session_->OnAudioOutputSinkChangingDisabled();
}
bool MediaSessionController::IsMediaSessionNeeded() const {
if (!is_playback_in_progress_)
......@@ -221,4 +224,10 @@ std::string MediaSessionController::GetAudioOutputSinkId(int player_id) const {
return audio_output_sink_id_;
}
bool MediaSessionController::SupportsAudioOutputDeviceSwitching(
int player_id) const {
DCHECK_EQ(player_id_, player_id);
return supports_audio_output_device_switching_;
}
} // namespace content
......@@ -60,6 +60,7 @@ class CONTENT_EXPORT MediaSessionController
bool IsPictureInPictureAvailable(int player_id) const override;
bool HasVideo(int player_id) const override;
std::string GetAudioOutputSinkId(int player_id) const override;
bool SupportsAudioOutputDeviceSwitching(int player_id) const override;
// Test helpers.
int get_player_id_for_testing() const { return player_id_; }
......@@ -110,6 +111,7 @@ class CONTENT_EXPORT MediaSessionController
bool is_picture_in_picture_available_ = false;
std::string audio_output_sink_id_ =
media::AudioDeviceDescription::kDefaultDeviceId;
bool supports_audio_output_device_switching_ = true;
media::MediaContentType media_content_type_ =
media::MediaContentType::Persistent;
......
......@@ -12,6 +12,7 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/timer/timer.h"
#include "base/util/ranges/algorithm.h"
#include "build/build_config.h"
#include "components/url_formatter/url_formatter.h"
#include "content/browser/media/session/audio_focus_delegate.h"
......@@ -135,6 +136,8 @@ MediaSessionUserAction MediaSessionActionToUserAction(
return MediaSessionUserAction::EnterPictureInPicture;
case media_session::mojom::MediaSessionAction::kExitPictureInPicture:
return MediaSessionUserAction::ExitPictureInPicture;
case media_session::mojom::MediaSessionAction::kSwitchAudioDevice:
return MediaSessionUserAction::SwitchAudioDevice;
}
NOTREACHED();
return MediaSessionUserAction::Play;
......@@ -1385,6 +1388,10 @@ void MediaSessionImpl::OnAudioOutputSinkIdChanged() {
RebuildAndNotifyMediaSessionInfoChanged();
}
void MediaSessionImpl::OnAudioOutputSinkChangingDisabled() {
RebuildAndNotifyMediaSessionInfoChanged();
}
bool MediaSessionImpl::ShouldRouteAction(
media_session::mojom::MediaSessionAction action) const {
return routed_service_ && base::Contains(routed_service_->actions(), action);
......@@ -1429,6 +1436,13 @@ void MediaSessionImpl::RebuildAndNotifyActionsChanged() {
media_session::mojom::MediaSessionAction::kExitPictureInPicture);
}
if (base::FeatureList::IsEnabled(
media::kGlobalMediaControlsSeamlessTransfer) &&
IsAudioOutputDeviceSwitchingSupported()) {
actions.insert(
media_session::mojom::MediaSessionAction::kSwitchAudioDevice);
}
// If we support kSeekTo then we support kScrubTo as well.
if (base::Contains(actions,
media_session::mojom::MediaSessionAction::kSeekTo)) {
......@@ -1522,6 +1536,16 @@ std::string MediaSessionImpl::GetSharedAudioOutputDeviceId() const {
return media::AudioDeviceDescription::kDefaultDeviceId;
}
bool MediaSessionImpl::IsAudioOutputDeviceSwitchingSupported() const {
if (normal_players_.empty())
return false;
return util::ranges::all_of(normal_players_, [](const auto& player) {
return player.first.observer->SupportsAudioOutputDeviceSwitching(
player.first.player_id);
});
}
MediaAudioVideoState MediaSessionImpl::GetMediaAudioVideoState() {
RenderFrameHost* routed_rfh =
routed_service_ ? routed_service_->GetRenderFrameHost() : nullptr;
......
......@@ -280,6 +280,10 @@ class MediaSessionImpl : public MediaSession,
// output device.
void OnAudioOutputSinkIdChanged();
// Called when any of the normal players can no longer support audio output
// device switching.
void OnAudioOutputSinkChangingDisabled();
// Returns whether the action should be routed to |routed_service_|.
bool ShouldRouteAction(media_session::mojom::MediaSessionAction action) const;
......@@ -401,6 +405,8 @@ class MediaSessionImpl : public MediaSession,
// device, the id of the default device will be returned.
std::string GetSharedAudioOutputDeviceId() const;
bool IsAudioOutputDeviceSwitchingSupported() const;
// Called when a MediaSessionAction is received. The action will be forwarded
// to blink::MediaSession corresponding to the current routed service.
void DidReceiveAction(media_session::mojom::MediaSessionAction action,
......
......@@ -74,6 +74,10 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
std::string GetAudioOutputSinkId(int player_id) const override { return ""; }
bool SupportsAudioOutputDeviceSwitching(int player_id) const override {
return false;
}
RenderFrameHost* render_frame_host() const override {
return render_frame_host_;
}
......
......@@ -57,6 +57,10 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
std::string GetAudioOutputSinkId(int player_id) const override { return ""; }
bool SupportsAudioOutputDeviceSwitching(int player_id) const override {
return false;
}
RenderFrameHost* render_frame_host() const override {
return render_frame_host_;
}
......
......@@ -63,6 +63,9 @@ class MediaSessionPlayerObserver {
// empty string if unavailable.
virtual std::string GetAudioOutputSinkId(int player_id) const = 0;
// Returns true if the |player_id| supports audio output device switching.
virtual bool SupportsAudioOutputDeviceSwitching(int player_id) const = 0;
// Returns the RenderFrameHost this player observer belongs to. Returns
// nullptr if unavailable.
virtual RenderFrameHost* render_frame_host() const = 0;
......
......@@ -70,6 +70,10 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
std::string GetAudioOutputSinkId(int player_id) const override { return ""; }
bool SupportsAudioOutputDeviceSwitching(int player_id) const override {
return false;
}
RenderFrameHost* render_frame_host() const override {
return render_frame_host_;
}
......
......@@ -12,7 +12,7 @@
namespace base {
class TickClock;
} // base namespace
} // namespace base
namespace content {
......@@ -47,7 +47,8 @@ class CONTENT_EXPORT MediaSessionUmaHelper {
ScrubTo = 12,
EnterPictureInPicture = 13,
ExitPictureInPicture = 14,
kMaxValue = ExitPictureInPicture,
SwitchAudioDevice = 15,
kMaxValue = SwitchAudioDevice,
};
MediaSessionUmaHelper();
......@@ -75,4 +76,4 @@ class CONTENT_EXPORT MediaSessionUmaHelper {
} // namespace content
#endif // CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_UMA_HELPER_H_
#endif // CONTENT_BROWSER_MEDIA_SESSION_MEDIA_SESSION_UMA_HELPER_H_
......@@ -177,6 +177,13 @@ std::string MockMediaSessionPlayerObserver::GetAudioOutputSinkId(
return players_.at(player_id).audio_sink_id_;
}
bool MockMediaSessionPlayerObserver::SupportsAudioOutputDeviceSwitching(
int player_id) const {
EXPECT_GE(player_id, 0);
EXPECT_GT(players_.size(), static_cast<size_t>(player_id));
return players_.at(player_id).supports_device_switching_;
}
MockMediaSessionPlayerObserver::MockPlayer::MockPlayer(bool is_playing,
double volume_multiplier)
: is_playing_(is_playing), volume_multiplier_(volume_multiplier) {}
......
......@@ -39,6 +39,7 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
RenderFrameHost* render_frame_host() const override;
bool HasVideo(int player_id) const override;
std::string GetAudioOutputSinkId(int player_id) const override;
bool SupportsAudioOutputDeviceSwitching(int player_id) const override;
// Simulate that a new player started.
// Returns the player_id.
......@@ -82,6 +83,7 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
bool is_in_picture_in_picture_;
std::string audio_sink_id_ =
media::AudioDeviceDescription::kDefaultDeviceId;
bool supports_device_switching_ = true;
};
// Basic representation of the players. The position in the vector is the
......
......@@ -108,4 +108,9 @@ std::string PepperPlayerDelegate::GetAudioOutputSinkId(int player_id) const {
return "";
}
bool PepperPlayerDelegate::SupportsAudioOutputDeviceSwitching(
int player_id) const {
return false;
}
} // namespace content
......@@ -39,6 +39,7 @@ class PepperPlayerDelegate : public MediaSessionPlayerObserver {
RenderFrameHost* render_frame_host() const override;
bool HasVideo(int player_id) const override;
std::string GetAudioOutputSinkId(int player_id) const override;
bool SupportsAudioOutputDeviceSwitching(int player_id) const override;
private:
void SetVolume(int player_id, double volume);
......
......@@ -44,6 +44,8 @@ fuchsia::media::sessions2::PlayerCapabilityFlags ActionToCapabilityFlag(
return {}; // PlayerControl does not support skipping ads.
case MediaSessionAction::kStop:
return {}; // PlayerControl assumes that stop is always supported.
case MediaSessionAction::kSwitchAudioDevice:
return {}; // PlayerControl does not support switching audio device.
}
}
......
......@@ -49,6 +49,7 @@ void PerformMediaSessionAction(
case mojom::MediaSessionAction::kSkipAd:
case mojom::MediaSessionAction::kSeekTo:
case mojom::MediaSessionAction::kScrubTo:
case mojom::MediaSessionAction::kSwitchAudioDevice:
break;
}
}
......
......@@ -31,6 +31,7 @@ enum MediaSessionAction {
kScrubTo,
kEnterPictureInPicture,
kExitPictureInPicture,
kSwitchAudioDevice,
};
[Extensible]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment