/* * 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 #include #include #include #include #include #include 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> 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 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::create( const AdapterMediaPlayerMap& mediaPlayers, const AdapterCreationMap& adapterCreationMap, std::shared_ptr speakerManager, std::shared_ptr messageSender, std::shared_ptr focusManager, std::shared_ptr contextManager, std::shared_ptr exceptionSender, std::shared_ptr 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( 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 speakerManager, std::shared_ptr contextManager, std::shared_ptr exceptionSender, std::shared_ptr 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 directive) { handleDirective(std::make_shared(directive, nullptr)); } void ExternalMediaPlayer::preHandleDirective(std::shared_ptr info) { } bool ExternalMediaPlayer::parseDirectivePayload(std::shared_ptr 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 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 ExternalMediaPlayer::preprocessDirective( std::shared_ptr 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 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 info, RequestType request) { rapidjson::Document payload; auto adapter = preprocessDirective(info, &payload); if (!adapter) { return; } setHandlingCompleted(info); adapter->handleLogout(); } void ExternalMediaPlayer::handlePlay(std::shared_ptr 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 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 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 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 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 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 info) { if (info && info->result) { info->result->setCompleted(); } removeDirective(info); } void ExternalMediaPlayer::sendExceptionEncounteredAndReportFailed( std::shared_ptr 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 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 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 messageSender, std::shared_ptr focusManager, std::shared_ptr 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