| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "services/media_session/media_controller.h" |
| |
| #include <set> |
| |
| #include "services/media_session/public/cpp/media_image_manager.h" |
| |
| namespace media_session { |
| |
| // ImageObserverHolder will hold each mojo image observer with the image |
| // size and type preferences it specified when the observer was added. |
| class MediaController::ImageObserverHolder { |
| public: |
| ImageObserverHolder(MediaController* owner, |
| mojom::MediaSessionImageType type, |
| int minimum_size_px, |
| int desired_size_px, |
| mojom::MediaControllerImageObserverPtr observer, |
| const std::vector<MediaImage>& current_images) |
| : manager_(minimum_size_px, desired_size_px), |
| owner_(owner), |
| type_(type), |
| minimum_size_px_(minimum_size_px), |
| desired_size_px_(desired_size_px), |
| observer_(std::move(observer)) { |
| // Set a connection error handler so that we will remove observers that have |
| // had an error / been closed. |
| observer_.set_connection_error_handler(base::BindOnce( |
| &MediaController::CleanupImageObservers, base::Unretained(owner_))); |
| |
| // Flush the observer with the latest state. |
| ImagesChanged(current_images); |
| } |
| |
| ~ImageObserverHolder() = default; |
| |
| bool is_valid() const { return !observer_.encountered_error(); } |
| |
| mojom::MediaSessionImageType type() const { return type_; } |
| |
| void ImagesChanged(const std::vector<MediaImage>& images) { |
| base::Optional<MediaImage> image = manager_.SelectImage(images); |
| |
| // If we could not find an image then we should call with an empty image to |
| // flush the observer. |
| if (!image) { |
| ClearImage(); |
| return; |
| } |
| |
| DCHECK(owner_->session_); |
| owner_->session_->GetMediaImageBitmap( |
| *image, minimum_size_px_, desired_size_px_, |
| base::BindOnce(&MediaController::ImageObserverHolder::OnImage, |
| base::Unretained(this))); |
| } |
| |
| void ClearImage() { |
| observer_->MediaControllerImageChanged(type_, SkBitmap()); |
| } |
| |
| private: |
| void OnImage(const SkBitmap& image) { |
| observer_->MediaControllerImageChanged(type_, image); |
| } |
| |
| media_session::MediaImageManager manager_; |
| |
| MediaController* const owner_; |
| |
| mojom::MediaSessionImageType const type_; |
| |
| int const minimum_size_px_; |
| |
| int const desired_size_px_; |
| |
| mojom::MediaControllerImageObserverPtr observer_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ImageObserverHolder); |
| }; |
| |
| MediaController::MediaController() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| MediaController::~MediaController() = default; |
| |
| void MediaController::Suspend() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (session_) |
| session_->Suspend(mojom::MediaSession::SuspendType::kUI); |
| } |
| |
| void MediaController::Resume() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (session_) |
| session_->Resume(mojom::MediaSession::SuspendType::kUI); |
| } |
| |
| void MediaController::Stop() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (session_) |
| session_->Stop(mojom::MediaSession::SuspendType::kUI); |
| } |
| |
| void MediaController::ToggleSuspendResume() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (session_info_.is_null()) |
| return; |
| |
| switch (session_info_->playback_state) { |
| case mojom::MediaPlaybackState::kPlaying: |
| Suspend(); |
| break; |
| case mojom::MediaPlaybackState::kPaused: |
| Resume(); |
| break; |
| } |
| } |
| |
| void MediaController::AddObserver(mojom::MediaControllerObserverPtr observer) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // Flush the new observer with the current state. |
| observer->MediaSessionChanged(request_id_); |
| observer->MediaSessionInfoChanged(session_info_.Clone()); |
| observer->MediaSessionMetadataChanged(session_metadata_); |
| observer->MediaSessionActionsChanged(session_actions_); |
| |
| observers_.AddPtr(std::move(observer)); |
| } |
| |
| void MediaController::MediaSessionInfoChanged(mojom::MediaSessionInfoPtr info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| observers_.ForAllPtrs([&info](mojom::MediaControllerObserver* observer) { |
| observer->MediaSessionInfoChanged(info.Clone()); |
| }); |
| |
| session_info_ = std::move(info); |
| } |
| |
| void MediaController::MediaSessionMetadataChanged( |
| const base::Optional<MediaMetadata>& metadata) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| observers_.ForAllPtrs([&metadata](mojom::MediaControllerObserver* observer) { |
| observer->MediaSessionMetadataChanged(metadata); |
| }); |
| |
| session_metadata_ = metadata; |
| } |
| |
| void MediaController::MediaSessionActionsChanged( |
| const std::vector<mojom::MediaSessionAction>& actions) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| observers_.ForAllPtrs([&actions](mojom::MediaControllerObserver* observer) { |
| observer->MediaSessionActionsChanged(actions); |
| }); |
| |
| session_actions_ = actions; |
| } |
| |
| void MediaController::MediaSessionImagesChanged( |
| const base::flat_map<mojom::MediaSessionImageType, std::vector<MediaImage>>& |
| images) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // Work out which image types have changed. |
| std::set<mojom::MediaSessionImageType> types_changed; |
| for (const auto& entry : images) { |
| auto it = session_images_.find(entry.first); |
| if (it != session_images_.end() && entry.second == it->second) |
| continue; |
| |
| types_changed.insert(entry.first); |
| } |
| |
| session_images_ = images; |
| |
| for (auto& holder : image_observers_) { |
| auto it = session_images_.find(holder->type()); |
| |
| if (it == session_images_.end()) { |
| // No image of this type is available from the session so we should clear |
| // any image the observers might have. |
| holder->ClearImage(); |
| } else if (base::ContainsKey(types_changed, holder->type())) { |
| holder->ImagesChanged(it->second); |
| } |
| } |
| } |
| |
| void MediaController::PreviousTrack() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (session_) |
| session_->PreviousTrack(); |
| } |
| |
| void MediaController::NextTrack() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (session_) |
| session_->NextTrack(); |
| } |
| |
| void MediaController::Seek(base::TimeDelta seek_time) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (session_) |
| session_->Seek(seek_time); |
| } |
| |
| void MediaController::ObserveImages( |
| mojom::MediaSessionImageType type, |
| int minimum_size_px, |
| int desired_size_px, |
| mojom::MediaControllerImageObserverPtr observer) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto it = session_images_.find(type); |
| |
| image_observers_.push_back(std::make_unique<ImageObserverHolder>( |
| this, type, minimum_size_px, desired_size_px, std::move(observer), |
| it == session_images_.end() ? std::vector<MediaImage>() : it->second)); |
| } |
| |
| void MediaController::SetMediaSession( |
| mojom::MediaSession* session, |
| const base::UnguessableToken& request_id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| DCHECK(session); |
| DCHECK(request_id); |
| |
| if (session_ == session) |
| return; |
| |
| Reset(); |
| |
| session_ = session; |
| request_id_ = request_id; |
| |
| // We should always notify the observers that the media session has changed. |
| observers_.ForAllPtrs( |
| [&request_id](mojom::MediaControllerObserver* observer) { |
| observer->MediaSessionChanged(request_id); |
| }); |
| |
| // Add |this| as an observer for |session|. |
| mojom::MediaSessionObserverPtr observer; |
| session_binding_.Bind(mojo::MakeRequest(&observer)); |
| session->AddObserver(std::move(observer)); |
| } |
| |
| void MediaController::ClearMediaSession() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!session_) |
| return; |
| |
| Reset(); |
| |
| // If we are no longer bound to a session we should flush the observers |
| // with empty data. |
| observers_.ForAllPtrs([](mojom::MediaControllerObserver* observer) { |
| observer->MediaSessionChanged(base::nullopt); |
| observer->MediaSessionInfoChanged(nullptr); |
| observer->MediaSessionMetadataChanged(base::nullopt); |
| observer->MediaSessionActionsChanged( |
| std::vector<mojom::MediaSessionAction>()); |
| }); |
| |
| for (auto& holder : image_observers_) |
| holder->ClearImage(); |
| } |
| |
| void MediaController::BindToInterface(mojom::MediaControllerRequest request) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| void MediaController::FlushForTesting() { |
| bindings_.FlushForTesting(); |
| } |
| |
| void MediaController::CleanupImageObservers() { |
| base::EraseIf(image_observers_, |
| [](const auto& holder) { return !holder->is_valid(); }); |
| } |
| |
| void MediaController::Reset() { |
| session_ = nullptr; |
| request_id_.reset(); |
| session_binding_.Close(); |
| session_info_.reset(); |
| session_metadata_.reset(); |
| session_actions_.clear(); |
| session_images_.clear(); |
| } |
| |
| } // namespace media_session |