blob: 96376c2ca77cbb61c8d7c922651bfc55578baa5a [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/media_session/media_session_client.h"
#include <algorithm>
#include <memory>
namespace cobalt {
namespace media_session {
MediaSessionPlaybackState MediaSessionClient::ComputeActualPlaybackState()
const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Per https://wicg.github.io/mediasession/#guessed-playback-state
// - If the "declared playback state" is "playing", then return "playing"
// - Otherwise, return the guessed playback state
MediaSessionPlaybackState declared_state;
declared_state = media_session_->playback_state();
if (declared_state == kMediaSessionPlaybackStatePlaying) {
return kMediaSessionPlaybackStatePlaying;
}
if (platform_playback_state_ == kMediaSessionPlaybackStatePlaying) {
// "...guessed playback state is playing if any of them is
// potentially playing and not muted..."
return kMediaSessionPlaybackStatePlaying;
}
// It's not super clear what to do when the declared state or the
// active media session state is kPaused or kNone
if (declared_state == kMediaSessionPlaybackStatePaused) {
return kMediaSessionPlaybackStatePaused;
}
return kMediaSessionPlaybackStateNone;
}
MediaSessionState::AvailableActionsSet
MediaSessionClient::ComputeAvailableActions() const {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// "Available actions" are determined based on active media session
// and supported media session actions.
// Note for cobalt, there's only one window/tab so there's only one
// "active media session"
// https://wicg.github.io/mediasession/#actions-model
//
// Note that this is essentially the "media session actions update algorithm"
// inverted.
MediaSessionState::AvailableActionsSet result =
MediaSessionState::AvailableActionsSet();
for (MediaSession::ActionMap::iterator it =
media_session_->action_map_.begin();
it != media_session_->action_map_.end(); ++it) {
result[it->first] = true;
}
switch (ComputeActualPlaybackState()) {
case kMediaSessionPlaybackStatePlaying:
// "If the active media session’s actual playback state is playing, remove
// play from available actions."
result[kMediaSessionActionPlay] = false;
break;
case kMediaSessionPlaybackStateNone:
case kMediaSessionPlaybackStatePaused:
// "Otherwise, remove pause from available actions."
result[kMediaSessionActionPause] = false;
break;
}
return result;
}
void MediaSessionClient::UpdatePlatformPlaybackState(
MediaSessionPlaybackState state) {
DCHECK(media_session_->task_runner_);
if (!media_session_->task_runner_->BelongsToCurrentThread()) {
media_session_->task_runner_->PostTask(
FROM_HERE, base::Bind(&MediaSessionClient::UpdatePlatformPlaybackState,
base::Unretained(this), state));
return;
}
platform_playback_state_ = state;
if (session_state_.actual_playback_state() != ComputeActualPlaybackState()) {
UpdateMediaSessionState();
}
}
void MediaSessionClient::InvokeActionInternal(
std::unique_ptr<MediaSessionActionDetails> details) {
DCHECK(details->has_action());
// Some fields should only be set for applicable actions.
DCHECK(!details->has_seek_offset() ||
details->action() == kMediaSessionActionSeekforward ||
details->action() == kMediaSessionActionSeekbackward);
DCHECK(!details->has_seek_time() ||
details->action() == kMediaSessionActionSeekto);
DCHECK(!details->has_fast_seek() ||
details->action() == kMediaSessionActionSeekto);
// Seek times/offsets are non-negative, even for seeking backwards.
DCHECK(!details->has_seek_time() || details->seek_time() >= 0.0);
DCHECK(!details->has_seek_offset() || details->seek_offset() >= 0.0);
DCHECK(media_session_->task_runner_);
if (!media_session_->task_runner_->BelongsToCurrentThread()) {
media_session_->task_runner_->PostTask(
FROM_HERE, base::Bind(&MediaSessionClient::InvokeActionInternal,
base::Unretained(this), base::Passed(&details)));
return;
}
MediaSession::ActionMap::iterator it =
media_session_->action_map_.find(details->action());
if (it == media_session_->action_map_.end()) {
return;
}
it->second->value().Run(*details);
}
MediaSessionState MediaSessionClient::GetMediaSessionState() {
MediaSessionState session_state;
GetMediaSessionStateInternal(&session_state);
return session_state;
}
void MediaSessionClient::GetMediaSessionStateInternal(
MediaSessionState* session_state) {
DCHECK(media_session_->task_runner_);
if (!media_session_->task_runner_->BelongsToCurrentThread()) {
media_session_->task_runner_->PostBlockingTask(
FROM_HERE,
base::Bind(&MediaSessionClient::GetMediaSessionStateInternal,
base::Unretained(this), base::Unretained(session_state)));
return;
}
*session_state = session_state_;
}
void MediaSessionClient::UpdateMediaSessionState() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
scoped_refptr<MediaMetadata> session_metadata(media_session_->metadata());
base::Optional<MediaMetadataInit> metadata;
if (session_metadata) {
metadata.emplace();
metadata->set_title(session_metadata->title());
metadata->set_artist(session_metadata->artist());
metadata->set_album(session_metadata->album());
metadata->set_artwork(session_metadata->artwork());
}
session_state_ = MediaSessionState(
metadata,
media_session_->last_position_updated_time_,
media_session_->media_position_state_,
ComputeActualPlaybackState(),
ComputeAvailableActions());
OnMediaSessionStateChanged(session_state_);
}
} // namespace media_session
} // namespace cobalt