1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "services/media_session/media_controller.h"
9 #include "base/containers/contains.h"
10 #include "base/containers/cxx20_erase.h"
11 #include "base/memory/raw_ptr.h"
12 #include "base/memory/weak_ptr.h"
13 #include "mojo/public/cpp/bindings/remote.h"
14 #include "services/media_session/audio_focus_request.h"
15 #include "services/media_session/public/cpp/media_image_manager.h"
17 namespace media_session {
19 // ImageObserverHolder will hold each mojo image observer with the image
20 // size and type preferences it specified when the observer was added.
21 class MediaController::ImageObserverHolder {
24 MediaController* owner,
25 mojom::MediaSessionImageType type,
28 mojo::PendingRemote<mojom::MediaControllerImageObserver> observer,
29 const std::vector<MediaImage>& current_images)
30 : manager_(minimum_size_px, desired_size_px),
33 minimum_size_px_(minimum_size_px),
34 desired_size_px_(desired_size_px),
35 observer_(std::move(observer)) {
36 // Set a connection error handler so that we will remove observers that have
37 // had an error / been closed.
38 observer_.set_disconnect_handler(base::BindOnce(
39 &MediaController::CleanupImageObservers, base::Unretained(owner_)));
41 // Flush the observer with the latest state.
42 ImagesChanged(current_images);
45 ImageObserverHolder(const ImageObserverHolder&) = delete;
46 ImageObserverHolder& operator=(const ImageObserverHolder&) = delete;
48 ~ImageObserverHolder() = default;
50 bool is_valid() const { return observer_.is_connected(); }
52 mojom::MediaSessionImageType type() const { return type_; }
54 void ImagesChanged(const std::vector<MediaImage>& images) {
55 absl::optional<MediaImage> image = manager_.SelectImage(images);
57 // If we could not find an image then we should call with an empty image to
58 // flush the observer.
64 DCHECK(owner_->session_->ipc());
65 owner_->session_->GetMediaImageBitmap(
66 *image, minimum_size_px_, desired_size_px_,
67 base::BindOnce(&MediaController::ImageObserverHolder::OnImage,
68 weak_ptr_factory_.GetWeakPtr()));
72 // If the last thing we sent was a ClearImage, don't send another one. If we
73 // haven't sent anything before, then send a ClearImage.
74 if (!did_send_image_last_.value_or(true))
76 did_send_image_last_ = false;
77 observer_->MediaControllerImageChanged(type_, SkBitmap());
81 void OnImage(const SkBitmap& image) {
82 did_send_image_last_ = true;
83 observer_->MediaControllerImageChanged(type_, image);
86 media_session::MediaImageManager manager_;
88 const raw_ptr<MediaController> owner_;
90 mojom::MediaSessionImageType const type_;
92 int const minimum_size_px_;
94 int const desired_size_px_;
96 mojo::Remote<mojom::MediaControllerImageObserver> observer_;
98 // Whether the last information sent to the observer was an image.
99 // Empty if we have not yet sent anything.
100 absl::optional<bool> did_send_image_last_;
102 base::WeakPtrFactory<ImageObserverHolder> weak_ptr_factory_{this};
105 MediaController::MediaController() {
106 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
109 MediaController::~MediaController() = default;
111 void MediaController::Suspend() {
112 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
115 session_->PerformUIAction(mojom::MediaSessionAction::kPause);
118 void MediaController::Resume() {
119 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
122 session_->PerformUIAction(mojom::MediaSessionAction::kPlay);
125 void MediaController::Stop() {
126 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
129 session_->PerformUIAction(mojom::MediaSessionAction::kStop);
132 void MediaController::ToggleSuspendResume() {
133 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
135 if (session_info_.is_null())
138 switch (session_info_->playback_state) {
139 case mojom::MediaPlaybackState::kPlaying:
142 case mojom::MediaPlaybackState::kPaused:
148 void MediaController::AddObserver(
149 mojo::PendingRemote<mojom::MediaControllerObserver> observer) {
150 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
152 mojo::Remote<mojom::MediaControllerObserver> media_controller_observer(
153 std::move(observer));
155 media_controller_observer->MediaSessionChanged(session_->id());
157 media_controller_observer->MediaSessionChanged(absl::nullopt);
160 // Flush the new observer with the current state.
161 media_controller_observer->MediaSessionInfoChanged(session_info_.Clone());
162 media_controller_observer->MediaSessionMetadataChanged(session_metadata_);
163 media_controller_observer->MediaSessionActionsChanged(session_actions_);
164 media_controller_observer->MediaSessionPositionChanged(session_position_);
166 observers_.Add(std::move(media_controller_observer));
169 void MediaController::MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) {
170 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
172 for (auto& observer : observers_)
173 observer->MediaSessionInfoChanged(info.Clone());
175 session_info_ = std::move(info);
178 void MediaController::MediaSessionMetadataChanged(
179 const absl::optional<MediaMetadata>& metadata) {
180 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
182 for (auto& observer : observers_)
183 observer->MediaSessionMetadataChanged(metadata);
185 session_metadata_ = metadata;
188 void MediaController::MediaSessionActionsChanged(
189 const std::vector<mojom::MediaSessionAction>& actions) {
190 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
192 for (auto& observer : observers_)
193 observer->MediaSessionActionsChanged(actions);
195 session_actions_ = actions;
198 void MediaController::MediaSessionPositionChanged(
199 const absl::optional<media_session::MediaPosition>& position) {
200 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
202 for (auto& observer : observers_)
203 observer->MediaSessionPositionChanged(position);
205 session_position_ = position;
208 void MediaController::MediaSessionImagesChanged(
209 const base::flat_map<mojom::MediaSessionImageType, std::vector<MediaImage>>&
211 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
213 // Work out which image types have changed.
214 std::set<mojom::MediaSessionImageType> types_changed;
215 for (const auto& entry : images) {
216 auto it = session_images_.find(entry.first);
217 if (it != session_images_.end() && entry.second == it->second)
220 types_changed.insert(entry.first);
223 session_images_ = images;
225 for (auto& holder : image_observers_) {
226 auto it = session_images_.find(holder->type());
228 if (it == session_images_.end()) {
229 // No image of this type is available from the session so we should clear
230 // any image the observers might have.
231 holder->ClearImage();
232 } else if (base::Contains(types_changed, holder->type())) {
233 holder->ImagesChanged(it->second);
238 void MediaController::PreviousTrack() {
239 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
242 session_->ipc()->PreviousTrack();
245 void MediaController::NextTrack() {
246 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
249 session_->ipc()->NextTrack();
252 void MediaController::Seek(base::TimeDelta seek_time) {
253 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
256 session_->ipc()->Seek(seek_time);
259 void MediaController::ObserveImages(
260 mojom::MediaSessionImageType type,
263 mojo::PendingRemote<mojom::MediaControllerImageObserver> observer) {
264 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
266 auto it = session_images_.find(type);
268 image_observers_.push_back(std::make_unique<ImageObserverHolder>(
269 this, type, minimum_size_px, desired_size_px, std::move(observer),
270 it == session_images_.end() ? std::vector<MediaImage>() : it->second));
273 void MediaController::SeekTo(base::TimeDelta seek_time) {
274 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
277 session_->ipc()->SeekTo(seek_time);
280 void MediaController::ScrubTo(base::TimeDelta seek_time) {
281 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
284 session_->ipc()->ScrubTo(seek_time);
287 void MediaController::EnterPictureInPicture() {
288 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
291 session_->ipc()->EnterPictureInPicture();
294 void MediaController::ExitPictureInPicture() {
295 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
298 session_->ipc()->ExitPictureInPicture();
301 void MediaController::SetAudioSinkId(const absl::optional<std::string>& id) {
302 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
305 session_->ipc()->SetAudioSinkId(id);
308 void MediaController::ToggleMicrophone() {
309 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
312 session_->ipc()->ToggleMicrophone();
315 void MediaController::ToggleCamera() {
316 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
319 session_->ipc()->ToggleCamera();
322 void MediaController::HangUp() {
323 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
326 session_->ipc()->HangUp();
329 void MediaController::Raise() {
330 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
333 session_->ipc()->Raise();
336 void MediaController::SetMute(bool mute) {
337 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
340 session_->ipc()->SetMute(mute);
343 void MediaController::RequestMediaRemoting() {
344 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
347 session_->ipc()->RequestMediaRemoting();
350 void MediaController::EnterAutoPictureInPicture() {
351 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
354 session_->ipc()->EnterAutoPictureInPicture();
358 void MediaController::SetMediaSession(AudioFocusRequest* session) {
359 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
363 if (session_ == session)
370 // We should always notify the observers that the media session has changed.
371 for (auto& observer : observers_)
372 observer->MediaSessionChanged(session->id());
374 // Add |this| as an observer for |session|.
375 session->ipc()->AddObserver(session_receiver_.BindNewPipeAndPassRemote());
378 void MediaController::ClearMediaSession() {
379 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
386 // If we are no longer bound to a session we should flush the observers
388 for (auto& observer : observers_) {
389 observer->MediaSessionChanged(absl::nullopt);
390 observer->MediaSessionInfoChanged(nullptr);
391 observer->MediaSessionMetadataChanged(absl::nullopt);
392 observer->MediaSessionActionsChanged(
393 std::vector<mojom::MediaSessionAction>());
394 observer->MediaSessionPositionChanged(absl::nullopt);
397 for (auto& holder : image_observers_)
398 holder->ClearImage();
401 void MediaController::BindToInterface(
402 mojo::PendingReceiver<mojom::MediaController> receiver) {
403 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
404 receivers_.Add(this, std::move(receiver));
407 void MediaController::FlushForTesting() {
408 receivers_.FlushForTesting();
411 void MediaController::CleanupImageObservers() {
412 base::EraseIf(image_observers_,
413 [](const auto& holder) { return !holder->is_valid(); });
416 void MediaController::Reset() {
418 session_receiver_.reset();
419 session_info_.reset();
420 session_metadata_.reset();
421 session_actions_.clear();
422 session_images_.clear();
423 session_position_.reset();
426 } // namespace media_session