SmartAudio/package/avs/avs-sdk/files/avs-device-sdk/CapabilityAgents/ExternalMediaPlayer/src/ExternalMediaPlayer.cpp

720 lines
30 KiB
C++

/*
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. 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.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file 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.
*/
/// @file ExternalMediaPlayer.cpp
#include "ExternalMediaPlayer/ExternalMediaPlayer.h"
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <rapidjson/error/en.h>
#include <AVSCommon/AVS/ExternalMediaPlayer/AdapterUtils.h>
#include <AVSCommon/AVS/SpeakerConstants/SpeakerConstants.h>
#include <AVSCommon/Utils/JSON/JSONUtils.h>
#include <AVSCommon/Utils/Memory/Memory.h>
namespace alexaClientSDK {
namespace capabilityAgents {
namespace externalMediaPlayer {
using namespace avsCommon::avs;
using namespace avsCommon::avs::externalMediaPlayer;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::sdkInterfaces::externalMediaPlayer;
using namespace avsCommon::avs::attachment;
using namespace avsCommon::utils;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::utils::json;
using namespace avsCommon::utils::logger;
/// String to identify log entries originating from this file.
static const std::string TAG("ExternalMediaPlayer");
/**
* Create a LogEntry using this file's TAG and the specified event string.
*
* @param The event string for this @c LogEntry.
*/
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
// The namespaces used in the context.
static const std::string EXTERNALMEDIAPLAYER_STATE_NAMESPACE = "ExternalMediaPlayer";
static const std::string PLAYBACKSTATEREPORTER_STATE_NAMESPACE = "Alexa.PlaybackStateReporter";
// The names used in the context.
static const std::string EXTERNALMEDIAPLAYER_NAME = "ExternalMediaPlayerState";
static const std::string PLAYBACKSTATEREPORTER_NAME = "playbackState";
// The namespace for this capability agent.
static const std::string EXTERNALMEDIAPLAYER_NAMESPACE = "ExternalMediaPlayer";
static const std::string PLAYBACKCONTROLLER_NAMESPACE = "Alexa.PlaybackController";
static const std::string PLAYLISTCONTROLLER_NAMESPACE = "Alexa.PlaylistController";
static const std::string SEEKCONTROLLER_NAMESPACE = "Alexa.SeekController";
static const std::string FAVORITESCONTROLLER_NAMESPACE = "Alexa.FavoritesController";
// The @c External media player play directive signature.
static const NamespaceAndName PLAY_DIRECTIVE{EXTERNALMEDIAPLAYER_NAMESPACE, "Play"};
static const NamespaceAndName LOGIN_DIRECTIVE{EXTERNALMEDIAPLAYER_NAMESPACE, "Login"};
static const NamespaceAndName LOGOUT_DIRECTIVE{EXTERNALMEDIAPLAYER_NAMESPACE, "Logout"};
// The @c Transport control directive signatures.
static const NamespaceAndName RESUME_DIRECTIVE{PLAYBACKCONTROLLER_NAMESPACE, "Play"};
static const NamespaceAndName PAUSE_DIRECTIVE{PLAYBACKCONTROLLER_NAMESPACE, "Pause"};
static const NamespaceAndName NEXT_DIRECTIVE{PLAYBACKCONTROLLER_NAMESPACE, "Next"};
static const NamespaceAndName PREVIOUS_DIRECTIVE{PLAYBACKCONTROLLER_NAMESPACE, "Previous"};
static const NamespaceAndName STARTOVER_DIRECTIVE{PLAYBACKCONTROLLER_NAMESPACE, "StartOver"};
static const NamespaceAndName REWIND_DIRECTIVE{PLAYBACKCONTROLLER_NAMESPACE, "Rewind"};
static const NamespaceAndName FASTFORWARD_DIRECTIVE{PLAYBACKCONTROLLER_NAMESPACE, "FastForward"};
// The @c PlayList control directive signature.
static const NamespaceAndName ENABLEREPEATONE_DIRECTIVE{PLAYLISTCONTROLLER_NAMESPACE, "EnableRepeatOne"};
static const NamespaceAndName ENABLEREPEAT_DIRECTIVE{PLAYLISTCONTROLLER_NAMESPACE, "EnableRepeat"};
static const NamespaceAndName DISABLEREPEAT_DIRECTIVE{PLAYLISTCONTROLLER_NAMESPACE, "DisableRepeat"};
static const NamespaceAndName ENABLESHUFFLE_DIRECTIVE{PLAYLISTCONTROLLER_NAMESPACE, "EnableShuffle"};
static const NamespaceAndName DISABLESHUFFLE_DIRECTIVE{PLAYLISTCONTROLLER_NAMESPACE, "DisableShuffle"};
// The @c Seek control directive signature.
static const NamespaceAndName SEEK_DIRECTIVE{SEEKCONTROLLER_NAMESPACE, "SetSeekPosition"};
static const NamespaceAndName ADJUSTSEEK_DIRECTIVE{SEEKCONTROLLER_NAMESPACE, "AdjustSeekPosition"};
// The @c favorites control directive signature.
static const NamespaceAndName FAVORITE_DIRECTIVE{FAVORITESCONTROLLER_NAMESPACE, "Favorite"};
static const NamespaceAndName UNFAVORITE_DIRECTIVE{FAVORITESCONTROLLER_NAMESPACE, "Unfavorite"};
// The @c ExternalMediaPlayer context state signatures.
static const NamespaceAndName SESSION_STATE{EXTERNALMEDIAPLAYER_STATE_NAMESPACE, EXTERNALMEDIAPLAYER_NAME};
static const NamespaceAndName PLAYBACK_STATE{PLAYBACKSTATEREPORTER_STATE_NAMESPACE, PLAYBACKSTATEREPORTER_NAME};
/// The const char for the players key field in the context.
static const char PLAYERS[] = "players";
/// The const char for the playerInFocus key field in the context.
static const char PLAYER_IN_FOCUS[] = "playerInFocus";
/// The max relative time in the past that we can seek to in milliseconds(-12hours in ms).
static const int64_t MAX_PAST_OFFSET = -86400000;
/// The max relative time in the past that we can seek to in milliseconds(+12 hours in ms).
static const int64_t MAX_FUTURE_OFFSET = 86400000;
/// The @c m_directiveToHandlerMap Map of the directives to their handlers.
std::unordered_map<NamespaceAndName, std::pair<RequestType, ExternalMediaPlayer::DirectiveHandler>>
ExternalMediaPlayer::m_directiveToHandlerMap = {
{LOGIN_DIRECTIVE, std::make_pair(RequestType::LOGIN, &ExternalMediaPlayer::handleLogin)},
{LOGOUT_DIRECTIVE, std::make_pair(RequestType::LOGOUT, &ExternalMediaPlayer::handleLogout)},
{PLAY_DIRECTIVE, std::make_pair(RequestType::PLAY, &ExternalMediaPlayer::handlePlay)},
{PAUSE_DIRECTIVE, std::make_pair(RequestType::PAUSE, &ExternalMediaPlayer::handlePlayControl)},
{RESUME_DIRECTIVE, std::make_pair(RequestType::RESUME, &ExternalMediaPlayer::handlePlayControl)},
{NEXT_DIRECTIVE, std::make_pair(RequestType::NEXT, &ExternalMediaPlayer::handlePlayControl)},
{PREVIOUS_DIRECTIVE, std::make_pair(RequestType::PREVIOUS, &ExternalMediaPlayer::handlePlayControl)},
{STARTOVER_DIRECTIVE, std::make_pair(RequestType::START_OVER, &ExternalMediaPlayer::handlePlayControl)},
{FASTFORWARD_DIRECTIVE, std::make_pair(RequestType::FAST_FORWARD, &ExternalMediaPlayer::handlePlayControl)},
{REWIND_DIRECTIVE, std::make_pair(RequestType::REWIND, &ExternalMediaPlayer::handlePlayControl)},
{ENABLEREPEATONE_DIRECTIVE,
std::make_pair(RequestType::ENABLE_REPEAT_ONE, &ExternalMediaPlayer::handlePlayControl)},
{ENABLEREPEAT_DIRECTIVE, std::make_pair(RequestType::ENABLE_REPEAT, &ExternalMediaPlayer::handlePlayControl)},
{DISABLEREPEAT_DIRECTIVE, std::make_pair(RequestType::DISABLE_REPEAT, &ExternalMediaPlayer::handlePlayControl)},
{ENABLESHUFFLE_DIRECTIVE, std::make_pair(RequestType::ENABLE_SHUFFLE, &ExternalMediaPlayer::handlePlayControl)},
{DISABLESHUFFLE_DIRECTIVE,
std::make_pair(RequestType::DISABLE_SHUFFLE, &ExternalMediaPlayer::handlePlayControl)},
{FAVORITE_DIRECTIVE, std::make_pair(RequestType::FAVORITE, &ExternalMediaPlayer::handlePlayControl)},
{UNFAVORITE_DIRECTIVE, std::make_pair(RequestType::UNFAVORITE, &ExternalMediaPlayer::handlePlayControl)},
{SEEK_DIRECTIVE, std::make_pair(RequestType::SEEK, &ExternalMediaPlayer::handleSeek)},
{ADJUSTSEEK_DIRECTIVE, std::make_pair(RequestType::ADJUST_SEEK, &ExternalMediaPlayer::handleAdjustSeek)}};
static DirectiveHandlerConfiguration g_configuration = {{PLAY_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{LOGIN_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{LOGOUT_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{RESUME_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{PAUSE_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{NEXT_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{PREVIOUS_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{STARTOVER_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{REWIND_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{FASTFORWARD_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{ENABLEREPEATONE_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{ENABLEREPEAT_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{DISABLEREPEAT_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{ENABLESHUFFLE_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{DISABLESHUFFLE_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{SEEK_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{ADJUSTSEEK_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{FAVORITE_DIRECTIVE, BlockingPolicy::NON_BLOCKING},
{UNFAVORITE_DIRECTIVE, BlockingPolicy::NON_BLOCKING}};
static std::unordered_map<avsCommon::avs::PlaybackButton, RequestType> g_buttonToRequestType = {
{avsCommon::avs::PlaybackButton::PLAY, RequestType::PAUSE_RESUME_TOGGLE},
{avsCommon::avs::PlaybackButton::PAUSE, RequestType::PAUSE_RESUME_TOGGLE},
{avsCommon::avs::PlaybackButton::NEXT, RequestType::NEXT},
{avsCommon::avs::PlaybackButton::PREVIOUS, RequestType::PREVIOUS}};
std::shared_ptr<ExternalMediaPlayer> ExternalMediaPlayer::create(
const AdapterMediaPlayerMap& mediaPlayers,
const AdapterCreationMap& adapterCreationMap,
std::shared_ptr<SpeakerManagerInterface> speakerManager,
std::shared_ptr<MessageSenderInterface> messageSender,
std::shared_ptr<FocusManagerInterface> focusManager,
std::shared_ptr<ContextManagerInterface> contextManager,
std::shared_ptr<ExceptionEncounteredSenderInterface> exceptionSender,
std::shared_ptr<PlaybackRouterInterface> playbackRouter) {
if (nullptr == messageSender) {
ACSDK_ERROR(LX("createFailed").d("reason", "nullMessageSender"));
return nullptr;
}
if (nullptr == focusManager) {
ACSDK_ERROR(LX("createFailed").d("reason", "nullFocusManager"));
return nullptr;
}
if (nullptr == contextManager) {
ACSDK_ERROR(LX("createFailed").d("reason", "nullContextManager"));
return nullptr;
}
if (nullptr == exceptionSender) {
ACSDK_ERROR(LX("createFailed").d("reason", "nullExceptionSender"));
return nullptr;
}
if (nullptr == playbackRouter) {
ACSDK_ERROR(LX("createFailed").d("reason", "nullPlaybackRouter"));
return nullptr;
}
auto externalMediaPlayer = std::shared_ptr<ExternalMediaPlayer>(
new ExternalMediaPlayer(speakerManager, contextManager, exceptionSender, playbackRouter));
contextManager->setStateProvider(SESSION_STATE, externalMediaPlayer);
contextManager->setStateProvider(PLAYBACK_STATE, externalMediaPlayer);
externalMediaPlayer->createAdapters(mediaPlayers, adapterCreationMap, messageSender, focusManager, contextManager);
return externalMediaPlayer;
}
ExternalMediaPlayer::ExternalMediaPlayer(
std::shared_ptr<SpeakerManagerInterface> speakerManager,
std::shared_ptr<ContextManagerInterface> contextManager,
std::shared_ptr<ExceptionEncounteredSenderInterface> exceptionSender,
std::shared_ptr<PlaybackRouterInterface> playbackRouter) :
CapabilityAgent{EXTERNALMEDIAPLAYER_NAMESPACE, exceptionSender},
RequiresShutdown{"ExternalMediaPlayer"},
m_speakerManager{speakerManager},
m_contextManager{contextManager},
m_playbackRouter{playbackRouter} {
m_speakerSettings.volume = avsCommon::avs::speakerConstants::AVS_SET_VOLUME_MAX;
m_speakerSettings.mute = false;
}
void ExternalMediaPlayer::provideState(
const avsCommon::avs::NamespaceAndName& stateProviderName,
unsigned int stateRequestToken) {
m_executor.submit([this, stateProviderName, stateRequestToken] {
executeProvideState(stateProviderName, true, stateRequestToken);
});
}
void ExternalMediaPlayer::handleDirectiveImmediately(std::shared_ptr<AVSDirective> directive) {
handleDirective(std::make_shared<DirectiveInfo>(directive, nullptr));
}
void ExternalMediaPlayer::preHandleDirective(std::shared_ptr<DirectiveInfo> info) {
}
bool ExternalMediaPlayer::parseDirectivePayload(std::shared_ptr<DirectiveInfo> info, rapidjson::Document* document) {
rapidjson::ParseResult result = document->Parse(info->directive->getPayload());
if (result) {
return true;
}
ACSDK_ERROR(LX("parseDirectivePayloadFailed")
.d("reason", rapidjson::GetParseError_En(result.Code()))
.d("offset", result.Offset())
.d("messageId", info->directive->getMessageId()));
sendExceptionEncounteredAndReportFailed(
info, "Unable to parse payload", ExceptionErrorType::UNEXPECTED_INFORMATION_RECEIVED);
return false;
}
void ExternalMediaPlayer::handleDirective(std::shared_ptr<DirectiveInfo> info) {
if (!info) {
ACSDK_ERROR(LX("handleDirectiveFailed").d("reason", "nullDirectiveInfo"));
return;
}
NamespaceAndName directiveNamespaceAndName(info->directive->getNamespace(), info->directive->getName());
auto handlerIt = m_directiveToHandlerMap.find(directiveNamespaceAndName);
if (handlerIt == m_directiveToHandlerMap.end()) {
ACSDK_ERROR(LX("handleDirectivesFailed")
.d("reason", "noDirectiveHandlerForDirective")
.d("nameSpace", info->directive->getNamespace())
.d("name", info->directive->getName()));
return;
}
ACSDK_DEBUG9(LX("handleDirectivesPayload").sensitive("Payload", info->directive->getPayload()));
auto handler = (handlerIt->second.second);
(this->*handler)(info, handlerIt->second.first);
}
std::shared_ptr<ExternalMediaAdapterInterface> ExternalMediaPlayer::preprocessDirective(
std::shared_ptr<DirectiveInfo> info,
rapidjson::Document* document) {
ACSDK_DEBUG9(LX("preprocessDirective"));
if (!parseDirectivePayload(info, document)) {
sendExceptionEncounteredAndReportFailed(info, "Failed to parse directive.");
return nullptr;
}
std::string playerId;
if (!jsonUtils::retrieveValue(*document, "playerId", &playerId)) {
ACSDK_ERROR(LX("preprocessDirectiveFailed").d("reason", "nullPlayerId"));
sendExceptionEncounteredAndReportFailed(info, "No PlayerId in directive.");
return nullptr;
}
auto adapterIt = m_adapters.find(playerId);
if (adapterIt == m_adapters.end()) {
ACSDK_ERROR(LX("preprocessDirectiveFailed").d("reason", "noAdapterForPlayerId").d("playerId", playerId));
sendExceptionEncounteredAndReportFailed(info, "Unrecogonized PlayerId.");
return nullptr;
}
auto adapter = adapterIt->second;
if (!adapter) {
ACSDK_ERROR(LX("preprocessDirectiveFailed").d("reason", "nullAdapter").d("playerId", playerId));
sendExceptionEncounteredAndReportFailed(info, "nullAdapter.");
return nullptr;
}
return adapter;
}
void ExternalMediaPlayer::handleLogin(std::shared_ptr<DirectiveInfo> info, RequestType request) {
rapidjson::Document payload;
auto adapter = preprocessDirective(info, &payload);
if (!adapter) {
return;
}
std::string accessToken;
if (!jsonUtils::retrieveValue(payload, "accessToken", &accessToken)) {
ACSDK_ERROR(LX("handleDirectiveFailed").d("reason", "nullAccessToken"));
sendExceptionEncounteredAndReportFailed(info, "missing accessToken in Login directive");
return;
}
std::string userName;
if (!jsonUtils::retrieveValue(payload, "username", &userName)) {
userName = "";
}
int64_t refreshInterval;
if (!jsonUtils::retrieveValue(payload, "tokenRefreshIntervalInMilliseconds", &refreshInterval)) {
ACSDK_ERROR(LX("handleDirectiveFailed").d("reason", "nullRefreshInterval"));
sendExceptionEncounteredAndReportFailed(info, "missing tokenRefreshIntervalInMilliseconds in Login directive");
return;
}
bool forceLogin;
if (!jsonUtils::retrieveValue(payload, "forceLogin", &forceLogin)) {
ACSDK_ERROR(LX("handleDirectiveFailed").d("reason", "nullForceLogin"));
sendExceptionEncounteredAndReportFailed(info, "missing forceLogin in Login directive");
return;
}
setHandlingCompleted(info);
adapter->handleLogin(accessToken, userName, forceLogin, std::chrono::milliseconds(refreshInterval));
}
void ExternalMediaPlayer::handleLogout(std::shared_ptr<DirectiveInfo> info, RequestType request) {
rapidjson::Document payload;
auto adapter = preprocessDirective(info, &payload);
if (!adapter) {
return;
}
setHandlingCompleted(info);
adapter->handleLogout();
}
void ExternalMediaPlayer::handlePlay(std::shared_ptr<DirectiveInfo> info, RequestType request) {
rapidjson::Document payload;
auto adapter = preprocessDirective(info, &payload);
if (!adapter) {
return;
}
std::string playbackContextToken;
if (!jsonUtils::retrieveValue(payload, "playbackContextToken", &playbackContextToken)) {
ACSDK_ERROR(LX("handleDirectiveFailed").d("reason", "nullPlaybackContextToken"));
sendExceptionEncounteredAndReportFailed(info, "missing playbackContextToken in Play directive");
return;
}
int64_t offset;
if (!jsonUtils::retrieveValue(payload, "offsetInMilliseconds", &offset)) {
offset = 0;
}
int64_t index;
if (!jsonUtils::retrieveValue(payload, "index", &index)) {
index = 0;
}
setHandlingCompleted(info);
adapter->handlePlay(playbackContextToken, index, std::chrono::milliseconds(offset));
}
void ExternalMediaPlayer::handleSeek(std::shared_ptr<DirectiveInfo> info, RequestType request) {
rapidjson::Document payload;
auto adapter = preprocessDirective(info, &payload);
if (!adapter) {
return;
}
int64_t position;
if (!jsonUtils::retrieveValue(payload, "positionMilliseconds", &position)) {
ACSDK_ERROR(LX("handleDirectiveFailed").d("reason", "nullPosition"));
sendExceptionEncounteredAndReportFailed(info, "missing positionMilliseconds in SetSeekPosition directive");
return;
}
setHandlingCompleted(info);
adapter->handleSeek(std::chrono::milliseconds(position));
}
void ExternalMediaPlayer::handleAdjustSeek(std::shared_ptr<DirectiveInfo> info, RequestType request) {
rapidjson::Document payload;
auto adapter = preprocessDirective(info, &payload);
if (!adapter) {
return;
}
int64_t deltaPosition;
if (!jsonUtils::retrieveValue(payload, "deltaPositionMilliseconds", &deltaPosition)) {
ACSDK_ERROR(LX("handleDirectiveFailed").d("reason", "nullDeltaPositionMilliseconds"));
sendExceptionEncounteredAndReportFailed(
info, "missing deltaPositionMilliseconds in AdjustSeekPosition directive");
return;
}
if (deltaPosition < MAX_PAST_OFFSET || deltaPosition > MAX_FUTURE_OFFSET) {
ACSDK_ERROR(LX("handleDirectiveFailed").d("reason", "deltaPositionMillisecondsOutOfRange."));
sendExceptionEncounteredAndReportFailed(
info, "missing deltaPositionMilliseconds in AdjustSeekPosition directive");
return;
}
setHandlingCompleted(info);
adapter->handleAdjustSeek(std::chrono::milliseconds(deltaPosition));
}
void ExternalMediaPlayer::handlePlayControl(std::shared_ptr<DirectiveInfo> info, RequestType request) {
rapidjson::Document payload;
auto adapter = preprocessDirective(info, &payload);
if (!adapter) {
return;
}
setHandlingCompleted(info);
adapter->handlePlayControl(request);
}
void ExternalMediaPlayer::cancelDirective(std::shared_ptr<DirectiveInfo> info) {
removeDirective(info);
}
void ExternalMediaPlayer::onDeregistered() {
}
DirectiveHandlerConfiguration ExternalMediaPlayer::getConfiguration() const {
return g_configuration;
}
void ExternalMediaPlayer::setPlayerInFocus(const std::string& playerInFocus) {
ACSDK_DEBUG9(LX("setPlayerInFocus").d("playerInFocus", playerInFocus));
m_playerInFocus = playerInFocus;
m_playbackRouter->setHandler(shared_from_this());
}
void ExternalMediaPlayer::onButtonPressed(PlaybackButton button) {
if (!m_playerInFocus.empty()) {
auto adapterIt = m_adapters.find(m_playerInFocus);
if (adapterIt == m_adapters.end()) {
// Should never reach here as playerInFocus is always set based on a contract with AVS.
ACSDK_ERROR(LX("AdapterNotFound").d("player", m_playerInFocus));
return;
}
auto buttonIt = g_buttonToRequestType.find(button);
if (buttonIt == g_buttonToRequestType.end()) {
ACSDK_ERROR(LX("ButtonToRequestTypeNotFound").d("button", PlaybackButtonToString(button)));
return;
}
((*adapterIt).second)->handlePlayControl((*buttonIt).second);
}
}
void ExternalMediaPlayer::doShutdown() {
m_executor.shutdown();
// Reset the EMP from being a state provider. If not there would be calls from the adapter to provide context
// which will try to add tasks to the executor thread.
m_contextManager->setStateProvider(SESSION_STATE, nullptr);
m_contextManager->setStateProvider(PLAYBACK_STATE, nullptr);
for (auto& adapter : m_adapters) {
if (!adapter.second) {
continue;
}
adapter.second->shutdown();
}
m_adapters.clear();
m_exceptionEncounteredSender.reset();
m_contextManager.reset();
m_playbackRouter.reset();
m_speakerManager.reset();
}
void ExternalMediaPlayer::removeDirective(std::shared_ptr<DirectiveInfo> info) {
// Check result too, to catch cases where DirectiveInfo was created locally, without a nullptr result.
// In those cases there is no messageId to remove because no result was expected.
if (info->directive && info->result) {
CapabilityAgent::removeDirective(info->directive->getMessageId());
}
}
void ExternalMediaPlayer::setHandlingCompleted(std::shared_ptr<DirectiveInfo> info) {
if (info && info->result) {
info->result->setCompleted();
}
removeDirective(info);
}
void ExternalMediaPlayer::sendExceptionEncounteredAndReportFailed(
std::shared_ptr<DirectiveInfo> info,
const std::string& message,
avsCommon::avs::ExceptionErrorType type) {
if (info && info->directive) {
m_exceptionEncounteredSender->sendExceptionEncountered(info->directive->getUnparsedDirective(), type, message);
}
if (info && info->result) {
info->result->setFailed(message);
}
removeDirective(info);
}
void ExternalMediaPlayer::executeProvideState(
const avsCommon::avs::NamespaceAndName& stateProviderName,
bool sendToken,
unsigned int stateRequestToken) {
ACSDK_DEBUG(LX("executeProvideState").d("sendToken", sendToken).d("stateRequestToken", stateRequestToken));
std::string state;
if (stateProviderName == SESSION_STATE) {
state = provideSessionState();
} else if (stateProviderName == PLAYBACK_STATE) {
state = providePlaybackState();
} else {
ACSDK_ERROR(LX("executeProvideState").d("reason", "unknownStateProviderName"));
return;
}
SetStateResult result;
if (sendToken) {
result = m_contextManager->setState(stateProviderName, state, StateRefreshPolicy::ALWAYS, stateRequestToken);
} else {
result = m_contextManager->setState(stateProviderName, state, StateRefreshPolicy::ALWAYS);
}
if (result != SetStateResult::SUCCESS) {
ACSDK_ERROR(LX("executeProvideState").d("reason", "contextManagerSetStateFailedForEMPState"));
}
}
std::string ExternalMediaPlayer::provideSessionState() {
rapidjson::Document state(rapidjson::kObjectType);
rapidjson::Document::AllocatorType& stateAlloc = state.GetAllocator();
state.AddMember(rapidjson::StringRef(PLAYER_IN_FOCUS), m_playerInFocus, stateAlloc);
rapidjson::Value players(rapidjson::kArrayType);
for (const auto& adapter : m_adapters) {
if (!adapter.second) {
continue;
}
rapidjson::Value playerJson = buildSessionState(adapter.second->getState().sessionState, stateAlloc);
players.PushBack(playerJson, stateAlloc);
}
state.AddMember(rapidjson::StringRef(PLAYERS), players, stateAlloc);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
if (!state.Accept(writer)) {
ACSDK_ERROR(LX("provideSessionStateFailed").d("reason", "writerRefusedJsonObject"));
return "";
}
return buffer.GetString();
}
std::string ExternalMediaPlayer::providePlaybackState() {
rapidjson::Document state(rapidjson::kObjectType);
rapidjson::Document::AllocatorType& stateAlloc = state.GetAllocator();
// Fill the default player state.
if (!buildDefaultPlayerState(&state, stateAlloc)) {
return "";
}
// Fetch actual PlaybackState from every player supported by the ExternalMediaPlayer.
rapidjson::Value players(rapidjson::kArrayType);
for (const auto& adapter : m_adapters) {
if (!adapter.second) {
continue;
}
rapidjson::Value playerJson = buildPlaybackState(adapter.second->getState().playbackState, stateAlloc);
players.PushBack(playerJson, stateAlloc);
}
state.AddMember(PLAYERS, players, stateAlloc);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
if (!state.Accept(writer)) {
ACSDK_ERROR(LX("providePlaybackState").d("reason", "writerRefusedJsonObject"));
return "";
}
return buffer.GetString();
}
/*
* SpeakerInterface.
*/
bool ExternalMediaPlayer::setVolume(int8_t volume) {
if (volume > avsCommon::avs::speakerConstants::AVS_SET_VOLUME_MAX ||
volume < avsCommon::avs::speakerConstants::AVS_SET_VOLUME_MIN) {
ACSDK_ERROR(LX("setVolumeFailed").d("reason", "invalid volume value").d("value", volume));
return false;
}
m_speakerSettings.volume = volume;
for (auto& adapter : m_adapters) {
if (!adapter.second) {
continue;
}
adapter.second->handleSetVolume(volume);
}
return true;
}
bool ExternalMediaPlayer::adjustVolume(int8_t volume) {
if (volume > avsCommon::avs::speakerConstants::AVS_ADJUST_VOLUME_MAX ||
volume < avsCommon::avs::speakerConstants::AVS_ADJUST_VOLUME_MIN) {
ACSDK_ERROR(LX("adjustVolumeFailed").d("reason", "invalid volume value").d("value", volume));
return false;
}
// TODO: Replace int variables here to the actual volume time when int8_t would be changed to it
int newVolume = m_speakerSettings.volume + volume;
newVolume = std::min(newVolume, (int)speakerConstants::AVS_SET_VOLUME_MAX);
newVolume = std::max(newVolume, (int)speakerConstants::AVS_SET_VOLUME_MIN);
m_speakerSettings.volume = newVolume;
for (auto& adapter : m_adapters) {
if (!adapter.second) {
continue;
}
adapter.second->handleSetVolume((int8_t)newVolume);
}
return true;
}
bool ExternalMediaPlayer::setMute(bool mute) {
m_speakerSettings.mute = mute;
for (auto& adapter : m_adapters) {
if (!adapter.second) {
continue;
}
adapter.second->handleSetMute(mute);
}
return true;
}
bool ExternalMediaPlayer::getSpeakerSettings(avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings* settings) {
if (!settings) {
ACSDK_ERROR(LX("getSpeakerSettingsFailed").d("reason", "nullSettings"));
return false;
}
settings->mute = m_speakerSettings.mute;
settings->volume = m_speakerSettings.volume;
return true;
}
avsCommon::sdkInterfaces::SpeakerInterface::Type ExternalMediaPlayer::getSpeakerType() {
return SpeakerInterface::Type::AVS_SYNCED;
}
void ExternalMediaPlayer::createAdapters(
const AdapterMediaPlayerMap& mediaPlayers,
const AdapterCreationMap& adapterCreationMap,
std::shared_ptr<MessageSenderInterface> messageSender,
std::shared_ptr<FocusManagerInterface> focusManager,
std::shared_ptr<ContextManagerInterface> contextManager) {
ACSDK_DEBUG0(LX("createAdapters"));
for (auto& entry : adapterCreationMap) {
auto mediaPlayerIt = mediaPlayers.find(entry.first);
if (mediaPlayerIt == mediaPlayers.end()) {
ACSDK_ERROR(LX("adapterCreationFailed").d("playerId", entry.first).d("reason", "nullMediaPlayer"));
continue;
}
auto adapter = entry.second(
(*mediaPlayerIt).second, m_speakerManager, messageSender, focusManager, contextManager, shared_from_this());
if (adapter) {
m_adapters[entry.first] = adapter;
} else {
ACSDK_ERROR(LX("adapterCreationFailed").d("playerId", entry.first));
}
}
}
} // namespace externalMediaPlayer
} // namespace capabilityAgents
} // namespace alexaClientSDK