Commit 3467174c authored by Noah Rose Ledesma's avatar Noah Rose Ledesma Committed by Commit Bot

GMC: Update MediaSessionInfo with audio sink id

The media session controller will now notify the media session of audio
device switching. Upon receiving this message, the media session will
update the MediaSessionInfo to include the device id and notify its
observers.

Because it is possible for a media session's players to be using
different audio devices, a device is only added to the info object if
all of the sessions players are using the same device.

Bug: 1096251
Change-Id: I280387991b333eff60141e38e119eff7444aebb1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2324406
Commit-Queue: Noah Rose Ledesma <noahrose@google.com>
Reviewed-by: default avatarBecca Hughes <beccahughes@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#795156}
parent 2ce4939f
......@@ -161,7 +161,10 @@ void MediaSessionController::OnPictureInPictureAvailabilityChanged(
}
void MediaSessionController::OnAudioOutputSinkChanged(
const std::string& raw_device_id) {}
const std::string& raw_device_id) {
audio_output_sink_id_ = raw_device_id;
media_session_->OnAudioOutputSinkIdChanged();
}
bool MediaSessionController::IsMediaSessionNeeded() const {
if (!is_playback_in_progress_)
......@@ -211,4 +214,9 @@ bool MediaSessionController::HasVideo(int player_id) const {
return has_video_ && has_audio_;
}
std::string MediaSessionController::GetAudioOutputSinkId(int player_id) const {
DCHECK_EQ(player_id_, player_id);
return audio_output_sink_id_;
}
} // namespace content
......@@ -13,6 +13,7 @@
#include "content/common/content_export.h"
#include "content/public/browser/media_player_id.h"
#include "content/public/browser/web_contents_observer.h"
#include "media/audio/audio_device_description.h"
#include "media/base/media_content_type.h"
#include "services/media_session/public/cpp/media_position.h"
......@@ -58,6 +59,7 @@ class CONTENT_EXPORT MediaSessionController
int player_id) const override;
bool IsPictureInPictureAvailable(int player_id) const override;
bool HasVideo(int player_id) const override;
std::string GetAudioOutputSinkId(int player_id) const override;
// Test helpers.
int get_player_id_for_testing() const { return player_id_; }
......@@ -103,6 +105,8 @@ class CONTENT_EXPORT MediaSessionController
bool has_audio_ = false;
bool has_video_ = false;
bool is_picture_in_picture_available_ = false;
std::string audio_output_sink_id_ =
media::AudioDeviceDescription::kDefaultDeviceId;
media::MediaContentType media_content_type_ =
media::MediaContentType::Persistent;
......
......@@ -11,6 +11,7 @@
#include "content/common/media/media_player_delegate_messages.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
#include "media/audio/audio_device_description.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
......@@ -279,6 +280,17 @@ TEST_F(MediaSessionControllerTest, PictureInPictureAvailability) {
controller_->get_player_id_for_testing()));
}
TEST_F(MediaSessionControllerTest, AudioOutputSinkIdChange) {
EXPECT_EQ(controller_->GetAudioOutputSinkId(
controller_->get_player_id_for_testing()),
media::AudioDeviceDescription::kDefaultDeviceId);
controller_->OnAudioOutputSinkChanged("1");
EXPECT_EQ(controller_->GetAudioOutputSinkId(
controller_->get_player_id_for_testing()),
"1");
}
TEST_F(MediaSessionControllerTest, AddPlayerWhenUnmuted) {
contents()->SetAudioMuted(true);
......
......@@ -958,6 +958,12 @@ MediaSessionImpl::GetMediaSessionInfoSync() {
: media_session::mojom::MediaPictureInPictureState::
kNotInPictureInPicture;
auto shared_audio_device_id = GetSharedAudioOutputDeviceId();
// When the default audio device is in use, or this session's players are
// using different devices, the |audio_sink_id| attribute should remain unset.
if (shared_audio_device_id != media::AudioDeviceDescription::kDefaultDeviceId)
info->audio_sink_id = shared_audio_device_id;
return info;
}
......@@ -1359,6 +1365,10 @@ void MediaSessionImpl::OnPictureInPictureAvailabilityChanged() {
RebuildAndNotifyActionsChanged();
}
void MediaSessionImpl::OnAudioOutputSinkIdChanged() {
RebuildAndNotifyMediaSessionInfoChanged();
}
bool MediaSessionImpl::ShouldRouteAction(
media_session::mojom::MediaSessionAction action) const {
return routed_service_ && base::Contains(routed_service_->actions(), action);
......@@ -1479,6 +1489,23 @@ bool MediaSessionImpl::IsPictureInPictureAvailable() const {
return first.observer->IsPictureInPictureAvailable(first.player_id);
}
std::string MediaSessionImpl::GetSharedAudioOutputDeviceId() const {
if (normal_players_.empty())
return media::AudioDeviceDescription::kDefaultDeviceId;
auto& first = normal_players_.begin()->first;
const auto& first_id = first.observer->GetAudioOutputSinkId(first.player_id);
if (std::all_of(normal_players_.cbegin(), normal_players_.cend(),
[&first_id](const auto& player) {
return player.first.observer->GetAudioOutputSinkId(
player.first.player_id) == first_id;
})) {
return first_id;
}
return media::AudioDeviceDescription::kDefaultDeviceId;
}
MediaAudioVideoState MediaSessionImpl::GetMediaAudioVideoState() {
RenderFrameHost* routed_rfh =
routed_service_ ? routed_service_->GetRenderFrameHost() : nullptr;
......
......@@ -272,6 +272,10 @@ class MediaSessionImpl : public MediaSession,
void OnPictureInPictureAvailabilityChanged();
// Called when any of the normal players have switched to a different audio
// output device.
void OnAudioOutputSinkIdChanged();
// Returns whether the action should be routed to |routed_service_|.
bool ShouldRouteAction(media_session::mojom::MediaSessionAction action) const;
......@@ -388,6 +392,11 @@ class MediaSessionImpl : public MediaSession,
bool IsPictureInPictureAvailable() const;
// Returns the device ID for the audio output device being used by all of the
// normal players. If the players are not all using the same audio output
// device, the id of the default device will be returned.
std::string GetSharedAudioOutputDeviceId() 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,
......
......@@ -2606,7 +2606,7 @@ IN_PROC_BROWSER_TEST_F(MediaSessionImplBrowserTest,
UISetAudioSink(kExampleSinkId);
EXPECT_EQ(player_observer->received_set_audio_sink_id_calls(), 1);
EXPECT_EQ(player_observer->GetAudioSinkId(0), kExampleSinkId);
EXPECT_EQ(player_observer->GetAudioOutputSinkId(0), kExampleSinkId);
}
class MediaSessionFaviconBrowserTest : public ContentBrowserTest {
......
......@@ -72,6 +72,8 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
bool HasVideo(int player_id) const override { return has_video_; }
std::string GetAudioOutputSinkId(int player_id) const override { return ""; }
RenderFrameHost* render_frame_host() const override {
return render_frame_host_;
}
......
......@@ -55,6 +55,8 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
bool HasVideo(int player_id) const override { return false; }
std::string GetAudioOutputSinkId(int player_id) const override { return ""; }
RenderFrameHost* render_frame_host() const override {
return render_frame_host_;
}
......
......@@ -30,9 +30,9 @@ using ::testing::_;
namespace content {
using media_session::mojom::AudioFocusType;
using media_session::mojom::MediaPlaybackState;
using media_session::mojom::MediaSessionInfo;
using media_session::mojom::MediaSessionInfoPtr;
using media_session::mojom::MediaPlaybackState;
using media_session::test::MockMediaSessionMojoObserver;
using media_session::test::TestAudioFocusObserver;
......@@ -549,7 +549,6 @@ TEST_F(MediaSessionImplTest, WebContentsDestroyed_StopsDucking) {
observer->WaitForGainedEvent();
}
{
MockMediaSessionMojoObserver observer(*media_session_1);
observer.WaitForState(MediaSessionInfo::SessionState::kDucking);
......@@ -695,4 +694,30 @@ TEST_F(MediaSessionImplTest, SessionInfoPictureInPicture) {
media_session::mojom::MediaPictureInPictureState::kNotInPictureInPicture);
}
TEST_F(MediaSessionImplTest, SessionInfoAudioSink) {
// When the session is created it should be using the default audio device.
// When the default audio device is in use, the |audio_sink_id| attribute
// should be unset.
EXPECT_FALSE(media_session::test::GetMediaSessionInfoSync(GetMediaSession())
->audio_sink_id.has_value());
int player1 = player_observer_->StartNewPlayer();
int player2 = player_observer_->StartNewPlayer();
GetMediaSession()->AddPlayer(player_observer_.get(), player1,
media::MediaContentType::Persistent);
GetMediaSession()->AddPlayer(player_observer_.get(), player2,
media::MediaContentType::Persistent);
player_observer_->SetAudioSinkId(player1, "1");
player_observer_->SetAudioSinkId(player2, "1");
auto info = media_session::test::GetMediaSessionInfoSync(GetMediaSession());
ASSERT_TRUE(info->audio_sink_id.has_value());
EXPECT_EQ(info->audio_sink_id.value(), "1");
// If multiple audio devices are being used the audio sink id attribute should
// be unset.
player_observer_->SetAudioSinkId(player2, "2");
info = media_session::test::GetMediaSessionInfoSync(GetMediaSession());
EXPECT_FALSE(info->audio_sink_id.has_value());
}
} // namespace content
......@@ -59,6 +59,10 @@ class MediaSessionPlayerObserver {
// Returns true if the |player_id| has video tracks.
virtual bool HasVideo(int player_id) const = 0;
// Returns the id of the audio output device used by |player_id|. Returns the
// empty string if unavailable.
virtual std::string GetAudioOutputSinkId(int player_id) const = 0;
// Returns the RenderFrameHost this player observer belongs to. Returns
// nullptr if unavailable.
virtual RenderFrameHost* render_frame_host() const = 0;
......
......@@ -68,6 +68,8 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
bool HasVideo(int player_id) const override { return false; }
std::string GetAudioOutputSinkId(int player_id) const override { return ""; }
RenderFrameHost* render_frame_host() const override {
return render_frame_host_;
}
......
......@@ -115,10 +115,10 @@ double MockMediaSessionPlayerObserver::GetVolumeMultiplier(size_t player_id) {
return players_[player_id].volume_multiplier_;
}
const std::string& MockMediaSessionPlayerObserver::GetAudioSinkId(
size_t player_id) {
void MockMediaSessionPlayerObserver::SetAudioSinkId(size_t player_id,
std::string sink_id) {
EXPECT_GT(players_.size(), player_id);
return players_[player_id].audio_sink_id_;
players_[player_id].audio_sink_id_ = std::move(sink_id);
}
void MockMediaSessionPlayerObserver::SetPlaying(size_t player_id,
......@@ -170,13 +170,16 @@ bool MockMediaSessionPlayerObserver::HasVideo(int player_id) const {
return false;
}
MockMediaSessionPlayerObserver::MockPlayer::MockPlayer(
bool is_playing,
double volume_multiplier,
const std::string& audio_sink_id)
: is_playing_(is_playing),
volume_multiplier_(volume_multiplier),
audio_sink_id_(audio_sink_id) {}
std::string MockMediaSessionPlayerObserver::GetAudioOutputSinkId(
int player_id) const {
EXPECT_GE(player_id, 0);
EXPECT_GT(players_.size(), static_cast<size_t>(player_id));
return players_.at(player_id).audio_sink_id_;
}
MockMediaSessionPlayerObserver::MockPlayer::MockPlayer(bool is_playing,
double volume_multiplier)
: is_playing_(is_playing), volume_multiplier_(volume_multiplier) {}
MockMediaSessionPlayerObserver::MockPlayer::~MockPlayer() = default;
......
......@@ -10,6 +10,7 @@
#include "base/time/time.h"
#include "content/browser/media/session/media_session_player_observer.h"
#include "media/audio/audio_device_description.h"
#include "services/media_session/public/cpp/media_position.h"
namespace content {
......@@ -37,6 +38,7 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
bool IsPictureInPictureAvailable(int player_id) const override;
RenderFrameHost* render_frame_host() const override;
bool HasVideo(int player_id) const override;
std::string GetAudioOutputSinkId(int player_id) const override;
// Simulate that a new player started.
// Returns the player_id.
......@@ -48,8 +50,8 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
// Returns the volume multiplier of |player_id|.
double GetVolumeMultiplier(size_t player_id);
// Returns the sink id being used for the audio output of |player_id|
const std::string& GetAudioSinkId(size_t player_id);
// Changes the audio output sink id of |player_id|.
void SetAudioSinkId(size_t player_id, std::string sink_id);
// Simulate a play state change for |player_id|.
void SetPlaying(size_t player_id, bool playing);
......@@ -70,8 +72,7 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
struct MockPlayer {
public:
explicit MockPlayer(bool is_playing = true,
double volume_multiplier = 1.0f,
const std::string& audio_sink_id = "");
double volume_multiplier = 1.0f);
~MockPlayer();
MockPlayer(const MockPlayer&);
......@@ -79,7 +80,8 @@ class MockMediaSessionPlayerObserver : public MediaSessionPlayerObserver {
double volume_multiplier_;
base::Optional<media_session::MediaPosition> position_;
bool is_in_picture_in_picture_;
std::string audio_sink_id_;
std::string audio_sink_id_ =
media::AudioDeviceDescription::kDefaultDeviceId;
};
// Basic representation of the players. The position in the vector is the
......
......@@ -21,10 +21,9 @@ const double kDuckVolume = 0.2f;
const int PepperPlayerDelegate::kPlayerId = 0;
PepperPlayerDelegate::PepperPlayerDelegate(
RenderFrameHost* render_frame_host, int32_t pp_instance)
: render_frame_host_(render_frame_host),
pp_instance_(pp_instance) {}
PepperPlayerDelegate::PepperPlayerDelegate(RenderFrameHost* render_frame_host,
int32_t pp_instance)
: render_frame_host_(render_frame_host), pp_instance_(pp_instance) {}
PepperPlayerDelegate::~PepperPlayerDelegate() = default;
......@@ -104,4 +103,9 @@ bool PepperPlayerDelegate::HasVideo(int player_id) const {
return true;
}
std::string PepperPlayerDelegate::GetAudioOutputSinkId(int player_id) const {
// This operation is not supported for pepper players.
return "";
}
} // namespace content
......@@ -28,8 +28,7 @@ class PepperPlayerDelegate : public MediaSessionPlayerObserver {
void OnResume(int player_id) override;
void OnSeekForward(int player_id, base::TimeDelta seek_time) override;
void OnSeekBackward(int player_id, base::TimeDelta seek_time) override;
void OnSetVolumeMultiplier(int player_id,
double volume_multiplier) override;
void OnSetVolumeMultiplier(int player_id, double volume_multiplier) override;
void OnEnterPictureInPicture(int player_id) override;
void OnExitPictureInPicture(int player_id) override;
void OnSetAudioSinkId(int player_id,
......@@ -39,6 +38,7 @@ class PepperPlayerDelegate : public MediaSessionPlayerObserver {
bool IsPictureInPictureAvailable(int player_id) const override;
RenderFrameHost* render_frame_host() const override;
bool HasVideo(int player_id) const override;
std::string GetAudioOutputSinkId(int player_id) const override;
private:
void SetVolume(int player_id, double volume);
......
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