2017-05-26 23:06:14 +00:00
|
|
|
/*
|
2018-01-12 23:45:42 +00:00
|
|
|
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
2017-05-26 23:06:14 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <chrono>
|
|
|
|
#include <condition_variable>
|
|
|
|
#include <fstream>
|
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
|
|
|
#include <thread>
|
2017-10-02 22:59:05 +00:00
|
|
|
#include <unordered_map>
|
2017-05-26 23:06:14 +00:00
|
|
|
|
|
|
|
#include <gmock/gmock.h>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
2018-05-04 22:45:54 +00:00
|
|
|
#include <AVSCommon/AVS/Initialization/AlexaClientSDKInit.h>
|
2017-10-30 22:14:38 +00:00
|
|
|
#include <AVSCommon/AVS/SpeakerConstants/SpeakerConstants.h>
|
2018-05-04 22:45:54 +00:00
|
|
|
#include <AVSCommon/Utils/Configuration/ConfigurationNode.h>
|
2017-06-23 23:26:34 +00:00
|
|
|
#include <AVSCommon/Utils/Logger/Logger.h>
|
|
|
|
#include <AVSCommon/Utils/Memory/Memory.h>
|
2017-10-02 22:59:05 +00:00
|
|
|
#include <PlaylistParser/PlaylistParser.h>
|
2017-05-26 23:06:14 +00:00
|
|
|
|
|
|
|
#include "MediaPlayer/MediaPlayer.h"
|
2017-10-30 22:14:38 +00:00
|
|
|
#include "AVSCommon/Utils/MediaPlayer/MediaPlayerObserverInterface.h"
|
2017-05-26 23:06:14 +00:00
|
|
|
|
|
|
|
namespace alexaClientSDK {
|
|
|
|
namespace mediaPlayer {
|
2017-06-09 23:23:31 +00:00
|
|
|
namespace test {
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2018-05-04 22:45:54 +00:00
|
|
|
using namespace avsCommon::avs;
|
2017-05-26 23:06:14 +00:00
|
|
|
using namespace avsCommon::avs::attachment;
|
2017-10-30 22:14:38 +00:00
|
|
|
using namespace avsCommon::avs::speakerConstants;
|
|
|
|
using namespace avsCommon::sdkInterfaces;
|
|
|
|
using namespace avsCommon::utils::mediaPlayer;
|
2017-06-23 23:26:34 +00:00
|
|
|
using namespace avsCommon::utils::memory;
|
2017-05-26 23:06:14 +00:00
|
|
|
using namespace ::testing;
|
|
|
|
|
2017-06-23 23:26:34 +00:00
|
|
|
/// String to identify log entries originating from this file.
|
|
|
|
static const std::string TAG("MediaPlayerTest");
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
static const MediaPlayer::SourceId ERROR_SOURCE_ID = MediaPlayer::ERROR;
|
|
|
|
|
2017-06-23 23:26:34 +00:00
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/// The path to the input Dir containing the test audio files.
|
|
|
|
std::string inputsDirPath;
|
|
|
|
|
|
|
|
/// MP3 test file path.
|
|
|
|
static const std::string MP3_FILE_PATH("/fox_dog.mp3");
|
|
|
|
|
2017-10-02 22:59:05 +00:00
|
|
|
static const std::string TEST_M3U_PLAYLIST_URL("fox_dog_playlist.m3u");
|
|
|
|
|
|
|
|
static std::string TEST_M3U_PLAYLIST_CONTENT;
|
2017-08-08 00:04:43 +00:00
|
|
|
|
|
|
|
/// file URI Prefix
|
|
|
|
static const std::string FILE_PREFIX("file://");
|
|
|
|
|
2017-09-19 22:08:49 +00:00
|
|
|
/// File length for the MP3 test file.
|
|
|
|
static const std::chrono::milliseconds MP3_FILE_LENGTH(2688);
|
|
|
|
|
|
|
|
// setOffset timing constants.
|
|
|
|
|
|
|
|
/// Offset to start playback at.
|
|
|
|
static const std::chrono::milliseconds OFFSET(2000);
|
|
|
|
|
2018-05-04 22:45:54 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
static const std::string MEDIA_PLAYER_CONFIG = R"({
|
|
|
|
"gstreamerMediaPlayer":{
|
|
|
|
"audioSink":"directsoundsink"
|
|
|
|
}
|
|
|
|
})";
|
|
|
|
#endif
|
|
|
|
|
2017-09-19 22:08:49 +00:00
|
|
|
/// Tolerance when setting expectations.
|
Version 1.8 alexa-client-sdk
Changes in this update:
Enhancements
Added local stop functionality. This allows a user to stop an active function, such as an alert or timer, by uttering "Alexa, stop" when an Alexa-enabled product is offline.
Alerts in the background now stream in 10 sec intervals, rather than continuously.
Added support for France to the sample app.
friendlyName can now be updated for BlueZ implementations of BlueZBluetoothDevice and BlueZHostController.
Bug Fixes
Fixed an issue where the Bluetooth agent didn't clear user data upon reset, including paired devices and the uuidMapping table.
Fixed MediaPlayer threading issues. Now each instance has it's own glib main loop thread, rather than utilizing the default main context worker thread.
Fixed segmentation fault issues that occurred when certain static initializers needed to be initialized in a certain order, but the order wasn't defined.
Known Issues
The ACL may encounter issues if audio attachments are received but not consumed.
SpeechSynthesizerState currently uses GAINING_FOCUS and LOSING_FOCUS as a workaround for handling intermediate state. These states may be removed in a future release.
The Alexa app doesn't always indicate when a device is successfully connected via Bluetooth.
Connecting a product to streaming media via Bluetooth will sometimes stop media playback within the source application. Resuming playback through the source application or toggling next/previous will correct playback.
When a source device is streaming silence via Bluetooth, the Alexa companion app indicates that audio content is streaming.
The Bluetooth agent assumes that the Bluetooth adapter is always connected to a power source. Disconnecting from a power source during operation is not yet supported.
On some products, interrupted Bluetooth playback may not resume if other content is locally streamed.
On Raspberry Pi, when streaming audio via Bluetooth, sometimes the audio stream stutters.
On Raspberry Pi, BlueALSA must be terminated each time the device boots. See Raspberry Pi Quick Start Guide for more information.
2018-06-27 21:41:15 +00:00
|
|
|
static const std::chrono::milliseconds TOLERANCE(500);
|
2018-01-12 23:45:42 +00:00
|
|
|
|
2017-09-19 22:08:49 +00:00
|
|
|
/// Padding to add to offsets when necessary.
|
|
|
|
static const std::chrono::milliseconds PADDING(10);
|
|
|
|
|
2017-10-02 22:59:05 +00:00
|
|
|
static std::unordered_map<std::string, std::string> urlsToContentTypes;
|
|
|
|
|
|
|
|
static std::unordered_map<std::string, std::string> urlsToContent;
|
|
|
|
|
|
|
|
/// A mock content fetcher
|
|
|
|
class MockContentFetcher : public avsCommon::sdkInterfaces::HTTPContentFetcherInterface {
|
|
|
|
public:
|
|
|
|
MockContentFetcher(const std::string& url) : m_url{url} {
|
|
|
|
}
|
|
|
|
|
2018-03-09 00:55:39 +00:00
|
|
|
std::unique_ptr<avsCommon::utils::HTTPContent> getContent(
|
|
|
|
FetchOptions fetchOption,
|
Version 1.11 alexa-client-sdk
Changes in this update:
**Enhancements**
* Added support for the new Alexa [DoNotDisturb](https://developer.amazon.com/docs/alexa-voice-service/donotdisturb.html) interface, which enables users to toggle the do not disturb (DND) function on their Alexa built-in products.
* The SDK now supports [Opus](https://opus-codec.org/license/) encoding, which is optional. To enable Opus, you must [set the CMake flag to `-DOPUS=ON`](https://github.com/alexa/avs-device-sdk/wiki/Build-Options#Opus-encoding), and include the [libopus library](https://github.com/alexa/avs-device-sdk/wiki/Dependencies#core-dependencies) dependency in your build.
* The MediaPlayer reference implementation has been expanded to support the SAMPLE-AES and AES-128 encryption methods for HLS streaming.
* AES-128 encryption is dependent on libcrypto, which is part of the required openSSL library, and is enabled by default.
* To enable [SAMPLE-AES](https://github.com/alexa/avs-device-sdk/wiki/Dependencies#core-dependencies/Enable-SAMPLE-AES-decryption) encryption, you must set the `-DSAMPLE_AES=ON` in your CMake command, and include the [FFMPEG](https://github.com/alexa/avs-device-sdk/wiki/Dependencies#core-dependencies/Enable-SAMPLE-AES-decryption) library dependency in your build.
* A new configuration for [deviceSettings](https://github.com/alexa/avs-device-sdk/blob/v1.11.0/Integration/AlexaClientSDKConfig.json#L59) has been added.This configuration allows you to specify the location of the device settings database.
* Added locale support for es-MX.
**Bug Fixes**
* Fixed an issue where music wouldn't resume playback in the Android app.
* Now all equalizer capabilities are fully disabled when equalizer is turned off in configuration file. Previously, devices were unconditionally required to provide support for equalizer in order to run the SDK.
* [Issue 1106](https://github.com/alexa/avs-device-sdk/issues/1106) - Fixed an issue in which the `CBLAuthDelegate` wasn't using the correct timeout during request refresh.
* [Issue 1128](https://github.com/alexa/avs-device-sdk/issues/1128) - Fixed an issue in which the `AudioPlayer` instance persisted at shutdown, due to a shared dependency with the `ProgressTimer`.
* Fixed in issue that occurred when a connection to streaming content was interrupted, the SDK did not attempt to resume the connection, and appeared to assume that the content had been fully downloaded. This triggered the next track to be played, assuming it was a playlist.
* [Issue 1040](https://github.com/alexa/avs-device-sdk/issues/1040) - Fixed an issue where alarms would continue to play after user barge-in.
**Known Issues**
* `PlaylistParser` and `IterativePlaylistParser` generate two HTTP requests (one to fetch the content type, and one to fetch the audio data) for each audio stream played.
* Music playback history isn't being displayed in the Alexa app for certain account and device types.
* On GCC 8+, issues related to `-Wclass-memaccess` will trigger warnings. However, this won't cause the build to fail, and these warnings can be ignored.
* In order to use Bluetooth source and sink PulseAudio, you must manually load and unload PulseAudio modules after the SDK starts.
* The `ACL` may encounter issues if audio attachments are received but not consumed.
* `SpeechSynthesizerState` currently uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate state. These states may be removed in a future release.
* The Alexa app doesn't always indicate when a device is successfully connected via Bluetooth.
* Connecting a product to streaming media via Bluetooth will sometimes stop media playback within the source application. Resuming playback through the source application or toggling next/previous will correct playback.
* When a source device is streaming silence via Bluetooth, the Alexa app indicates that audio content is streaming.
* The Bluetooth agent assumes that the Bluetooth adapter is always connected to a power source. Disconnecting from a power source during operation is not yet supported.
* On some products, interrupted Bluetooth playback may not resume if other content is locally streamed.
* `make integration` is currently not available for Android. In order to run integration tests on Android, you'll need to manually upload the test binary file along with any input file. At that point, the adb can be used to run the integration tests.
* On Raspberry Pi running Android Things with HDMI output audio, beginning of speech is truncated when Alexa responds to user TTS.
* When the sample app is restarted and network connection is lost, alerts don't play.
* When network connection is lost, lost connection status is not returned via local Text-to Speech (TTS).
2018-12-19 19:13:36 +00:00
|
|
|
std::shared_ptr<avsCommon::avs::attachment::AttachmentWriter> writer,
|
|
|
|
const std::vector<std::string>& customHeaders = std::vector<std::string>()) override {
|
2017-10-02 22:59:05 +00:00
|
|
|
if (fetchOption == FetchOptions::CONTENT_TYPE) {
|
|
|
|
auto urlAndContentType = urlsToContentTypes.find(m_url);
|
|
|
|
if (urlAndContentType == urlsToContentTypes.end()) {
|
|
|
|
return nullptr;
|
|
|
|
} else {
|
|
|
|
std::promise<long> statusPromise;
|
|
|
|
auto statusFuture = statusPromise.get_future();
|
|
|
|
statusPromise.set_value(200);
|
|
|
|
std::promise<std::string> contentTypePromise;
|
|
|
|
auto contentTypeFuture = contentTypePromise.get_future();
|
|
|
|
contentTypePromise.set_value(urlAndContentType->second);
|
|
|
|
return avsCommon::utils::memory::make_unique<avsCommon::utils::HTTPContent>(
|
Version 1.10 alexa-client-sdk
Changes in this update:
**Enhancements**
* New optional configuration for [EqualizerController](https://github.com/alexa/avs-device-sdk/blob/v1.10.0/Integration/AlexaClientSDKConfig.json#L154). The EqualizerController interface allows you to adjust equalizer settings on your product, such as decibel (dB) levels and modes.
* Added reference implementation of the EqualizerController for GStreamer-based (MacOS, Linux, and Raspberry Pi) and OpenSL ES-based (Android) MediaPlayers. Note: In order to use with Android, it must support OpenSL ES.
* New optional configuration for the [TemplateRuntime display card value](https://github.com/alexa/avs-device-sdk/blob/v1.10.0/Integration/AlexaClientSDKConfig.json#L144).
* A configuration file generator script, `genConfig.sh` is now included with the SDK in the **tools/Install** directory. `genConfig.sh` and it's associated arguments populate `AlexaClientSDKConfig.json` with the data required to authorize with LWA.
* Added Bluetooth A2DP source and AVRCP target support for Linux.
* Added Amazon for Business (A4B) support, which includes support for handling the new [RevokeAuthorization](https://developer.amazon.com/docs/alexa-voice-service/system.html#revokeauth) directive in the Settings interface. A new CMake option has been added to enable A4B within the SDK, `-DA4B`.
* Added locale support for IT and ES.
* The Alexa Communication Library (ACL), `CBLAUthDelegate`, and sample app have been enhanced to detect de-authorization using the new `z` command.
* Added `ExternalMediaPlayerObserver`, which receives notification of player state, track, and username changes.
* `HTTP2ConnectionInterface` was factored out of `HTTP2Transport` to enable unit testing of `HTTP2Transport` and re-use of `HTTP2Connection` logic.
**Bug Fixes**
* Fixed a bug in which `ExternalMediaPlayer` adapter playback wasn't being recognized by AVS.
* [Issue 973](https://github.com/alexa/avs-device-sdk/issues/973) - Fixed issues related to `AudioPlayer` where progress reports were being sent out of order or with incorrect offsets.
* An `EXPECTING`, state has been added to `DialogUXState` in order to handle `EXPECT_SPEECH` state for hold-to-talk devices.
* [Issue 948](https://github.com/alexa/avs-device-sdk/issues/948) - Fixed a bug in which the sample app was stuck in a listening state.
* Fixed a bug where there was a delay between receiving a `DeleteAlert` directive, and deleting the alert.
* [Issue 839](https://github.com/alexa/avs-device-sdk/issues/839) - Fixed an issue where speech was being truncated due to the `DialogUXStateAggregator` transitioning between a `THINKING` and `IDLE` state.
* Fixed a bug in which the `AudioPlayer` attempted to play when it wasn't in the `FOREGROUND` focus.
* `CapabilitiesDelegateTest` now works on Android.
* [Issue 950](https://github.com/alexa/avs-device-sdk/issues/950) - Improved Android Media Player audio quality.
* [Issue 908](https://github.com/alexa/avs-device-sdk/issues/908) - Fixed compile error on g++ 7.x in which includes were missing.
2018-10-24 17:01:29 +00:00
|
|
|
std::move(statusFuture), std::move(contentTypeFuture), nullptr);
|
2017-10-02 22:59:05 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
auto urlAndContent = urlsToContent.find(m_url);
|
|
|
|
if (urlAndContent == urlsToContent.end()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
std::promise<long> statusPromise;
|
|
|
|
auto statusFuture = statusPromise.get_future();
|
|
|
|
statusPromise.set_value(200);
|
|
|
|
std::promise<std::string> contentTypePromise;
|
|
|
|
auto contentTypeFuture = contentTypePromise.get_future();
|
|
|
|
contentTypePromise.set_value("");
|
2018-03-09 00:55:39 +00:00
|
|
|
auto attachment = writeStringIntoAttachment(urlAndContent->second, writer);
|
2017-10-02 22:59:05 +00:00
|
|
|
if (!attachment) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return avsCommon::utils::memory::make_unique<avsCommon::utils::HTTPContent>(
|
Version 1.10 alexa-client-sdk
Changes in this update:
**Enhancements**
* New optional configuration for [EqualizerController](https://github.com/alexa/avs-device-sdk/blob/v1.10.0/Integration/AlexaClientSDKConfig.json#L154). The EqualizerController interface allows you to adjust equalizer settings on your product, such as decibel (dB) levels and modes.
* Added reference implementation of the EqualizerController for GStreamer-based (MacOS, Linux, and Raspberry Pi) and OpenSL ES-based (Android) MediaPlayers. Note: In order to use with Android, it must support OpenSL ES.
* New optional configuration for the [TemplateRuntime display card value](https://github.com/alexa/avs-device-sdk/blob/v1.10.0/Integration/AlexaClientSDKConfig.json#L144).
* A configuration file generator script, `genConfig.sh` is now included with the SDK in the **tools/Install** directory. `genConfig.sh` and it's associated arguments populate `AlexaClientSDKConfig.json` with the data required to authorize with LWA.
* Added Bluetooth A2DP source and AVRCP target support for Linux.
* Added Amazon for Business (A4B) support, which includes support for handling the new [RevokeAuthorization](https://developer.amazon.com/docs/alexa-voice-service/system.html#revokeauth) directive in the Settings interface. A new CMake option has been added to enable A4B within the SDK, `-DA4B`.
* Added locale support for IT and ES.
* The Alexa Communication Library (ACL), `CBLAUthDelegate`, and sample app have been enhanced to detect de-authorization using the new `z` command.
* Added `ExternalMediaPlayerObserver`, which receives notification of player state, track, and username changes.
* `HTTP2ConnectionInterface` was factored out of `HTTP2Transport` to enable unit testing of `HTTP2Transport` and re-use of `HTTP2Connection` logic.
**Bug Fixes**
* Fixed a bug in which `ExternalMediaPlayer` adapter playback wasn't being recognized by AVS.
* [Issue 973](https://github.com/alexa/avs-device-sdk/issues/973) - Fixed issues related to `AudioPlayer` where progress reports were being sent out of order or with incorrect offsets.
* An `EXPECTING`, state has been added to `DialogUXState` in order to handle `EXPECT_SPEECH` state for hold-to-talk devices.
* [Issue 948](https://github.com/alexa/avs-device-sdk/issues/948) - Fixed a bug in which the sample app was stuck in a listening state.
* Fixed a bug where there was a delay between receiving a `DeleteAlert` directive, and deleting the alert.
* [Issue 839](https://github.com/alexa/avs-device-sdk/issues/839) - Fixed an issue where speech was being truncated due to the `DialogUXStateAggregator` transitioning between a `THINKING` and `IDLE` state.
* Fixed a bug in which the `AudioPlayer` attempted to play when it wasn't in the `FOREGROUND` focus.
* `CapabilitiesDelegateTest` now works on Android.
* [Issue 950](https://github.com/alexa/avs-device-sdk/issues/950) - Improved Android Media Player audio quality.
* [Issue 908](https://github.com/alexa/avs-device-sdk/issues/908) - Fixed compile error on g++ 7.x in which includes were missing.
2018-10-24 17:01:29 +00:00
|
|
|
std::move(statusFuture), std::move(contentTypeFuture), attachment);
|
2017-10-02 22:59:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::shared_ptr<avsCommon::avs::attachment::InProcessAttachment> writeStringIntoAttachment(
|
2018-03-09 00:55:39 +00:00
|
|
|
const std::string& string,
|
|
|
|
std::shared_ptr<avsCommon::avs::attachment::AttachmentWriter> writer) {
|
2017-10-02 22:59:05 +00:00
|
|
|
static int id = 0;
|
|
|
|
std::shared_ptr<avsCommon::avs::attachment::InProcessAttachment> stream =
|
|
|
|
std::make_shared<avsCommon::avs::attachment::InProcessAttachment>(std::to_string(id++));
|
|
|
|
if (!stream) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2018-03-09 00:55:39 +00:00
|
|
|
|
2017-10-02 22:59:05 +00:00
|
|
|
if (!writer) {
|
2018-03-09 00:55:39 +00:00
|
|
|
writer = stream->createWriter();
|
2017-10-02 22:59:05 +00:00
|
|
|
}
|
|
|
|
avsCommon::avs::attachment::AttachmentWriter::WriteStatus writeStatus;
|
|
|
|
writer->write(string.data(), string.size(), &writeStatus);
|
|
|
|
return stream;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::string m_url;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A mock factory that creates mock content fetchers
|
|
|
|
class MockContentFetcherFactory : public avsCommon::sdkInterfaces::HTTPContentFetcherInterfaceFactoryInterface {
|
|
|
|
std::unique_ptr<avsCommon::sdkInterfaces::HTTPContentFetcherInterface> create(const std::string& url) {
|
|
|
|
return avsCommon::utils::memory::make_unique<MockContentFetcher>(url);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/**
|
|
|
|
* Mock AttachmentReader.
|
|
|
|
*/
|
|
|
|
class MockAttachmentReader : public AttachmentReader {
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
*
|
|
|
|
* @param iterations The number of times this AttachmentReader will (re)read the input file before @c read
|
|
|
|
* will return a @c CLOSED status.
|
|
|
|
* @param receiveSizes An vector of sizes (in bytes) that this @c AttachmentReceiver will simulate receiving.
|
|
|
|
* Each successive element in the vector corresponds to a successive 100 millisecond interval starting from
|
|
|
|
* the time this @c MockAttachmentReader was created.
|
|
|
|
*/
|
|
|
|
MockAttachmentReader(int iterations = 1, std::vector<size_t> receiveSizes = {std::numeric_limits<size_t>::max()});
|
|
|
|
|
2017-10-02 22:59:05 +00:00
|
|
|
size_t read(void* buf, std::size_t numBytes, ReadStatus* readStatus, std::chrono::milliseconds timeoutMs) override;
|
2017-05-26 23:06:14 +00:00
|
|
|
|
|
|
|
void close(ClosePoint closePoint) override;
|
|
|
|
|
2017-12-09 00:07:37 +00:00
|
|
|
bool seek(uint64_t offset) override {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-02-12 23:31:53 +00:00
|
|
|
uint64_t getNumUnreadBytes() override {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/**
|
|
|
|
* Receive bytes from the test file.
|
|
|
|
*
|
|
|
|
* @param buf The buffer to receive the bytes.
|
|
|
|
* @param size The number of bytes to receive.
|
|
|
|
* @return The number of bytes received.
|
|
|
|
*/
|
|
|
|
size_t receiveBytes(char* buf, std::size_t size);
|
|
|
|
|
|
|
|
/// The number of iterations of reading the input file that are left before this reader returns closed.
|
|
|
|
int m_iterationsLeft;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The total number of bytes that are supposed to have been received (and made available) by this
|
|
|
|
* @c AttachmentReader at 100 millisecond increments from @c m_startTime.
|
|
|
|
*/
|
|
|
|
std::vector<size_t> m_receiveTotals;
|
|
|
|
|
|
|
|
/// The start of time for reading from this AttachmentReader.
|
|
|
|
std::chrono::steady_clock::time_point m_startTime;
|
|
|
|
|
|
|
|
/// The number of bytes returned so far by @c read().
|
|
|
|
size_t m_totalRead;
|
|
|
|
|
|
|
|
/// The current ifstream (if any) from which to read the attachment.
|
|
|
|
std::unique_ptr<std::ifstream> m_stream;
|
|
|
|
};
|
|
|
|
|
|
|
|
MockAttachmentReader::MockAttachmentReader(int iterations, std::vector<size_t> receiveSizes) :
|
|
|
|
m_iterationsLeft{iterations},
|
|
|
|
m_startTime(std::chrono::steady_clock::now()),
|
|
|
|
m_totalRead{0} {
|
|
|
|
// Convert human friendly vector of received sizes in to a vector of received totals.
|
|
|
|
EXPECT_GT(receiveSizes.size(), 0U);
|
|
|
|
m_receiveTotals.reserve(receiveSizes.size());
|
|
|
|
size_t total = 0;
|
|
|
|
for (size_t ix = 0; ix < receiveSizes.size(); ix++) {
|
|
|
|
total += receiveSizes[ix];
|
|
|
|
m_receiveTotals.push_back(total);
|
|
|
|
}
|
|
|
|
EXPECT_EQ(m_receiveTotals.size(), receiveSizes.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t MockAttachmentReader::read(
|
2017-10-02 22:59:05 +00:00
|
|
|
void* buf,
|
|
|
|
std::size_t numBytes,
|
|
|
|
ReadStatus* readStatus,
|
|
|
|
std::chrono::milliseconds timeoutMs) {
|
2017-05-26 23:06:14 +00:00
|
|
|
// Convert the current time in to an index in to m_receivedTotals (time since @c m_startTime
|
|
|
|
// divided by 100 millisecond intervals).
|
|
|
|
auto now = std::chrono::steady_clock::now();
|
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_startTime);
|
|
|
|
auto index = static_cast<size_t>(elapsed.count() / 100);
|
|
|
|
index = std::min(index, m_receiveTotals.size() - 1);
|
|
|
|
|
|
|
|
// Use the index to find the total number of bytes received by the @c MockAttachmentReader so far. Then subtract
|
|
|
|
// the number of bytes consumed by @c read() so far to calculate the number of bytes available for @c read().
|
|
|
|
auto receivedTotal = m_receiveTotals[index];
|
|
|
|
EXPECT_LE(m_totalRead, receivedTotal);
|
|
|
|
auto available = receivedTotal - m_totalRead;
|
|
|
|
|
|
|
|
// Use the avaialable number of bytes to calculate how many bytes @c read() should return and the status
|
|
|
|
// that should accompany them. Also perform the actual read in to the output buffer.
|
|
|
|
size_t result = 0;
|
|
|
|
auto status = ReadStatus::ERROR_INTERNAL;
|
|
|
|
if (available > 0) {
|
|
|
|
auto sizeToRead = std::min(available, numBytes);
|
2017-10-02 22:59:05 +00:00
|
|
|
result = receiveBytes(static_cast<char*>(buf), sizeToRead);
|
2017-05-26 23:06:14 +00:00
|
|
|
if (result > 0) {
|
|
|
|
m_totalRead += result;
|
|
|
|
status = (result == numBytes) ? ReadStatus::OK : ReadStatus::OK_WOULDBLOCK;
|
|
|
|
} else {
|
|
|
|
status = ReadStatus::CLOSED;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
status = ReadStatus::OK_WOULDBLOCK;
|
|
|
|
}
|
|
|
|
if (readStatus) {
|
|
|
|
*readStatus = status;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MockAttachmentReader::close(ClosePoint closePoint) {
|
|
|
|
if (m_stream) {
|
|
|
|
m_stream->close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t MockAttachmentReader::receiveBytes(char* buf, std::size_t size) {
|
|
|
|
auto pos = buf;
|
|
|
|
auto end = buf + size;
|
|
|
|
while (pos < end) {
|
|
|
|
if (!m_stream || m_stream->eof()) {
|
|
|
|
if (m_iterationsLeft-- > 0) {
|
Version 1.7.0 of the avs-device-sdk
Changes in this update:
**Enhancements**
* `AuthDelegate` and `AuthServer.py` have been replaced by `CBLAUthDelegate`, which provides a more straightforward path to authorization.
* Added a new configuration property called [`cblAuthDelegate`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L2). This object specifies parameters for `CBLAuthDelegate`.
* Added a new configuration property called [`miscDatabase`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L34), which is a generic key/value database to be used by various components.
* Added a new configuration property called [`dcfDelegate`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L17) This object specifies parameters for `DCFDelegate`. Within this object, values were added for the 'endpoint' and `overridenDcfPublishMessageBody`. 'endpoint' is the endpoint to connect to in order to send device capabilities. `overridenDcfPublishMessageBody`is the message that will get sent out to the Capabilities API. Note: values within the `dcfDelegate` object will only work in `DEBUG` builds.
* Added a new configuration property called [`deviceInfo`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L9) which specifies device-identifying information for use by the Device Capability Framework (DCF), and for authorization (CBLAuthDelegate).
* Updated the Directive Sequencer to support wildcard directive handlers. This allows a handler for a given AVS interface to register at the namespace level, rather than specifying the names of all directives within that namespace.
* Updated the Raspberry Pi installation script to include `alsasink` in the configuration file.
* Added `audioSink` as a configuration option. This allows users to override the audio sink element used in `Gstreamer`.
* Added an interface for monitoring internet connection status: `InternetConnectionMonitorInterface.h`.
* The Alexa Communications Library (ACL) is no longer required to wait until authorization has succeeded before attempting to connect to AVS. Instead, `HTTP2Transport` handles waiting for authorization to complete.
* Added the Device Capabilities Framework (DCF) delegate. Device capabilities can now be sent for each capability interface using DCF publish messages.
* The sample app has been updated to send DCF publish messages, which will automatically occur when the sample app starts. Note: a DCF publish message must be successfully sent in order for communication with AVS to occur.
* The SDK now supports HTTP PUT messages.
* Added support for opt-arg style arguments and multiple configuration files. Now, the sample app can be invoked by either of these commands: `SampleApp <configfile> <debuglevel>` OR `SampleApp -C file1 -C file2 ... -L loglevel`.
**Bug Fixes**
* Issues [447](https://github.com/alexa/avs-device-sdk/issues/447) and [553](https://github.com/alexa/avs-device-sdk/issues/553) Fixed the `AttachmentRenderSource`'s handling of `BLOCKING` `AttachmentReaders`.
* Updated the `Logger` implementation to be more resilient to `nullptr` string inputs.
* Fixed a `TimeUtils` utility-related compile issue.
* Fixed a bug in which alerts failed to activate if the system was restarted without network connection.
* Fixed Android 64-bit build failure issue.
**Known Issues**
* The `ACL` may encounter issues if audio attachments are received but not consumed.
* `SpeechSynthesizerState` currently uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate state. These states may be removed in a future release.
* Some ERROR messages may be printed during start-up event if initialization proceeds normally and successfully.
* If an unrecoverable authorization error or an unrecoverable DCF error is encountered, the sample app may crash on shutdown.
* If a non-CBL `clientId` is included in the `deviceInfo` section of `AlexaClientSDKConfig.json`, the error will be reported as an unrecoverable authorization error, rather than a more specific error.
2018-04-18 22:17:28 +00:00
|
|
|
m_stream = make_unique<std::ifstream>(inputsDirPath + MP3_FILE_PATH, std::ifstream::binary);
|
2017-05-26 23:06:14 +00:00
|
|
|
EXPECT_TRUE(m_stream);
|
|
|
|
EXPECT_TRUE(m_stream->good());
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_stream->read(pos, end - pos);
|
|
|
|
pos += m_stream->gcount();
|
|
|
|
}
|
|
|
|
auto result = pos - buf;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-10-02 22:59:05 +00:00
|
|
|
class MockPlayerObserver : public MediaPlayerObserverInterface {
|
2017-05-26 23:06:14 +00:00
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Destructor.
|
|
|
|
*/
|
2017-10-02 22:59:05 +00:00
|
|
|
~MockPlayerObserver(){};
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
// void onPlaybackStarted() override;
|
|
|
|
void onPlaybackStarted(SourceId id) override;
|
|
|
|
|
|
|
|
void onPlaybackFinished(SourceId id) override;
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void onPlaybackError(SourceId id, const ErrorType& type, std::string error) override;
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void onPlaybackPaused(SourceId id) override;
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void onPlaybackResumed(SourceId id) override;
|
2017-08-08 00:04:43 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void onPlaybackStopped(SourceId id) override;
|
|
|
|
|
|
|
|
void onTags(SourceId id, std::unique_ptr<const VectorOfTags> vectorOfTags) override;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait for a message to be received.
|
|
|
|
*
|
|
|
|
* This function waits for a specified number of milliseconds for a message to arrive.
|
|
|
|
* @param id The @c SourceId expected in a callback.
|
|
|
|
* @param duration Number of milliseconds to wait before giving up.
|
|
|
|
* @return true if a message was received within the specified duration, else false.
|
|
|
|
*/
|
|
|
|
bool waitForPlaybackStarted(
|
|
|
|
SourceId id,
|
|
|
|
const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
2017-08-08 00:04:43 +00:00
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/**
|
|
|
|
* Wait for a message to be received.
|
|
|
|
*
|
|
|
|
* This function waits for a specified number of milliseconds for a message to arrive.
|
2017-10-30 22:14:38 +00:00
|
|
|
* @param id The @c SourceId expected in a callback.
|
2017-05-26 23:06:14 +00:00
|
|
|
* @param duration Number of milliseconds to wait before giving up.
|
|
|
|
* @return true if a message was received within the specified duration, else false.
|
|
|
|
*/
|
2017-10-30 22:14:38 +00:00
|
|
|
bool waitForPlaybackFinished(
|
|
|
|
SourceId id,
|
|
|
|
const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
2017-05-26 23:06:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait for a message to be received.
|
|
|
|
*
|
|
|
|
* This function waits for a specified number of milliseconds for a message to arrive.
|
2017-10-30 22:14:38 +00:00
|
|
|
* @param id The @c SourceId expected in a callback.
|
2017-05-26 23:06:14 +00:00
|
|
|
* @param duration Number of milliseconds to wait before giving up.
|
|
|
|
* @return true if a message was received within the specified duration, else false.
|
|
|
|
*/
|
2017-10-30 22:14:38 +00:00
|
|
|
bool waitForPlaybackPaused(SourceId id, const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-08-08 00:04:43 +00:00
|
|
|
/**
|
|
|
|
* Wait for a message to be received.
|
|
|
|
*
|
|
|
|
* This function waits for a specified number of milliseconds for a message to arrive.
|
2017-10-30 22:14:38 +00:00
|
|
|
* @param id The @c SourceId expected in a callback.
|
2017-08-08 00:04:43 +00:00
|
|
|
* @param duration Number of milliseconds to wait before giving up.
|
|
|
|
* @return true if a message was received within the specified duration, else false.
|
|
|
|
*/
|
2017-10-30 22:14:38 +00:00
|
|
|
bool waitForPlaybackResumed(
|
|
|
|
SourceId id,
|
|
|
|
const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
2017-08-08 00:04:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait for a message to be received.
|
|
|
|
*
|
|
|
|
* This function waits for a specified number of milliseconds for a message to arrive.
|
2017-10-30 22:14:38 +00:00
|
|
|
* @param id The @c SourceId expected in a callback.
|
|
|
|
* @param duration Number of milliseconds to wait before giving up.
|
|
|
|
* @return true if a message was received within the specified duration, else false.
|
|
|
|
*/
|
|
|
|
bool waitForPlaybackStopped(
|
|
|
|
SourceId id,
|
|
|
|
const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
|
|
|
|
2018-01-12 23:45:42 +00:00
|
|
|
/**
|
|
|
|
* Wait for a playback error message to be received.
|
|
|
|
*
|
|
|
|
* This function waits for a specified number of milliseconds for a message to arrive.
|
|
|
|
* @param id The @c SourceId expected in a callback.
|
|
|
|
* @param duration Number of milliseconds to wait before giving up.
|
|
|
|
* @return true if a message was received within the specified duration, else false.
|
|
|
|
*/
|
|
|
|
bool waitForPlaybackError(SourceId id, const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
/**
|
|
|
|
* Wait for a message to be received.
|
|
|
|
* This function waits for a specified number of milliseconds for a message to arrive.
|
|
|
|
*
|
|
|
|
* @param id The @c SourceId expected in a callback.
|
2017-08-08 00:04:43 +00:00
|
|
|
* @param duration Number of milliseconds to wait before giving up.
|
|
|
|
* @return true if a message was received within the specified duration, else false.
|
|
|
|
*/
|
2017-10-30 22:14:38 +00:00
|
|
|
bool waitForTags(SourceId id, const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
2017-08-08 00:04:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO: Make this class a mock and remove this.
|
|
|
|
*
|
|
|
|
* This gets the number of times onPlaybackStarted was called.
|
|
|
|
*/
|
|
|
|
int getOnPlaybackStartedCallCount();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO: Make this class a mock and remove this.
|
|
|
|
*
|
|
|
|
* This gets the number of times onPlaybackFinished was called.
|
|
|
|
*/
|
|
|
|
int getOnPlaybackFinishedCallCount();
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
/**
|
|
|
|
* TODO: Make this class a mock and remove this.
|
|
|
|
*
|
|
|
|
* This gets the number of times onTags was called.
|
|
|
|
*/
|
|
|
|
int getOnTagsCallCount();
|
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
private:
|
|
|
|
/// Mutex to protect the flags @c m_playbackStarted and .@c m_playbackFinished.
|
|
|
|
std::mutex m_mutex;
|
|
|
|
/// Trigger to wake up m_wakePlaybackStarted calls.
|
|
|
|
std::condition_variable m_wakePlaybackStarted;
|
|
|
|
/// Trigger to wake up m_wakePlaybackStarted calls.
|
|
|
|
std::condition_variable m_wakePlaybackFinished;
|
2017-08-08 00:04:43 +00:00
|
|
|
/// Trigger to wake up m_wakePlaybackPaused calls.
|
|
|
|
std::condition_variable m_wakePlaybackPaused;
|
|
|
|
/// Trigger to wake up m_wakePlaybackResumed calls.
|
|
|
|
std::condition_variable m_wakePlaybackResumed;
|
2017-10-30 22:14:38 +00:00
|
|
|
std::condition_variable m_wakePlaybackStopped;
|
2018-01-12 23:45:42 +00:00
|
|
|
std::condition_variable m_wakePlaybackError;
|
2017-10-30 22:14:38 +00:00
|
|
|
/// Trigger to wake up m_wakeTags calls.
|
|
|
|
std::condition_variable m_wakeTags;
|
2017-08-08 00:04:43 +00:00
|
|
|
|
|
|
|
// TODO: Make this class a mock and remove these.
|
|
|
|
int m_onPlaybackStartedCallCount = 0;
|
|
|
|
int m_onPlaybackFinishedCallCount = 0;
|
2017-10-30 22:14:38 +00:00
|
|
|
int m_onTagsCallCount = 0;
|
2017-08-08 00:04:43 +00:00
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/// Flag to set when a playback start message is received.
|
|
|
|
bool m_playbackStarted;
|
|
|
|
/// Flag to set when a playback finished message is received.
|
|
|
|
bool m_playbackFinished;
|
2017-08-08 00:04:43 +00:00
|
|
|
/// Flag to set when a playback paused message is received.
|
|
|
|
bool m_playbackPaused;
|
|
|
|
/// Flag to set when a playback paused message is received.
|
|
|
|
bool m_playbackResumed;
|
2017-10-30 22:14:38 +00:00
|
|
|
/// Flag to set when a playback stopped message is received.
|
|
|
|
bool m_playbackStopped;
|
2018-01-12 23:45:42 +00:00
|
|
|
/// Flag to set when a playback error message is received.
|
|
|
|
bool m_playbackError;
|
2017-10-30 22:14:38 +00:00
|
|
|
/// Flag to set when a tags message is received.
|
|
|
|
bool m_tags;
|
|
|
|
|
|
|
|
SourceId m_lastId = 0;
|
2017-05-26 23:06:14 +00:00
|
|
|
};
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void MockPlayerObserver::onPlaybackStarted(SourceId id) {
|
2017-05-26 23:06:14 +00:00
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
2017-10-30 22:14:38 +00:00
|
|
|
m_lastId = id;
|
2017-05-26 23:06:14 +00:00
|
|
|
m_playbackStarted = true;
|
|
|
|
m_playbackFinished = false;
|
2017-10-30 22:14:38 +00:00
|
|
|
m_playbackStopped = false;
|
2017-05-26 23:06:14 +00:00
|
|
|
m_wakePlaybackStarted.notify_all();
|
2017-08-08 00:04:43 +00:00
|
|
|
m_onPlaybackStartedCallCount++;
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void MockPlayerObserver::onPlaybackFinished(SourceId id) {
|
2017-05-26 23:06:14 +00:00
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
2017-10-30 22:14:38 +00:00
|
|
|
m_lastId = id;
|
2017-05-26 23:06:14 +00:00
|
|
|
m_playbackFinished = true;
|
|
|
|
m_playbackStarted = false;
|
|
|
|
m_wakePlaybackFinished.notify_all();
|
2017-08-08 00:04:43 +00:00
|
|
|
m_onPlaybackFinishedCallCount++;
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void MockPlayerObserver::onPlaybackError(SourceId id, const ErrorType& type, std::string error) {
|
|
|
|
m_lastId = id;
|
2018-01-12 23:45:42 +00:00
|
|
|
m_playbackError = true;
|
|
|
|
m_wakePlaybackError.notify_all();
|
2017-05-26 23:06:14 +00:00
|
|
|
};
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void MockPlayerObserver::onPlaybackPaused(SourceId id) {
|
2017-08-08 00:04:43 +00:00
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
2017-10-30 22:14:38 +00:00
|
|
|
m_lastId = id;
|
2017-08-08 00:04:43 +00:00
|
|
|
m_playbackPaused = true;
|
|
|
|
m_wakePlaybackPaused.notify_all();
|
|
|
|
};
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void MockPlayerObserver::onPlaybackResumed(SourceId id) {
|
2017-08-08 00:04:43 +00:00
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
2017-10-30 22:14:38 +00:00
|
|
|
m_lastId = id;
|
2017-08-08 00:04:43 +00:00
|
|
|
m_playbackResumed = true;
|
|
|
|
m_playbackPaused = false;
|
|
|
|
m_wakePlaybackResumed.notify_all();
|
|
|
|
};
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void MockPlayerObserver::onPlaybackStopped(SourceId id) {
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
m_lastId = id;
|
|
|
|
m_playbackStopped = true;
|
|
|
|
m_playbackStarted = false;
|
|
|
|
m_wakePlaybackStopped.notify_all();
|
|
|
|
};
|
|
|
|
|
|
|
|
bool MockPlayerObserver::waitForPlaybackStarted(SourceId id, const std::chrono::milliseconds duration) {
|
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
|
|
if (!m_wakePlaybackStarted.wait_for(lock, duration, [this, id]() { return m_playbackStarted && id == m_lastId; })) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MockPlayerObserver::waitForPlaybackFinished(SourceId id, const std::chrono::milliseconds duration) {
|
2017-05-26 23:06:14 +00:00
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
2017-10-30 22:14:38 +00:00
|
|
|
if (!m_wakePlaybackFinished.wait_for(
|
|
|
|
lock, duration, [this, id]() { return m_playbackFinished && id == m_lastId; })) {
|
2017-05-26 23:06:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
bool MockPlayerObserver::waitForPlaybackPaused(SourceId id, const std::chrono::milliseconds duration) {
|
2017-05-26 23:06:14 +00:00
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
2017-10-30 22:14:38 +00:00
|
|
|
if (!m_wakePlaybackPaused.wait_for(lock, duration, [this, id]() { return m_playbackPaused && id == m_lastId; })) {
|
2017-05-26 23:06:14 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
bool MockPlayerObserver::waitForPlaybackResumed(SourceId id, const std::chrono::milliseconds duration) {
|
2017-08-08 00:04:43 +00:00
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
2017-10-30 22:14:38 +00:00
|
|
|
if (!m_wakePlaybackResumed.wait_for(lock, duration, [this, id]() { return m_playbackResumed && id == m_lastId; })) {
|
2017-08-08 00:04:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
bool MockPlayerObserver::waitForPlaybackStopped(SourceId id, const std::chrono::milliseconds duration) {
|
2017-08-08 00:04:43 +00:00
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
2017-10-30 22:14:38 +00:00
|
|
|
if (!m_wakePlaybackStopped.wait_for(lock, duration, [this, id]() { return m_playbackStopped && id == m_lastId; })) {
|
2017-08-08 00:04:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-01-12 23:45:42 +00:00
|
|
|
bool MockPlayerObserver::waitForPlaybackError(SourceId id, const std::chrono::milliseconds duration) {
|
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
|
|
if (!m_wakePlaybackError.wait_for(lock, duration, [this, id]() { return m_playbackError && id == m_lastId; })) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-08 00:04:43 +00:00
|
|
|
int MockPlayerObserver::getOnPlaybackStartedCallCount() {
|
|
|
|
return m_onPlaybackStartedCallCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MockPlayerObserver::getOnPlaybackFinishedCallCount() {
|
|
|
|
return m_onPlaybackFinishedCallCount;
|
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
int MockPlayerObserver::getOnTagsCallCount() {
|
|
|
|
return m_onTagsCallCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MockPlayerObserver::onTags(SourceId id, std::unique_ptr<const VectorOfTags> vectorOfTags) {
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
m_lastId = id;
|
|
|
|
m_tags = true;
|
|
|
|
m_onTagsCallCount++;
|
|
|
|
m_wakeTags.notify_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MockPlayerObserver::waitForTags(SourceId id, const std::chrono::milliseconds duration) {
|
|
|
|
std::unique_lock<std::mutex> lock(m_mutex);
|
|
|
|
if (!m_wakeTags.wait_for(lock, duration, [this, id]() { return m_tags && id == m_lastId; })) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-02 22:59:05 +00:00
|
|
|
class MediaPlayerTest : public ::testing::Test {
|
2017-05-26 23:06:14 +00:00
|
|
|
public:
|
|
|
|
void SetUp() override;
|
|
|
|
|
2018-01-12 23:45:42 +00:00
|
|
|
void TearDown() override {
|
|
|
|
m_mediaPlayer->shutdown();
|
|
|
|
}
|
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/**
|
|
|
|
* Sets the audio source to play.
|
|
|
|
*
|
|
|
|
*/
|
2017-07-18 22:25:37 +00:00
|
|
|
void setAttachmentReaderSource(
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId* id,
|
2017-10-02 22:59:05 +00:00
|
|
|
int iterations = 1,
|
|
|
|
std::vector<size_t> receiveSizes = {std::numeric_limits<size_t>::max()});
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-07-18 22:25:37 +00:00
|
|
|
/**
|
|
|
|
* Sets IStream source to play.
|
|
|
|
*
|
|
|
|
* @param repeat Whether to play the stream over and over until stopped.
|
|
|
|
*/
|
2017-10-30 22:14:38 +00:00
|
|
|
void setIStreamSource(MediaPlayer::SourceId* id, bool repeat = false);
|
2017-07-18 22:25:37 +00:00
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/// An instance of the @c MediaPlayer
|
|
|
|
std::shared_ptr<MediaPlayer> m_mediaPlayer;
|
|
|
|
|
|
|
|
/// An observer to whom the playback started and finished notifications need to be sent.
|
|
|
|
std::shared_ptr<MockPlayerObserver> m_playerObserver;
|
|
|
|
};
|
|
|
|
|
|
|
|
void MediaPlayerTest::SetUp() {
|
2018-05-04 22:45:54 +00:00
|
|
|
// ACSDK-1373 - MediaPlayerTest fail on Windows
|
|
|
|
// with GStreamer 1.14 default audio sink.
|
|
|
|
#ifdef _WIN32
|
|
|
|
auto inString = std::shared_ptr<std::istringstream>(new std::istringstream(MEDIA_PLAYER_CONFIG));
|
|
|
|
initialization::AlexaClientSDKInit::initialize({inString});
|
|
|
|
#endif
|
2017-05-26 23:06:14 +00:00
|
|
|
m_playerObserver = std::make_shared<MockPlayerObserver>();
|
2017-10-02 22:59:05 +00:00
|
|
|
m_mediaPlayer = MediaPlayer::create(std::make_shared<MockContentFetcherFactory>());
|
2017-05-26 23:06:14 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer);
|
|
|
|
m_mediaPlayer->setObserver(m_playerObserver);
|
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void MediaPlayerTest::setAttachmentReaderSource(
|
|
|
|
MediaPlayer::SourceId* id,
|
|
|
|
int iterations,
|
|
|
|
std::vector<size_t> receiveSizes) {
|
|
|
|
auto returnId =
|
|
|
|
m_mediaPlayer->setSource(std::unique_ptr<AttachmentReader>(new MockAttachmentReader(iterations, receiveSizes)));
|
|
|
|
ASSERT_NE(ERROR_SOURCE_ID, returnId);
|
|
|
|
if (id) {
|
|
|
|
*id = returnId;
|
|
|
|
}
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
void MediaPlayerTest::setIStreamSource(MediaPlayer::SourceId* id, bool repeat) {
|
Version 1.7.0 of the avs-device-sdk
Changes in this update:
**Enhancements**
* `AuthDelegate` and `AuthServer.py` have been replaced by `CBLAUthDelegate`, which provides a more straightforward path to authorization.
* Added a new configuration property called [`cblAuthDelegate`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L2). This object specifies parameters for `CBLAuthDelegate`.
* Added a new configuration property called [`miscDatabase`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L34), which is a generic key/value database to be used by various components.
* Added a new configuration property called [`dcfDelegate`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L17) This object specifies parameters for `DCFDelegate`. Within this object, values were added for the 'endpoint' and `overridenDcfPublishMessageBody`. 'endpoint' is the endpoint to connect to in order to send device capabilities. `overridenDcfPublishMessageBody`is the message that will get sent out to the Capabilities API. Note: values within the `dcfDelegate` object will only work in `DEBUG` builds.
* Added a new configuration property called [`deviceInfo`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L9) which specifies device-identifying information for use by the Device Capability Framework (DCF), and for authorization (CBLAuthDelegate).
* Updated the Directive Sequencer to support wildcard directive handlers. This allows a handler for a given AVS interface to register at the namespace level, rather than specifying the names of all directives within that namespace.
* Updated the Raspberry Pi installation script to include `alsasink` in the configuration file.
* Added `audioSink` as a configuration option. This allows users to override the audio sink element used in `Gstreamer`.
* Added an interface for monitoring internet connection status: `InternetConnectionMonitorInterface.h`.
* The Alexa Communications Library (ACL) is no longer required to wait until authorization has succeeded before attempting to connect to AVS. Instead, `HTTP2Transport` handles waiting for authorization to complete.
* Added the Device Capabilities Framework (DCF) delegate. Device capabilities can now be sent for each capability interface using DCF publish messages.
* The sample app has been updated to send DCF publish messages, which will automatically occur when the sample app starts. Note: a DCF publish message must be successfully sent in order for communication with AVS to occur.
* The SDK now supports HTTP PUT messages.
* Added support for opt-arg style arguments and multiple configuration files. Now, the sample app can be invoked by either of these commands: `SampleApp <configfile> <debuglevel>` OR `SampleApp -C file1 -C file2 ... -L loglevel`.
**Bug Fixes**
* Issues [447](https://github.com/alexa/avs-device-sdk/issues/447) and [553](https://github.com/alexa/avs-device-sdk/issues/553) Fixed the `AttachmentRenderSource`'s handling of `BLOCKING` `AttachmentReaders`.
* Updated the `Logger` implementation to be more resilient to `nullptr` string inputs.
* Fixed a `TimeUtils` utility-related compile issue.
* Fixed a bug in which alerts failed to activate if the system was restarted without network connection.
* Fixed Android 64-bit build failure issue.
**Known Issues**
* The `ACL` may encounter issues if audio attachments are received but not consumed.
* `SpeechSynthesizerState` currently uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate state. These states may be removed in a future release.
* Some ERROR messages may be printed during start-up event if initialization proceeds normally and successfully.
* If an unrecoverable authorization error or an unrecoverable DCF error is encountered, the sample app may crash on shutdown.
* If a non-CBL `clientId` is included in the `deviceInfo` section of `AlexaClientSDKConfig.json`, the error will be reported as an unrecoverable authorization error, rather than a more specific error.
2018-04-18 22:17:28 +00:00
|
|
|
auto returnId = m_mediaPlayer->setSource(
|
|
|
|
make_unique<std::ifstream>(inputsDirPath + MP3_FILE_PATH, std::ifstream::binary), repeat);
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_NE(ERROR_SOURCE_ID, returnId);
|
|
|
|
if (id) {
|
|
|
|
*id = returnId;
|
|
|
|
}
|
2017-07-18 22:25:37 +00:00
|
|
|
}
|
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/**
|
|
|
|
* Read an audio file into a buffer. Set the source of the @c MediaPlayer to the buffer. Playback audio till the end.
|
|
|
|
* Check whether the playback started and playback finished notifications are received.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testStartPlayWaitForEnd) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
2017-08-08 00:04:43 +00:00
|
|
|
/**
|
|
|
|
* Set the source of the @c MediaPlayer to a url representing a single audio file. Playback audio till the end.
|
|
|
|
* Check whether the playback started and playback finished notifications are received.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testStartPlayForUrl) {
|
|
|
|
std::string url_single(FILE_PREFIX + inputsDirPath + MP3_FILE_PATH);
|
2017-10-30 22:14:38 +00:00
|
|
|
auto sourceId = m_mediaPlayer->setSource(url_single);
|
|
|
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
2017-08-08 00:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the source of the @c MediaPlayer twice consecutively to a url representing a single audio file.
|
|
|
|
* Playback audio till the end. Check whether the playback started and playback finished notifications
|
|
|
|
* are received.
|
2017-09-19 22:08:49 +00:00
|
|
|
*
|
|
|
|
* Consecutive calls to setSource(const std::string url) without play() cause tests to occasionally fail: ACSDK-508.
|
2017-08-08 00:04:43 +00:00
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testConsecutiveSetSource) {
|
|
|
|
std::string url_single(FILE_PREFIX + inputsDirPath + MP3_FILE_PATH);
|
|
|
|
m_mediaPlayer->setSource("");
|
2017-10-30 22:14:38 +00:00
|
|
|
auto id = m_mediaPlayer->setSource(url_single);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(id));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(id));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(id));
|
2017-08-08 00:04:43 +00:00
|
|
|
}
|
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/**
|
2017-10-30 22:14:38 +00:00
|
|
|
* Plays a second different type of source after one source has finished playing.
|
2017-05-26 23:06:14 +00:00
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testStartPlayWaitForEndStartPlayAgain) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setIStreamSource(&sourceId);
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read an audio file into a buffer. Set the source of the @c MediaPlayer to the buffer. Playback audio for a few
|
|
|
|
* seconds. Playback started notification should be received when the playback starts. Then call @c stop and expect
|
|
|
|
* the playback finished notification is received.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testStopPlay) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setIStreamSource(&sourceId, true);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-10-02 22:59:05 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read an audio file into a buffer. Set the source of the @c MediaPlayer to the buffer. Playback audio for a few
|
2017-10-30 22:14:38 +00:00
|
|
|
* seconds. Playback started notification should be received when the playback starts. Then call @c stop and expect
|
|
|
|
* the playback finished notification is received.
|
2017-05-26 23:06:14 +00:00
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testStartPlayCallAfterStopPlay) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setIStreamSource(&sourceId, true);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->play(sourceId));
|
|
|
|
}
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
/**
|
|
|
|
* Read an audio file into a buffer. Set the source of the @c MediaPlayer to the buffer. Playback audio for a few
|
|
|
|
* seconds. Playback started notification should be received when the playback starts. Call @c stop.
|
|
|
|
* and wait for the playback finished notification is received. Call @c play again.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testStartPlayCallAfterStopPlayDifferentSource) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-10-02 22:59:05 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-10-02 22:59:05 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
2017-08-08 00:04:43 +00:00
|
|
|
/*
|
|
|
|
* Pause an audio after playback has started.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testPauseDuringPlay) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setIStreamSource(&sourceId, true);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-08-08 00:04:43 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
2017-08-08 00:04:43 +00:00
|
|
|
ASSERT_EQ(m_playerObserver->getOnPlaybackFinishedCallCount(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-10-30 22:14:38 +00:00
|
|
|
* Resume paused audio.
|
2017-08-08 00:04:43 +00:00
|
|
|
*/
|
2017-10-30 22:14:38 +00:00
|
|
|
TEST_F(MediaPlayerTest, testResumeAfterPauseThenStop) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setIStreamSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->resume(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackResumed(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
2017-08-08 00:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-10-30 22:14:38 +00:00
|
|
|
* Stop of a paused audio after playback has started. An additional stop event should
|
2017-08-08 00:04:43 +00:00
|
|
|
* be sent.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testStopAfterPause) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setIStreamSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-08-08 00:04:43 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
2017-08-08 00:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pause of a paused audio after playback has started. The pause() should fail.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testPauseAfterPause) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setIStreamSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
2017-08-08 00:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calling resume after playback has started. The resume operation should fail.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testResumeAfterPlay) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setIStreamSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->resume(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
2017-08-08 00:04:43 +00:00
|
|
|
}
|
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
/**
|
|
|
|
* Read an audio file into a buffer. Set the source of the @c MediaPlayer to the buffer. Playback audio for a few
|
2017-10-02 22:59:05 +00:00
|
|
|
* seconds. Playback started notification should be received when the playback starts. Call @c getOffset.
|
2017-05-26 23:06:14 +00:00
|
|
|
* Check the offset value. Then call @c stop and expect the playback finished notification is received.
|
2017-10-02 22:59:05 +00:00
|
|
|
* Call @c getOffset again. Check the offset value.
|
2017-05-26 23:06:14 +00:00
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testGetOffsetInMilliseconds) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-10-02 22:59:05 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
2017-10-30 22:14:38 +00:00
|
|
|
std::chrono::milliseconds offset = m_mediaPlayer->getOffset(sourceId);
|
2017-10-02 22:59:05 +00:00
|
|
|
ASSERT_TRUE((offset > std::chrono::milliseconds::zero()) && (offset <= MP3_FILE_LENGTH));
|
|
|
|
ASSERT_NE(MEDIA_PLAYER_INVALID_OFFSET, offset);
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
|
|
|
ASSERT_EQ(MEDIA_PLAYER_INVALID_OFFSET, m_mediaPlayer->getOffset(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
2017-09-19 22:08:49 +00:00
|
|
|
/**
|
2017-10-02 22:59:05 +00:00
|
|
|
* Test getOffset with a null pipeline. Expect that MEDIA_PLAYER_INVALID_OFFSET is returned.
|
2017-09-19 22:08:49 +00:00
|
|
|
* This currently results in errors on shutdown. Will be fixed by ACSDK-446.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testGetOffsetInMillisecondsNullPipeline) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
ASSERT_EQ(MEDIA_PLAYER_INVALID_OFFSET, m_mediaPlayer->getOffset(sourceId + 1));
|
2017-09-19 22:08:49 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
/// Tests that calls to getOffset fail when the pipeline is in a stopped state.
|
|
|
|
TEST_F(MediaPlayerTest, testGetOffsetWhenStoppedFails) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
std::chrono::milliseconds offset = m_mediaPlayer->getOffset(sourceId);
|
|
|
|
ASSERT_EQ(MEDIA_PLAYER_INVALID_OFFSET, offset);
|
|
|
|
}
|
2017-10-02 22:59:05 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
/// Tests that calls to getOffset succeed when the pipeline is in a paused state.
|
|
|
|
TEST_F(MediaPlayerTest, testGetOffsetWhenPaused) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-10-02 22:59:05 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
std::chrono::milliseconds offset = m_mediaPlayer->getOffset(sourceId);
|
2018-01-12 23:45:42 +00:00
|
|
|
ASSERT_GE(offset, std::chrono::milliseconds::zero());
|
|
|
|
ASSERT_LE(offset, MP3_FILE_LENGTH);
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_NE(MEDIA_PLAYER_INVALID_OFFSET, offset);
|
|
|
|
}
|
2017-10-02 22:59:05 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
// /**
|
|
|
|
// * Check playing two attachments back to back.
|
|
|
|
// * Read an audio file into a buffer. Set the source of the @c MediaPlayer to the buffer. Playback audio for a few
|
|
|
|
// * seconds. Playback started notification should be received when the playback starts. Call @c getOffset.
|
|
|
|
// * Check the offset value. Wait for playback to finish and expect the playback finished notification is received.
|
|
|
|
// * Repeat the above for a new source.
|
|
|
|
// */
|
|
|
|
TEST_F(MediaPlayerTest, testPlayingTwoAttachments) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-10-02 22:59:05 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_NE(MEDIA_PLAYER_INVALID_OFFSET, m_mediaPlayer->getOffset(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-10-02 22:59:05 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_NE(MEDIA_PLAYER_INVALID_OFFSET, m_mediaPlayer->getOffset(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check playback of an attachment that is received sporadically. Playback started notification should be received
|
|
|
|
* when the playback starts. Wait for playback to finish and expect the playback finished notification is received.
|
|
|
|
* To a human ear the playback of this test is expected to sound reasonably smooth.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testUnsteadyReads) {
|
2017-10-02 22:59:05 +00:00
|
|
|
// clang-format off
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId,
|
2017-05-26 23:06:14 +00:00
|
|
|
3, {
|
|
|
|
// Sporadic receive sizes averaging out to about 6000 bytes per second.
|
|
|
|
// Each element corresponds to a 100 millisecond time interval, so each
|
|
|
|
// row of 10 corresponds to a second's worth of sizes of data.
|
|
|
|
4000, 1000, 500, 500, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 500, 0, 500, 0, 1000, 0, 4000,
|
|
|
|
0, 100, 100, 100, 100, 100, 0, 2500, 0, 3000,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 6000, 0, 0, 0, 6000,
|
|
|
|
0, 0, 0, 3000, 0, 0, 0, 0, 0, 3000,
|
|
|
|
0, 2000, 0, 0, 2000, 0, 0, 0, 2000, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 12000,
|
|
|
|
0, 0, 0, 1000, 0, 0, 0, 1000, 0, 1000,
|
|
|
|
0, 0, 0, 0, 3000, 0, 0, 0, 0, 6000
|
|
|
|
});
|
2017-10-02 22:59:05 +00:00
|
|
|
// clang-format on
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId, std::chrono::milliseconds(15000)));
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check playback of an attachment whose receipt is interrupted for about 3 seconds. Playback started notification
|
|
|
|
* should be received when the playback starts. Wait for playback to finish and expect the playback finished
|
|
|
|
* notification is received. To a human ear the playback of this test is expected to sound reasonably smooth
|
|
|
|
* initially, then be interrupted for a few seconds, and then continue fairly smoothly.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testRecoveryFromPausedReads) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId;
|
2017-10-02 22:59:05 +00:00
|
|
|
// clang-format off
|
2017-10-30 22:14:38 +00:00
|
|
|
setAttachmentReaderSource(&sourceId,
|
2017-05-26 23:06:14 +00:00
|
|
|
3, {
|
|
|
|
// Receive sizes averaging out to 6000 bytes per second with a 3 second gap.
|
|
|
|
// Each element corresponds to a 100 millisecond time interval, so each
|
|
|
|
// row of 10 corresponds to a second's worth of sizes of data.
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 6000,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 6000,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 6000,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 18000,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 6000,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 6000,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 6000,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 6000
|
|
|
|
});
|
2017-10-02 22:59:05 +00:00
|
|
|
// clang-format on
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId, std::chrono::milliseconds(20000)));
|
2017-05-26 23:06:14 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
/// Tests playing a dummy playlist
|
2017-08-08 00:04:43 +00:00
|
|
|
TEST_F(MediaPlayerTest, testStartPlayWithUrlPlaylistWaitForEnd) {
|
2017-10-30 22:14:38 +00:00
|
|
|
MediaPlayer::SourceId sourceId = m_mediaPlayer->setSource(TEST_M3U_PLAYLIST_URL);
|
|
|
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId, std::chrono::milliseconds(10000)));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId, std::chrono::milliseconds(10000)));
|
2017-08-08 00:04:43 +00:00
|
|
|
ASSERT_EQ(m_playerObserver->getOnPlaybackStartedCallCount(), 1);
|
|
|
|
ASSERT_EQ(m_playerObserver->getOnPlaybackFinishedCallCount(), 1);
|
|
|
|
}
|
|
|
|
|
2017-09-19 22:08:49 +00:00
|
|
|
/**
|
2017-10-02 22:59:05 +00:00
|
|
|
* Test setting the offset to a seekable source. Setting the offset should succeed and playback should start from the
|
|
|
|
* offset.
|
2017-09-19 22:08:49 +00:00
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testSetOffsetSeekableSource) {
|
|
|
|
std::chrono::milliseconds offset(OFFSET);
|
|
|
|
|
|
|
|
std::string url_single(FILE_PREFIX + inputsDirPath + MP3_FILE_PATH);
|
Version 1.8 alexa-client-sdk
Changes in this update:
Enhancements
Added local stop functionality. This allows a user to stop an active function, such as an alert or timer, by uttering "Alexa, stop" when an Alexa-enabled product is offline.
Alerts in the background now stream in 10 sec intervals, rather than continuously.
Added support for France to the sample app.
friendlyName can now be updated for BlueZ implementations of BlueZBluetoothDevice and BlueZHostController.
Bug Fixes
Fixed an issue where the Bluetooth agent didn't clear user data upon reset, including paired devices and the uuidMapping table.
Fixed MediaPlayer threading issues. Now each instance has it's own glib main loop thread, rather than utilizing the default main context worker thread.
Fixed segmentation fault issues that occurred when certain static initializers needed to be initialized in a certain order, but the order wasn't defined.
Known Issues
The ACL may encounter issues if audio attachments are received but not consumed.
SpeechSynthesizerState currently uses GAINING_FOCUS and LOSING_FOCUS as a workaround for handling intermediate state. These states may be removed in a future release.
The Alexa app doesn't always indicate when a device is successfully connected via Bluetooth.
Connecting a product to streaming media via Bluetooth will sometimes stop media playback within the source application. Resuming playback through the source application or toggling next/previous will correct playback.
When a source device is streaming silence via Bluetooth, the Alexa companion app indicates that audio content is streaming.
The Bluetooth agent assumes that the Bluetooth adapter is always connected to a power source. Disconnecting from a power source during operation is not yet supported.
On some products, interrupted Bluetooth playback may not resume if other content is locally streamed.
On Raspberry Pi, when streaming audio via Bluetooth, sometimes the audio stream stutters.
On Raspberry Pi, BlueALSA must be terminated each time the device boots. See Raspberry Pi Quick Start Guide for more information.
2018-06-27 21:41:15 +00:00
|
|
|
auto sourceId = m_mediaPlayer->setSource(url_single, offset);
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
2017-09-19 22:08:49 +00:00
|
|
|
auto start = std::chrono::steady_clock::now();
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
2017-09-19 22:08:49 +00:00
|
|
|
ASSERT_EQ(m_playerObserver->getOnPlaybackStartedCallCount(), 1);
|
|
|
|
ASSERT_EQ(m_playerObserver->getOnPlaybackFinishedCallCount(), 1);
|
2017-10-30 22:14:38 +00:00
|
|
|
|
|
|
|
std::chrono::milliseconds timeElapsed =
|
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
|
Version 1.8 alexa-client-sdk
Changes in this update:
Enhancements
Added local stop functionality. This allows a user to stop an active function, such as an alert or timer, by uttering "Alexa, stop" when an Alexa-enabled product is offline.
Alerts in the background now stream in 10 sec intervals, rather than continuously.
Added support for France to the sample app.
friendlyName can now be updated for BlueZ implementations of BlueZBluetoothDevice and BlueZHostController.
Bug Fixes
Fixed an issue where the Bluetooth agent didn't clear user data upon reset, including paired devices and the uuidMapping table.
Fixed MediaPlayer threading issues. Now each instance has it's own glib main loop thread, rather than utilizing the default main context worker thread.
Fixed segmentation fault issues that occurred when certain static initializers needed to be initialized in a certain order, but the order wasn't defined.
Known Issues
The ACL may encounter issues if audio attachments are received but not consumed.
SpeechSynthesizerState currently uses GAINING_FOCUS and LOSING_FOCUS as a workaround for handling intermediate state. These states may be removed in a future release.
The Alexa app doesn't always indicate when a device is successfully connected via Bluetooth.
Connecting a product to streaming media via Bluetooth will sometimes stop media playback within the source application. Resuming playback through the source application or toggling next/previous will correct playback.
When a source device is streaming silence via Bluetooth, the Alexa companion app indicates that audio content is streaming.
The Bluetooth agent assumes that the Bluetooth adapter is always connected to a power source. Disconnecting from a power source during operation is not yet supported.
On some products, interrupted Bluetooth playback may not resume if other content is locally streamed.
On Raspberry Pi, when streaming audio via Bluetooth, sometimes the audio stream stutters.
On Raspberry Pi, BlueALSA must be terminated each time the device boots. See Raspberry Pi Quick Start Guide for more information.
2018-06-27 21:41:15 +00:00
|
|
|
ACSDK_INFO(LX("MediaPlayerTest").d("timeElapsed", timeElapsed.count()));
|
|
|
|
// Time elapsed should be total file length minus the offset.
|
|
|
|
ASSERT_TRUE(timeElapsed < (MP3_FILE_LENGTH - offset + TOLERANCE));
|
2017-09-19 22:08:49 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 23:31:53 +00:00
|
|
|
// TODO: ACSDK-1024 MediaPlayerTest.testSetOffsetOutsideBounds is flaky
|
2017-09-19 22:08:49 +00:00
|
|
|
/**
|
|
|
|
* Test setting the offset outside the bounds of the source. Playback will immediately end.
|
|
|
|
*/
|
2018-02-12 23:31:53 +00:00
|
|
|
TEST_F(MediaPlayerTest, DISABLED_testSetOffsetOutsideBounds) {
|
2017-09-19 22:08:49 +00:00
|
|
|
std::chrono::milliseconds outOfBounds(MP3_FILE_LENGTH + PADDING);
|
|
|
|
|
|
|
|
std::string url_single(FILE_PREFIX + inputsDirPath + MP3_FILE_PATH);
|
2017-12-09 00:07:37 +00:00
|
|
|
auto sourceId = m_mediaPlayer->setSource(url_single, outOfBounds);
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
2017-09-19 22:08:49 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
2018-01-12 23:45:42 +00:00
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackError(sourceId));
|
2017-09-19 22:08:49 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 23:45:42 +00:00
|
|
|
// TODO: ACSDK-828: this test ends up with a shorter playback time than the actual file length.
|
2017-09-19 22:08:49 +00:00
|
|
|
/**
|
|
|
|
* Test calling setSource resets the offset.
|
|
|
|
*
|
|
|
|
* Consecutive calls to setSource(const std::string url) without play() cause tests to occasionally fail: ACSDK-508.
|
|
|
|
*/
|
2018-01-12 23:45:42 +00:00
|
|
|
TEST_F(MediaPlayerTest, DISABLED_testSetSourceResetsOffset) {
|
2017-09-19 22:08:49 +00:00
|
|
|
std::chrono::milliseconds offset(OFFSET);
|
|
|
|
|
|
|
|
std::string url_single(FILE_PREFIX + inputsDirPath + MP3_FILE_PATH);
|
2017-12-09 00:07:37 +00:00
|
|
|
auto sourceId = m_mediaPlayer->setSource(url_single, offset);
|
2018-01-12 23:45:42 +00:00
|
|
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
2017-09-19 22:08:49 +00:00
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
sourceId = m_mediaPlayer->setSource(url_single);
|
|
|
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
2017-09-19 22:08:49 +00:00
|
|
|
// Play, expect full file.
|
2018-01-12 23:45:42 +00:00
|
|
|
auto start = std::chrono::steady_clock::now();
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
2017-09-19 22:08:49 +00:00
|
|
|
|
|
|
|
std::chrono::milliseconds timeElapsed =
|
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
|
|
|
|
ACSDK_INFO(LX("MediaPlayerTest").d("timeElapsed", timeElapsed.count()));
|
|
|
|
|
|
|
|
// Time elapsed should be the full file.
|
|
|
|
ASSERT_TRUE(timeElapsed >= MP3_FILE_LENGTH);
|
|
|
|
ASSERT_EQ(m_playerObserver->getOnPlaybackStartedCallCount(), 1);
|
|
|
|
ASSERT_EQ(m_playerObserver->getOnPlaybackFinishedCallCount(), 1);
|
|
|
|
}
|
|
|
|
|
2017-10-02 22:59:05 +00:00
|
|
|
/**
|
|
|
|
* Test consecutive setSource() and play() calls. Expect the PlaybackStarted and PlaybackFinished will be received
|
|
|
|
* before the timeout.
|
|
|
|
*/
|
2017-12-09 00:07:37 +00:00
|
|
|
TEST_F(MediaPlayerTest, testRepeatAttachment) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
2017-10-02 22:59:05 +00:00
|
|
|
for (int i = 0; i < 10; i++) {
|
2017-12-09 00:07:37 +00:00
|
|
|
setAttachmentReaderSource(&sourceId);
|
2017-10-30 22:14:38 +00:00
|
|
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
2017-10-02 22:59:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 22:14:38 +00:00
|
|
|
/**
|
|
|
|
* Test that the media plays after a volume change.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testSetVolumePlays) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
SpeakerInterface::SpeakerSettings settings;
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
m_mediaPlayer->setVolume(10);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->resume(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
|
|
|
|
m_mediaPlayer->getSpeakerSettings(&settings);
|
|
|
|
ASSERT_EQ(settings.volume, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that the media plays after a volume adjustment.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testAdjustVolumePlaysDuringPlay) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
SpeakerInterface::SpeakerSettings settings;
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
|
|
|
|
m_mediaPlayer->adjustVolume(-90);
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
|
|
|
|
m_mediaPlayer->getSpeakerSettings(&settings);
|
|
|
|
ASSERT_EQ(settings.volume, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that the media plays after a volume adjustment.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testAdjustVolumePlays) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
SpeakerInterface::SpeakerSettings settings;
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
|
|
|
|
m_mediaPlayer->adjustVolume(-90);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->resume(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
|
|
|
|
m_mediaPlayer->getSpeakerSettings(&settings);
|
|
|
|
ASSERT_EQ(settings.volume, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test the play behavior when the media is adjusted out of bounds. The volume should be capped
|
|
|
|
* at the bounds. The media should play to completion.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testAdjustVolumeOutOfBounds) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
|
|
|
|
m_mediaPlayer->setVolume(10);
|
|
|
|
m_mediaPlayer->adjustVolume(-100);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->resume(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
|
|
|
|
SpeakerInterface::SpeakerSettings settings;
|
|
|
|
m_mediaPlayer->getSpeakerSettings(&settings);
|
|
|
|
ASSERT_EQ(settings.volume, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test the media plays to completion even if it's muted.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testSetMutePlays) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
SpeakerInterface::SpeakerSettings settings;
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
|
|
|
|
m_mediaPlayer->setMute(true);
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->resume(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
|
|
|
|
m_mediaPlayer->getSpeakerSettings(&settings);
|
|
|
|
ASSERT_TRUE(settings.mute);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that the speaker settings can be retrieved.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testGetSpeakerSettings) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
SpeakerInterface::SpeakerSettings settings;
|
|
|
|
|
|
|
|
m_mediaPlayer->setMute(true);
|
|
|
|
m_mediaPlayer->setVolume(15);
|
|
|
|
m_mediaPlayer->getSpeakerSettings(&settings);
|
|
|
|
ASSERT_TRUE(settings.mute);
|
|
|
|
ASSERT_EQ(settings.volume, 15);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tests this rounding edgecase. Calling adjustVolume(-10) with volume at 90 resulted in a value of 79
|
|
|
|
* when not rounded properly.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testRoundingEdgeCase) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
SpeakerInterface::SpeakerSettings settings;
|
|
|
|
|
|
|
|
m_mediaPlayer->setVolume(90);
|
|
|
|
m_mediaPlayer->adjustVolume(-10);
|
|
|
|
m_mediaPlayer->getSpeakerSettings(&settings);
|
|
|
|
ASSERT_EQ(settings.volume, 80);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read an audio file into a buffer. Set the source of the @c MediaPlayer to the buffer. Playback audio till the end.
|
|
|
|
* Check whether the playback started and playback finished notifications are received.
|
|
|
|
* Check whether the tags were read from the file.
|
|
|
|
*/
|
|
|
|
TEST_F(MediaPlayerTest, testReadTags) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForTags(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
/*
|
|
|
|
* fox_dog.mp3 returns 3 sets of tags.
|
|
|
|
*/
|
|
|
|
ASSERT_EQ(m_playerObserver->getOnTagsCallCount(), 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests that consecutive calls to the same public API fails
|
|
|
|
TEST_F(MediaPlayerTest, testConsecutiveSameApiCalls) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->resume(sourceId));
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->resume(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackResumed(sourceId));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests that pausing immediately before waiting for a callback is valid.
|
|
|
|
TEST_F(MediaPlayerTest, testImmediatePause) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests setting multiple set source calls and observing onPlaybackStopped and onPlaybackFinished calls
|
|
|
|
TEST_F(MediaPlayerTest, multiplePlayAndSetSource) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
MediaPlayer::SourceId secondSourceId;
|
|
|
|
MediaPlayer::SourceId thirdSourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
|
|
|
|
std::chrono::milliseconds offset = m_mediaPlayer->getOffset(sourceId);
|
|
|
|
ASSERT_NE(MEDIA_PLAYER_INVALID_OFFSET, offset);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
ASSERT_FALSE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
|
|
|
|
|
|
|
setAttachmentReaderSource(&secondSourceId);
|
|
|
|
ASSERT_FALSE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(secondSourceId));
|
|
|
|
|
|
|
|
setAttachmentReaderSource(&thirdSourceId);
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(secondSourceId));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests passing an invalid source id to a play() call
|
|
|
|
TEST_F(MediaPlayerTest, invalidSourceId) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->play(sourceId + 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests that two consecutive calls to pause fails.
|
|
|
|
TEST_F(MediaPlayerTest, doublePause) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->pause(sourceId));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests that a resume when already playing fails
|
|
|
|
TEST_F(MediaPlayerTest, resumeWhenPlaying) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->resume(sourceId));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests that a resume when stopped (not paused) fails
|
|
|
|
TEST_F(MediaPlayerTest, resumeWhenStopped) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
|
|
|
ASSERT_FALSE(m_mediaPlayer->resume(sourceId));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Tests that a stop callback when playing leads to an onPlaybackStopped callback
|
|
|
|
TEST_F(MediaPlayerTest, newSetSourceLeadsToStoppedCallback) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
|
|
|
|
MediaPlayer::SourceId secondSourceId;
|
|
|
|
setAttachmentReaderSource(&secondSourceId);
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
|
|
|
}
|
|
|
|
|
2017-12-09 00:07:37 +00:00
|
|
|
/// Tests that resuming after a pause with a pending play leads to an onPlaybackResumed callback.
|
|
|
|
TEST_F(MediaPlayerTest, resumeAfterPauseWithPendingPlay) {
|
|
|
|
MediaPlayer::SourceId sourceId;
|
|
|
|
setAttachmentReaderSource(&sourceId);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the situation where a play is followed immediately by a pause.
|
|
|
|
* The pause() needs to happen before the onPlaybackStarted call is received to
|
|
|
|
* properly test this case. The assumption here is that the play() call should always return
|
|
|
|
* before the actual start of audio playback.
|
|
|
|
*/
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
|
|
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
|
|
|
|
|
|
|
// Expect onPlaybackResumed call.
|
|
|
|
m_mediaPlayer->resume(sourceId);
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackResumed(sourceId));
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
|
|
|
}
|
|
|
|
|
2017-10-02 22:59:05 +00:00
|
|
|
} // namespace test
|
|
|
|
} // namespace mediaPlayer
|
|
|
|
} // namespace alexaClientSDK
|
2017-05-26 23:06:14 +00:00
|
|
|
|
2017-10-02 22:59:05 +00:00
|
|
|
int main(int argc, char** argv) {
|
2017-05-26 23:06:14 +00:00
|
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
|
|
|
|
|
|
if (argc < 2) {
|
2017-12-09 00:07:37 +00:00
|
|
|
std::cerr << "Usage: " << std::string(argv[0]) << " <absolute path to test inputs folder>" << std::endl;
|
2017-05-26 23:06:14 +00:00
|
|
|
} else {
|
2017-06-09 23:23:31 +00:00
|
|
|
alexaClientSDK::mediaPlayer::test::inputsDirPath = std::string(argv[1]);
|
2017-10-02 22:59:05 +00:00
|
|
|
alexaClientSDK::mediaPlayer::test::urlsToContentTypes.insert(
|
|
|
|
{alexaClientSDK::mediaPlayer::test::FILE_PREFIX + alexaClientSDK::mediaPlayer::test::inputsDirPath +
|
|
|
|
alexaClientSDK::mediaPlayer::test::MP3_FILE_PATH,
|
|
|
|
"audio/mpeg"});
|
2018-01-12 23:45:42 +00:00
|
|
|
std::ifstream fileStream(
|
Version 1.7.0 of the avs-device-sdk
Changes in this update:
**Enhancements**
* `AuthDelegate` and `AuthServer.py` have been replaced by `CBLAUthDelegate`, which provides a more straightforward path to authorization.
* Added a new configuration property called [`cblAuthDelegate`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L2). This object specifies parameters for `CBLAuthDelegate`.
* Added a new configuration property called [`miscDatabase`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L34), which is a generic key/value database to be used by various components.
* Added a new configuration property called [`dcfDelegate`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L17) This object specifies parameters for `DCFDelegate`. Within this object, values were added for the 'endpoint' and `overridenDcfPublishMessageBody`. 'endpoint' is the endpoint to connect to in order to send device capabilities. `overridenDcfPublishMessageBody`is the message that will get sent out to the Capabilities API. Note: values within the `dcfDelegate` object will only work in `DEBUG` builds.
* Added a new configuration property called [`deviceInfo`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L9) which specifies device-identifying information for use by the Device Capability Framework (DCF), and for authorization (CBLAuthDelegate).
* Updated the Directive Sequencer to support wildcard directive handlers. This allows a handler for a given AVS interface to register at the namespace level, rather than specifying the names of all directives within that namespace.
* Updated the Raspberry Pi installation script to include `alsasink` in the configuration file.
* Added `audioSink` as a configuration option. This allows users to override the audio sink element used in `Gstreamer`.
* Added an interface for monitoring internet connection status: `InternetConnectionMonitorInterface.h`.
* The Alexa Communications Library (ACL) is no longer required to wait until authorization has succeeded before attempting to connect to AVS. Instead, `HTTP2Transport` handles waiting for authorization to complete.
* Added the Device Capabilities Framework (DCF) delegate. Device capabilities can now be sent for each capability interface using DCF publish messages.
* The sample app has been updated to send DCF publish messages, which will automatically occur when the sample app starts. Note: a DCF publish message must be successfully sent in order for communication with AVS to occur.
* The SDK now supports HTTP PUT messages.
* Added support for opt-arg style arguments and multiple configuration files. Now, the sample app can be invoked by either of these commands: `SampleApp <configfile> <debuglevel>` OR `SampleApp -C file1 -C file2 ... -L loglevel`.
**Bug Fixes**
* Issues [447](https://github.com/alexa/avs-device-sdk/issues/447) and [553](https://github.com/alexa/avs-device-sdk/issues/553) Fixed the `AttachmentRenderSource`'s handling of `BLOCKING` `AttachmentReaders`.
* Updated the `Logger` implementation to be more resilient to `nullptr` string inputs.
* Fixed a `TimeUtils` utility-related compile issue.
* Fixed a bug in which alerts failed to activate if the system was restarted without network connection.
* Fixed Android 64-bit build failure issue.
**Known Issues**
* The `ACL` may encounter issues if audio attachments are received but not consumed.
* `SpeechSynthesizerState` currently uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate state. These states may be removed in a future release.
* Some ERROR messages may be printed during start-up event if initialization proceeds normally and successfully.
* If an unrecoverable authorization error or an unrecoverable DCF error is encountered, the sample app may crash on shutdown.
* If a non-CBL `clientId` is included in the `deviceInfo` section of `AlexaClientSDKConfig.json`, the error will be reported as an unrecoverable authorization error, rather than a more specific error.
2018-04-18 22:17:28 +00:00
|
|
|
alexaClientSDK::mediaPlayer::test::inputsDirPath + alexaClientSDK::mediaPlayer::test::MP3_FILE_PATH,
|
|
|
|
std::ifstream::binary);
|
2018-01-12 23:45:42 +00:00
|
|
|
std::stringstream fileData;
|
|
|
|
fileData << fileStream.rdbuf();
|
|
|
|
alexaClientSDK::mediaPlayer::test::urlsToContent.insert({alexaClientSDK::mediaPlayer::test::FILE_PREFIX +
|
|
|
|
alexaClientSDK::mediaPlayer::test::inputsDirPath +
|
|
|
|
alexaClientSDK::mediaPlayer::test::MP3_FILE_PATH,
|
|
|
|
fileData.str()});
|
2017-10-02 22:59:05 +00:00
|
|
|
alexaClientSDK::mediaPlayer::test::urlsToContentTypes.insert(
|
|
|
|
{alexaClientSDK::mediaPlayer::test::TEST_M3U_PLAYLIST_URL, "audio/mpegurl"});
|
|
|
|
alexaClientSDK::mediaPlayer::test::TEST_M3U_PLAYLIST_CONTENT =
|
|
|
|
std::string("EXTINF:2,fox_dog.mp3\n") +
|
|
|
|
std::string(
|
|
|
|
alexaClientSDK::mediaPlayer::test::FILE_PREFIX + alexaClientSDK::mediaPlayer::test::inputsDirPath +
|
|
|
|
alexaClientSDK::mediaPlayer::test::MP3_FILE_PATH) +
|
|
|
|
'\n' + std::string("EXTINF:2,fox_dog.mp3\n") +
|
|
|
|
std::string(
|
|
|
|
alexaClientSDK::mediaPlayer::test::FILE_PREFIX + alexaClientSDK::mediaPlayer::test::inputsDirPath +
|
|
|
|
alexaClientSDK::mediaPlayer::test::MP3_FILE_PATH) +
|
|
|
|
'\n';
|
|
|
|
alexaClientSDK::mediaPlayer::test::urlsToContent.insert(
|
|
|
|
{alexaClientSDK::mediaPlayer::test::TEST_M3U_PLAYLIST_URL,
|
|
|
|
alexaClientSDK::mediaPlayer::test::TEST_M3U_PLAYLIST_CONTENT});
|
Version 1.7.0 of the avs-device-sdk
Changes in this update:
**Enhancements**
* `AuthDelegate` and `AuthServer.py` have been replaced by `CBLAUthDelegate`, which provides a more straightforward path to authorization.
* Added a new configuration property called [`cblAuthDelegate`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L2). This object specifies parameters for `CBLAuthDelegate`.
* Added a new configuration property called [`miscDatabase`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L34), which is a generic key/value database to be used by various components.
* Added a new configuration property called [`dcfDelegate`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L17) This object specifies parameters for `DCFDelegate`. Within this object, values were added for the 'endpoint' and `overridenDcfPublishMessageBody`. 'endpoint' is the endpoint to connect to in order to send device capabilities. `overridenDcfPublishMessageBody`is the message that will get sent out to the Capabilities API. Note: values within the `dcfDelegate` object will only work in `DEBUG` builds.
* Added a new configuration property called [`deviceInfo`](https://github.com/alexa/avs-device-sdk/blob/master/Integration/AlexaClientSDKConfig.json#L9) which specifies device-identifying information for use by the Device Capability Framework (DCF), and for authorization (CBLAuthDelegate).
* Updated the Directive Sequencer to support wildcard directive handlers. This allows a handler for a given AVS interface to register at the namespace level, rather than specifying the names of all directives within that namespace.
* Updated the Raspberry Pi installation script to include `alsasink` in the configuration file.
* Added `audioSink` as a configuration option. This allows users to override the audio sink element used in `Gstreamer`.
* Added an interface for monitoring internet connection status: `InternetConnectionMonitorInterface.h`.
* The Alexa Communications Library (ACL) is no longer required to wait until authorization has succeeded before attempting to connect to AVS. Instead, `HTTP2Transport` handles waiting for authorization to complete.
* Added the Device Capabilities Framework (DCF) delegate. Device capabilities can now be sent for each capability interface using DCF publish messages.
* The sample app has been updated to send DCF publish messages, which will automatically occur when the sample app starts. Note: a DCF publish message must be successfully sent in order for communication with AVS to occur.
* The SDK now supports HTTP PUT messages.
* Added support for opt-arg style arguments and multiple configuration files. Now, the sample app can be invoked by either of these commands: `SampleApp <configfile> <debuglevel>` OR `SampleApp -C file1 -C file2 ... -L loglevel`.
**Bug Fixes**
* Issues [447](https://github.com/alexa/avs-device-sdk/issues/447) and [553](https://github.com/alexa/avs-device-sdk/issues/553) Fixed the `AttachmentRenderSource`'s handling of `BLOCKING` `AttachmentReaders`.
* Updated the `Logger` implementation to be more resilient to `nullptr` string inputs.
* Fixed a `TimeUtils` utility-related compile issue.
* Fixed a bug in which alerts failed to activate if the system was restarted without network connection.
* Fixed Android 64-bit build failure issue.
**Known Issues**
* The `ACL` may encounter issues if audio attachments are received but not consumed.
* `SpeechSynthesizerState` currently uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate state. These states may be removed in a future release.
* Some ERROR messages may be printed during start-up event if initialization proceeds normally and successfully.
* If an unrecoverable authorization error or an unrecoverable DCF error is encountered, the sample app may crash on shutdown.
* If a non-CBL `clientId` is included in the `deviceInfo` section of `AlexaClientSDKConfig.json`, the error will be reported as an unrecoverable authorization error, rather than a more specific error.
2018-04-18 22:17:28 +00:00
|
|
|
|
2017-05-26 23:06:14 +00:00
|
|
|
return RUN_ALL_TESTS();
|
|
|
|
}
|
|
|
|
}
|