1441 lines
56 KiB
C++
1441 lines
56 KiB
C++
|
/*
|
||
|
* Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License").
|
||
|
* You may not use this file except in compliance with the License.
|
||
|
* A copy of the License is located at
|
||
|
*
|
||
|
* http://aws.amazon.com/apache2.0/
|
||
|
*
|
||
|
* or in the "license" file accompanying this file. This file is distributed
|
||
|
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
||
|
* express or implied. See the License for the specific language governing
|
||
|
* permissions and limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <chrono>
|
||
|
#include <condition_variable>
|
||
|
#include <fstream>
|
||
|
#include <memory>
|
||
|
#include <mutex>
|
||
|
#include <thread>
|
||
|
#include <unordered_map>
|
||
|
|
||
|
#include <gmock/gmock.h>
|
||
|
#include <gtest/gtest.h>
|
||
|
|
||
|
#include <AVSCommon/AVS/Initialization/AlexaClientSDKInit.h>
|
||
|
#include <AVSCommon/AVS/SpeakerConstants/SpeakerConstants.h>
|
||
|
#include <AVSCommon/Utils/Configuration/ConfigurationNode.h>
|
||
|
#include <AVSCommon/Utils/Logger/Logger.h>
|
||
|
#include <AVSCommon/Utils/Memory/Memory.h>
|
||
|
#include <PlaylistParser/PlaylistParser.h>
|
||
|
|
||
|
#include "MediaPlayer/MediaPlayer.h"
|
||
|
#include "AVSCommon/Utils/MediaPlayer/MediaPlayerObserverInterface.h"
|
||
|
|
||
|
namespace alexaClientSDK {
|
||
|
namespace mediaPlayer {
|
||
|
namespace test {
|
||
|
|
||
|
using namespace avsCommon::avs;
|
||
|
using namespace avsCommon::avs::attachment;
|
||
|
using namespace avsCommon::avs::speakerConstants;
|
||
|
using namespace avsCommon::sdkInterfaces;
|
||
|
using namespace avsCommon::utils::mediaPlayer;
|
||
|
using namespace avsCommon::utils::memory;
|
||
|
using namespace ::testing;
|
||
|
|
||
|
/// String to identify log entries originating from this file.
|
||
|
static const std::string TAG("MediaPlayerTest");
|
||
|
|
||
|
static const MediaPlayer::SourceId ERROR_SOURCE_ID = MediaPlayer::ERROR;
|
||
|
|
||
|
/**
|
||
|
* Create a LogEntry using this file's TAG and the specified event string.
|
||
|
*
|
||
|
* @param The event string for this @c LogEntry.
|
||
|
*/
|
||
|
#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event)
|
||
|
|
||
|
/// The 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");
|
||
|
|
||
|
static const std::string TEST_M3U_PLAYLIST_URL("fox_dog_playlist.m3u");
|
||
|
|
||
|
static std::string TEST_M3U_PLAYLIST_CONTENT;
|
||
|
|
||
|
/// file URI Prefix
|
||
|
static const std::string FILE_PREFIX("file://");
|
||
|
|
||
|
/// 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);
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
static const std::string MEDIA_PLAYER_CONFIG = R"({
|
||
|
"gstreamerMediaPlayer":{
|
||
|
"audioSink":"directsoundsink"
|
||
|
}
|
||
|
})";
|
||
|
#endif
|
||
|
|
||
|
#ifdef RESOLVED_ACSDK_627
|
||
|
|
||
|
/// Tolerance when setting expectations.
|
||
|
static const std::chrono::milliseconds TOLERANCE(200);
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/// Padding to add to offsets when necessary.
|
||
|
static const std::chrono::milliseconds PADDING(10);
|
||
|
|
||
|
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} {
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<avsCommon::utils::HTTPContent> getContent(
|
||
|
FetchOptions fetchOption,
|
||
|
std::shared_ptr<avsCommon::avs::attachment::AttachmentWriter> writer) override {
|
||
|
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>(
|
||
|
avsCommon::utils::HTTPContent{std::move(statusFuture), std::move(contentTypeFuture), nullptr});
|
||
|
}
|
||
|
} 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("");
|
||
|
auto attachment = writeStringIntoAttachment(urlAndContent->second, writer);
|
||
|
if (!attachment) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
return avsCommon::utils::memory::make_unique<avsCommon::utils::HTTPContent>(
|
||
|
avsCommon::utils::HTTPContent{std::move(statusFuture), std::move(contentTypeFuture), attachment});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
std::shared_ptr<avsCommon::avs::attachment::InProcessAttachment> writeStringIntoAttachment(
|
||
|
const std::string& string,
|
||
|
std::shared_ptr<avsCommon::avs::attachment::AttachmentWriter> writer) {
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
if (!writer) {
|
||
|
writer = stream->createWriter();
|
||
|
}
|
||
|
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);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* 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()});
|
||
|
|
||
|
size_t read(void* buf, std::size_t numBytes, ReadStatus* readStatus, std::chrono::milliseconds timeoutMs) override;
|
||
|
|
||
|
void close(ClosePoint closePoint) override;
|
||
|
|
||
|
bool seek(uint64_t offset) override {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
uint64_t getNumUnreadBytes() override {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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(
|
||
|
void* buf,
|
||
|
std::size_t numBytes,
|
||
|
ReadStatus* readStatus,
|
||
|
std::chrono::milliseconds timeoutMs) {
|
||
|
// 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);
|
||
|
result = receiveBytes(static_cast<char*>(buf), sizeToRead);
|
||
|
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) {
|
||
|
m_stream = make_unique<std::ifstream>(inputsDirPath + MP3_FILE_PATH, std::ifstream::binary);
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
class MockPlayerObserver : public MediaPlayerObserverInterface {
|
||
|
public:
|
||
|
/**
|
||
|
* Destructor.
|
||
|
*/
|
||
|
~MockPlayerObserver(){};
|
||
|
|
||
|
// void onPlaybackStarted() override;
|
||
|
void onPlaybackStarted(SourceId id) override;
|
||
|
|
||
|
void onPlaybackFinished(SourceId id) override;
|
||
|
|
||
|
void onPlaybackError(SourceId id, const ErrorType& type, std::string error) override;
|
||
|
|
||
|
void onPlaybackPaused(SourceId id) override;
|
||
|
|
||
|
void onPlaybackResumed(SourceId id) override;
|
||
|
|
||
|
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));
|
||
|
|
||
|
/**
|
||
|
* 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 waitForPlaybackFinished(
|
||
|
SourceId id,
|
||
|
const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
||
|
|
||
|
/**
|
||
|
* 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 waitForPlaybackPaused(SourceId id, const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
||
|
|
||
|
/**
|
||
|
* 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 waitForPlaybackResumed(
|
||
|
SourceId id,
|
||
|
const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
||
|
|
||
|
/**
|
||
|
* 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 waitForPlaybackStopped(
|
||
|
SourceId id,
|
||
|
const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
||
|
|
||
|
/**
|
||
|
* 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));
|
||
|
|
||
|
/**
|
||
|
* 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 waitForTags(SourceId id, const std::chrono::milliseconds duration = std::chrono::milliseconds(5000));
|
||
|
|
||
|
/**
|
||
|
* 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();
|
||
|
|
||
|
/**
|
||
|
* TODO: Make this class a mock and remove this.
|
||
|
*
|
||
|
* This gets the number of times onTags was called.
|
||
|
*/
|
||
|
int getOnTagsCallCount();
|
||
|
|
||
|
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;
|
||
|
/// Trigger to wake up m_wakePlaybackPaused calls.
|
||
|
std::condition_variable m_wakePlaybackPaused;
|
||
|
/// Trigger to wake up m_wakePlaybackResumed calls.
|
||
|
std::condition_variable m_wakePlaybackResumed;
|
||
|
std::condition_variable m_wakePlaybackStopped;
|
||
|
std::condition_variable m_wakePlaybackError;
|
||
|
/// Trigger to wake up m_wakeTags calls.
|
||
|
std::condition_variable m_wakeTags;
|
||
|
|
||
|
// TODO: Make this class a mock and remove these.
|
||
|
int m_onPlaybackStartedCallCount = 0;
|
||
|
int m_onPlaybackFinishedCallCount = 0;
|
||
|
int m_onTagsCallCount = 0;
|
||
|
|
||
|
/// 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;
|
||
|
/// 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;
|
||
|
/// Flag to set when a playback stopped message is received.
|
||
|
bool m_playbackStopped;
|
||
|
/// Flag to set when a playback error message is received.
|
||
|
bool m_playbackError;
|
||
|
/// Flag to set when a tags message is received.
|
||
|
bool m_tags;
|
||
|
|
||
|
SourceId m_lastId = 0;
|
||
|
};
|
||
|
|
||
|
void MockPlayerObserver::onPlaybackStarted(SourceId id) {
|
||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||
|
m_lastId = id;
|
||
|
m_playbackStarted = true;
|
||
|
m_playbackFinished = false;
|
||
|
m_playbackStopped = false;
|
||
|
m_wakePlaybackStarted.notify_all();
|
||
|
m_onPlaybackStartedCallCount++;
|
||
|
}
|
||
|
|
||
|
void MockPlayerObserver::onPlaybackFinished(SourceId id) {
|
||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||
|
m_lastId = id;
|
||
|
m_playbackFinished = true;
|
||
|
m_playbackStarted = false;
|
||
|
m_wakePlaybackFinished.notify_all();
|
||
|
m_onPlaybackFinishedCallCount++;
|
||
|
}
|
||
|
|
||
|
void MockPlayerObserver::onPlaybackError(SourceId id, const ErrorType& type, std::string error) {
|
||
|
m_lastId = id;
|
||
|
m_playbackError = true;
|
||
|
m_wakePlaybackError.notify_all();
|
||
|
};
|
||
|
|
||
|
void MockPlayerObserver::onPlaybackPaused(SourceId id) {
|
||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||
|
m_lastId = id;
|
||
|
m_playbackPaused = true;
|
||
|
m_wakePlaybackPaused.notify_all();
|
||
|
};
|
||
|
|
||
|
void MockPlayerObserver::onPlaybackResumed(SourceId id) {
|
||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||
|
m_lastId = id;
|
||
|
m_playbackResumed = true;
|
||
|
m_playbackPaused = false;
|
||
|
m_wakePlaybackResumed.notify_all();
|
||
|
};
|
||
|
|
||
|
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) {
|
||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||
|
if (!m_wakePlaybackFinished.wait_for(
|
||
|
lock, duration, [this, id]() { return m_playbackFinished && id == m_lastId; })) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool MockPlayerObserver::waitForPlaybackPaused(SourceId id, const std::chrono::milliseconds duration) {
|
||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||
|
if (!m_wakePlaybackPaused.wait_for(lock, duration, [this, id]() { return m_playbackPaused && id == m_lastId; })) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool MockPlayerObserver::waitForPlaybackResumed(SourceId id, const std::chrono::milliseconds duration) {
|
||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||
|
if (!m_wakePlaybackResumed.wait_for(lock, duration, [this, id]() { return m_playbackResumed && id == m_lastId; })) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool MockPlayerObserver::waitForPlaybackStopped(SourceId id, const std::chrono::milliseconds duration) {
|
||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||
|
if (!m_wakePlaybackStopped.wait_for(lock, duration, [this, id]() { return m_playbackStopped && id == m_lastId; })) {
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
int MockPlayerObserver::getOnPlaybackStartedCallCount() {
|
||
|
return m_onPlaybackStartedCallCount;
|
||
|
}
|
||
|
|
||
|
int MockPlayerObserver::getOnPlaybackFinishedCallCount() {
|
||
|
return m_onPlaybackFinishedCallCount;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
class MediaPlayerTest : public ::testing::Test {
|
||
|
public:
|
||
|
void SetUp() override;
|
||
|
|
||
|
void TearDown() override {
|
||
|
m_mediaPlayer->shutdown();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the audio source to play.
|
||
|
*
|
||
|
*/
|
||
|
void setAttachmentReaderSource(
|
||
|
MediaPlayer::SourceId* id,
|
||
|
int iterations = 1,
|
||
|
std::vector<size_t> receiveSizes = {std::numeric_limits<size_t>::max()});
|
||
|
|
||
|
/**
|
||
|
* Sets IStream source to play.
|
||
|
*
|
||
|
* @param repeat Whether to play the stream over and over until stopped.
|
||
|
*/
|
||
|
void setIStreamSource(MediaPlayer::SourceId* id, bool repeat = false);
|
||
|
|
||
|
/// 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() {
|
||
|
// 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
|
||
|
m_playerObserver = std::make_shared<MockPlayerObserver>();
|
||
|
m_mediaPlayer = MediaPlayer::create(std::make_shared<MockContentFetcherFactory>());
|
||
|
ASSERT_TRUE(m_mediaPlayer);
|
||
|
m_mediaPlayer->setObserver(m_playerObserver);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void MediaPlayerTest::setIStreamSource(MediaPlayer::SourceId* id, bool repeat) {
|
||
|
auto returnId = m_mediaPlayer->setSource(
|
||
|
make_unique<std::ifstream>(inputsDirPath + MP3_FILE_PATH, std::ifstream::binary), repeat);
|
||
|
ASSERT_NE(ERROR_SOURCE_ID, returnId);
|
||
|
if (id) {
|
||
|
*id = returnId;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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) {
|
||
|
MediaPlayer::SourceId sourceId;
|
||
|
setAttachmentReaderSource(&sourceId);
|
||
|
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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);
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
*
|
||
|
* Consecutive calls to setSource(const std::string url) without play() cause tests to occasionally fail: ACSDK-508.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testConsecutiveSetSource) {
|
||
|
std::string url_single(FILE_PREFIX + inputsDirPath + MP3_FILE_PATH);
|
||
|
m_mediaPlayer->setSource("");
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Plays a second different type of source after one source has finished playing.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testStartPlayWaitForEndStartPlayAgain) {
|
||
|
MediaPlayer::SourceId sourceId;
|
||
|
setIStreamSource(&sourceId);
|
||
|
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
||
|
|
||
|
setAttachmentReaderSource(&sourceId);
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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) {
|
||
|
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(5));
|
||
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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, testStartPlayCallAfterStopPlay) {
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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));
|
||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(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));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Pause an audio after playback has started.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testPauseDuringPlay) {
|
||
|
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(1));
|
||
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
||
|
ASSERT_EQ(m_playerObserver->getOnPlaybackFinishedCallCount(), 0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Resume paused audio.
|
||
|
*/
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Stop of a paused audio after playback has started. An additional stop event should
|
||
|
* be sent.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testStopAfterPause) {
|
||
|
MediaPlayer::SourceId sourceId;
|
||
|
setIStreamSource(&sourceId);
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
||
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
||
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Pause of a paused audio after playback has started. The pause() should fail.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testPauseAfterPause) {
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Calling resume after playback has started. The resume operation should fail.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testResumeAfterPlay) {
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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. Then call @c stop and expect the playback finished notification is received.
|
||
|
* Call @c getOffset again. Check the offset value.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testGetOffsetInMilliseconds) {
|
||
|
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));
|
||
|
std::chrono::milliseconds offset = m_mediaPlayer->getOffset(sourceId);
|
||
|
ASSERT_TRUE((offset > std::chrono::milliseconds::zero()) && (offset <= MP3_FILE_LENGTH));
|
||
|
ASSERT_NE(MEDIA_PLAYER_INVALID_OFFSET, offset);
|
||
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
||
|
ASSERT_EQ(MEDIA_PLAYER_INVALID_OFFSET, m_mediaPlayer->getOffset(sourceId));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test getOffset with a null pipeline. Expect that MEDIA_PLAYER_INVALID_OFFSET is returned.
|
||
|
* This currently results in errors on shutdown. Will be fixed by ACSDK-446.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testGetOffsetInMillisecondsNullPipeline) {
|
||
|
MediaPlayer::SourceId sourceId;
|
||
|
setAttachmentReaderSource(&sourceId);
|
||
|
ASSERT_EQ(MEDIA_PLAYER_INVALID_OFFSET, m_mediaPlayer->getOffset(sourceId + 1));
|
||
|
}
|
||
|
|
||
|
/// 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));
|
||
|
|
||
|
std::chrono::milliseconds offset = m_mediaPlayer->getOffset(sourceId);
|
||
|
ASSERT_EQ(MEDIA_PLAYER_INVALID_OFFSET, offset);
|
||
|
}
|
||
|
|
||
|
/// 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));
|
||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||
|
ASSERT_TRUE(m_mediaPlayer->pause(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackPaused(sourceId));
|
||
|
|
||
|
std::chrono::milliseconds offset = m_mediaPlayer->getOffset(sourceId);
|
||
|
ASSERT_GE(offset, std::chrono::milliseconds::zero());
|
||
|
ASSERT_LE(offset, MP3_FILE_LENGTH);
|
||
|
ASSERT_NE(MEDIA_PLAYER_INVALID_OFFSET, offset);
|
||
|
}
|
||
|
|
||
|
// /**
|
||
|
// * 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));
|
||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||
|
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));
|
||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||
|
ASSERT_NE(MEDIA_PLAYER_INVALID_OFFSET, m_mediaPlayer->getOffset(sourceId));
|
||
|
ASSERT_TRUE(m_mediaPlayer->stop(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStopped(sourceId));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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) {
|
||
|
// clang-format off
|
||
|
MediaPlayer::SourceId sourceId;
|
||
|
setAttachmentReaderSource(&sourceId,
|
||
|
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
|
||
|
});
|
||
|
// clang-format on
|
||
|
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId, std::chrono::milliseconds(15000)));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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) {
|
||
|
MediaPlayer::SourceId sourceId;
|
||
|
// clang-format off
|
||
|
setAttachmentReaderSource(&sourceId,
|
||
|
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
|
||
|
});
|
||
|
// clang-format on
|
||
|
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId, std::chrono::milliseconds(20000)));
|
||
|
}
|
||
|
|
||
|
/// Tests playing a dummy playlist
|
||
|
TEST_F(MediaPlayerTest, testStartPlayWithUrlPlaylistWaitForEnd) {
|
||
|
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)));
|
||
|
ASSERT_EQ(m_playerObserver->getOnPlaybackStartedCallCount(), 1);
|
||
|
ASSERT_EQ(m_playerObserver->getOnPlaybackFinishedCallCount(), 1);
|
||
|
}
|
||
|
|
||
|
// TODO: ACSDK-627 This test fails frequently on Ubuntu Linux platforms.
|
||
|
#ifdef RESOLVED_ACSDK_627
|
||
|
/**
|
||
|
* Test setting the offset to a seekable source. Setting the offset should succeed and playback should start from the
|
||
|
* offset.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testSetOffsetSeekableSource) {
|
||
|
std::chrono::milliseconds offset(OFFSET);
|
||
|
|
||
|
std::string url_single(FILE_PREFIX + inputsDirPath + MP3_FILE_PATH);
|
||
|
auto sourceId = m_mediaPlayer->setSource(url_single);
|
||
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
||
|
ASSERT_TRUE(m_mediaPlayer->setOffset(sourceId, offset));
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
||
|
auto start = std::chrono::steady_clock::now();
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
||
|
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));
|
||
|
ASSERT_EQ(m_playerObserver->getOnPlaybackStartedCallCount(), 1);
|
||
|
ASSERT_EQ(m_playerObserver->getOnPlaybackFinishedCallCount(), 1);
|
||
|
|
||
|
std::chrono::milliseconds timeElapsed =
|
||
|
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// TODO: ACSDK-1024 MediaPlayerTest.testSetOffsetOutsideBounds is flaky
|
||
|
/**
|
||
|
* Test setting the offset outside the bounds of the source. Playback will immediately end.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, DISABLED_testSetOffsetOutsideBounds) {
|
||
|
std::chrono::milliseconds outOfBounds(MP3_FILE_LENGTH + PADDING);
|
||
|
|
||
|
std::string url_single(FILE_PREFIX + inputsDirPath + MP3_FILE_PATH);
|
||
|
auto sourceId = m_mediaPlayer->setSource(url_single, outOfBounds);
|
||
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
||
|
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackError(sourceId));
|
||
|
}
|
||
|
|
||
|
// TODO: ACSDK-828: this test ends up with a shorter playback time than the actual file length.
|
||
|
/**
|
||
|
* Test calling setSource resets the offset.
|
||
|
*
|
||
|
* Consecutive calls to setSource(const std::string url) without play() cause tests to occasionally fail: ACSDK-508.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, DISABLED_testSetSourceResetsOffset) {
|
||
|
std::chrono::milliseconds offset(OFFSET);
|
||
|
|
||
|
std::string url_single(FILE_PREFIX + inputsDirPath + MP3_FILE_PATH);
|
||
|
auto sourceId = m_mediaPlayer->setSource(url_single, offset);
|
||
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
||
|
|
||
|
sourceId = m_mediaPlayer->setSource(url_single);
|
||
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
||
|
// Play, expect full file.
|
||
|
auto start = std::chrono::steady_clock::now();
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test consecutive setSource() and play() calls. Expect the PlaybackStarted and PlaybackFinished will be received
|
||
|
* before the timeout.
|
||
|
*/
|
||
|
TEST_F(MediaPlayerTest, testRepeatAttachment) {
|
||
|
MediaPlayer::SourceId sourceId;
|
||
|
for (int i = 0; i < 10; i++) {
|
||
|
setAttachmentReaderSource(&sourceId);
|
||
|
ASSERT_NE(ERROR_SOURCE_ID, sourceId);
|
||
|
ASSERT_TRUE(m_mediaPlayer->play(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackStarted(sourceId));
|
||
|
ASSERT_TRUE(m_playerObserver->waitForPlaybackFinished(sourceId));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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));
|
||
|
}
|
||
|
|
||
|
/// 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));
|
||
|
}
|
||
|
|
||
|
} // namespace test
|
||
|
} // namespace mediaPlayer
|
||
|
} // namespace alexaClientSDK
|
||
|
|
||
|
int main(int argc, char** argv) {
|
||
|
::testing::InitGoogleTest(&argc, argv);
|
||
|
|
||
|
if (argc < 2) {
|
||
|
std::cerr << "Usage: " << std::string(argv[0]) << " <absolute path to test inputs folder>" << std::endl;
|
||
|
} else {
|
||
|
alexaClientSDK::mediaPlayer::test::inputsDirPath = std::string(argv[1]);
|
||
|
alexaClientSDK::mediaPlayer::test::urlsToContentTypes.insert(
|
||
|
{alexaClientSDK::mediaPlayer::test::FILE_PREFIX + alexaClientSDK::mediaPlayer::test::inputsDirPath +
|
||
|
alexaClientSDK::mediaPlayer::test::MP3_FILE_PATH,
|
||
|
"audio/mpeg"});
|
||
|
std::ifstream fileStream(
|
||
|
alexaClientSDK::mediaPlayer::test::inputsDirPath + alexaClientSDK::mediaPlayer::test::MP3_FILE_PATH,
|
||
|
std::ifstream::binary);
|
||
|
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()});
|
||
|
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});
|
||
|
|
||
|
return RUN_ALL_TESTS();
|
||
|
}
|
||
|
}
|