media_player_impl.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "fuchsia_web/webengine/browser/media_player_impl.h"
  5. #include <fuchsia/media/cpp/fidl.h>
  6. #include "base/fuchsia/fuchsia_logging.h"
  7. #include "base/logging.h"
  8. #include "base/strings/string_piece.h"
  9. #include "base/strings/utf_string_conversions.h"
  10. #include "base/time/time.h"
  11. #include "content/public/browser/media_session.h"
  12. #include "services/media_session/public/mojom/constants.mojom.h"
  13. namespace {
  14. fuchsia::media::sessions2::PlayerCapabilityFlags ActionToCapabilityFlag(
  15. media_session::mojom::MediaSessionAction action) {
  16. using MediaSessionAction = media_session::mojom::MediaSessionAction;
  17. using PlayerCapabilityFlags =
  18. fuchsia::media::sessions2::PlayerCapabilityFlags;
  19. switch (action) {
  20. case MediaSessionAction::kEnterPictureInPicture:
  21. case MediaSessionAction::kExitPictureInPicture:
  22. return {}; // PlayerControl does not support picture-in-picture.
  23. case MediaSessionAction::kPlay:
  24. return PlayerCapabilityFlags::PLAY;
  25. case MediaSessionAction::kPause:
  26. return PlayerCapabilityFlags::PAUSE;
  27. case MediaSessionAction::kPreviousTrack:
  28. return PlayerCapabilityFlags::CHANGE_TO_PREV_ITEM;
  29. case MediaSessionAction::kNextTrack:
  30. return PlayerCapabilityFlags::CHANGE_TO_NEXT_ITEM;
  31. case MediaSessionAction::kSeekBackward:
  32. return PlayerCapabilityFlags::SKIP_REVERSE;
  33. case MediaSessionAction::kSeekForward:
  34. return PlayerCapabilityFlags::SKIP_FORWARD;
  35. case MediaSessionAction::kSeekTo:
  36. return PlayerCapabilityFlags::SEEK;
  37. case MediaSessionAction::kScrubTo:
  38. return {}; // PlayerControl does not support scrub-to.
  39. case MediaSessionAction::kSkipAd:
  40. return {}; // PlayerControl does not support skipping ads.
  41. case MediaSessionAction::kStop:
  42. return {}; // PlayerControl assumes that stop is always supported.
  43. case MediaSessionAction::kSwitchAudioDevice:
  44. return {}; // PlayerControl does not support switching audio device.
  45. case MediaSessionAction::kToggleMicrophone:
  46. return {}; // PlayerControl does not support toggling microphone.
  47. case MediaSessionAction::kToggleCamera:
  48. return {}; // PlayerControl does not support toggling camera.
  49. case MediaSessionAction::kHangUp:
  50. return {}; // PlayerControl does not support hanging up.
  51. case MediaSessionAction::kRaise:
  52. return {}; // PlayerControl does not support raising.
  53. case MediaSessionAction::kSetMute:
  54. return {}; // TODO(crbug.com/1240811): implement set mute.
  55. }
  56. }
  57. void AddMetadata(base::StringPiece label,
  58. base::StringPiece16 value,
  59. fuchsia::media::Metadata* metadata) {
  60. fuchsia::media::Property property = {.label{label},
  61. .value{base::UTF16ToUTF8(value)}};
  62. metadata->properties.emplace_back(std::move(property));
  63. }
  64. fuchsia::media::sessions2::PlayerState SessionStateToPlayerState(
  65. media_session::mojom::MediaSessionInfo::SessionState state) {
  66. switch (state) {
  67. case media_session::mojom::MediaSessionInfo::SessionState::kActive:
  68. case media_session::mojom::MediaSessionInfo::SessionState::kDucking:
  69. return fuchsia::media::sessions2::PlayerState::PLAYING;
  70. case media_session::mojom::MediaSessionInfo::SessionState::kInactive:
  71. return fuchsia::media::sessions2::PlayerState::IDLE;
  72. case media_session::mojom::MediaSessionInfo::SessionState::kSuspended:
  73. return fuchsia::media::sessions2::PlayerState::PAUSED;
  74. };
  75. }
  76. } // namespace
  77. MediaPlayerImpl::MediaPlayerImpl(
  78. content::MediaSession* media_session,
  79. fidl::InterfaceRequest<fuchsia::media::sessions2::Player> request,
  80. base::OnceClosure on_disconnect)
  81. : media_session_(media_session),
  82. on_disconnect_(std::move(on_disconnect)),
  83. binding_(this, std::move(request)),
  84. observer_receiver_(this) {
  85. binding_.set_error_handler([this](zx_status_t status) mutable {
  86. ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status)
  87. << "Player disconnected.";
  88. std::move(on_disconnect_).Run();
  89. });
  90. // Set default values for some fields in |pending_info_delta_|, which some
  91. // clients may otherwise use incorrect defaults for.
  92. pending_info_delta_.set_local(true);
  93. }
  94. MediaPlayerImpl::~MediaPlayerImpl() = default;
  95. void MediaPlayerImpl::WatchInfoChange(
  96. WatchInfoChangeCallback info_change_callback) {
  97. if (pending_info_change_callback_) {
  98. DLOG(ERROR) << "Unexpected WatchInfoChange().";
  99. ReportErrorAndDisconnect(ZX_ERR_BAD_STATE);
  100. return;
  101. }
  102. pending_info_change_callback_ = std::move(info_change_callback);
  103. if (!observer_receiver_.is_bound()) {
  104. // |media_session| will notify us via our MediaSessionObserver interface
  105. // of the current state of the session (metadata, actions, etc) in response
  106. // to AddObserver().
  107. media_session_->AddObserver(observer_receiver_.BindNewPipeAndPassRemote());
  108. }
  109. MaybeSendPlayerInfoDelta();
  110. }
  111. void MediaPlayerImpl::Play() {
  112. media_session_->Resume(content::MediaSession::SuspendType::kUI);
  113. }
  114. void MediaPlayerImpl::Pause() {
  115. media_session_->Suspend(content::MediaSession::SuspendType::kUI);
  116. }
  117. void MediaPlayerImpl::Stop() {
  118. media_session_->Suspend(content::MediaSession::SuspendType::kUI);
  119. }
  120. void MediaPlayerImpl::Seek(zx_duration_t position) {
  121. media_session_->SeekTo(base::TimeDelta::FromZxDuration(position));
  122. }
  123. void MediaPlayerImpl::SkipForward() {
  124. media_session_->Seek(
  125. base::Seconds(media_session::mojom::kDefaultSeekTimeSeconds));
  126. }
  127. void MediaPlayerImpl::SkipReverse() {
  128. media_session_->Seek(
  129. -base::Seconds(media_session::mojom::kDefaultSeekTimeSeconds));
  130. }
  131. void MediaPlayerImpl::NextItem() {
  132. media_session_->NextTrack();
  133. }
  134. void MediaPlayerImpl::PrevItem() {
  135. media_session_->PreviousTrack();
  136. }
  137. void MediaPlayerImpl::SetPlaybackRate(float playback_rate) {
  138. // content::MediaSession does not support changes to playback rate.
  139. }
  140. void MediaPlayerImpl::SetRepeatMode(
  141. fuchsia::media::sessions2::RepeatMode repeat_mode) {
  142. // content::MediaSession does not provide control over repeat playback.
  143. }
  144. void MediaPlayerImpl::SetShuffleMode(bool shuffle_on) {
  145. // content::MediaSession does not provide control over item playback order.
  146. }
  147. void MediaPlayerImpl::BindVolumeControl(
  148. fidl::InterfaceRequest<fuchsia::media::audio::VolumeControl>
  149. volume_control_request) {
  150. // content::MediaSession does not provide control over audio gain.
  151. volume_control_request.Close(ZX_ERR_NOT_SUPPORTED);
  152. }
  153. void MediaPlayerImpl::MediaSessionInfoChanged(
  154. media_session::mojom::MediaSessionInfoPtr info) {
  155. fuchsia::media::sessions2::PlayerStatus status;
  156. status.set_player_state(SessionStateToPlayerState(info->state));
  157. pending_info_delta_.set_player_status(std::move(status));
  158. MaybeSendPlayerInfoDelta();
  159. }
  160. void MediaPlayerImpl::MediaSessionMetadataChanged(
  161. const absl::optional<media_session::MediaMetadata>& metadata_mojo) {
  162. fuchsia::media::Metadata metadata;
  163. if (metadata_mojo) {
  164. AddMetadata(fuchsia::media::METADATA_LABEL_TITLE, metadata_mojo->title,
  165. &metadata);
  166. AddMetadata(fuchsia::media::METADATA_LABEL_ARTIST, metadata_mojo->artist,
  167. &metadata);
  168. AddMetadata(fuchsia::media::METADATA_LABEL_ALBUM, metadata_mojo->album,
  169. &metadata);
  170. AddMetadata(fuchsia::media::METADATA_SOURCE_TITLE,
  171. metadata_mojo->source_title, &metadata);
  172. }
  173. pending_info_delta_.set_metadata(std::move(metadata));
  174. MaybeSendPlayerInfoDelta();
  175. }
  176. void MediaPlayerImpl::MediaSessionActionsChanged(
  177. const std::vector<media_session::mojom::MediaSessionAction>& actions) {
  178. // TODO(https://crbug.com/879317): Implement PROVIDE_BITMAPS.
  179. fuchsia::media::sessions2::PlayerCapabilityFlags capability_flags{};
  180. for (auto action : actions)
  181. capability_flags |= ActionToCapabilityFlag(action);
  182. pending_info_delta_.mutable_player_capabilities()->set_flags(
  183. std::move(capability_flags));
  184. MaybeSendPlayerInfoDelta();
  185. }
  186. void MediaPlayerImpl::MediaSessionImagesChanged(
  187. const base::flat_map<media_session::mojom::MediaSessionImageType,
  188. std::vector<media_session::MediaImage>>& images) {
  189. // TODO(https://crbug.com/879317): Implement image-changed.
  190. NOTIMPLEMENTED_LOG_ONCE();
  191. }
  192. void MediaPlayerImpl::MediaSessionPositionChanged(
  193. const absl::optional<media_session::MediaPosition>& position) {
  194. // TODO(https://crbug.com/879317): Implement media position changes.
  195. NOTIMPLEMENTED_LOG_ONCE();
  196. }
  197. void MediaPlayerImpl::MaybeSendPlayerInfoDelta() {
  198. if (!pending_info_change_callback_)
  199. return;
  200. if (pending_info_delta_.IsEmpty())
  201. return;
  202. // std::exchange(foo, {}) returns the contents of |foo|, while ensuring that
  203. // |foo| is reset to the initial/empty state.
  204. std::exchange(pending_info_change_callback_,
  205. {})(std::exchange(pending_info_delta_, {}));
  206. }
  207. void MediaPlayerImpl::ReportErrorAndDisconnect(zx_status_t status) {
  208. binding_.Close(status);
  209. std::move(on_disconnect_).Run();
  210. }