blob: bdf32d5777ac903e67865c4fda2ba9e9e02421af [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GARNET_BIN_MEDIAPLAYER_TEST_COMMAND_QUEUE_H_
#define GARNET_BIN_MEDIAPLAYER_TEST_COMMAND_QUEUE_H_
#include <fuchsia/mediaplayer/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/task.h>
#include <lib/fit/function.h>
#include <queue>
#include "lib/fxl/logging.h"
#include "lib/media/timeline/timeline_function.h"
namespace media_player {
namespace test {
class CommandQueue {
public:
using StatusCondition =
fit::function<bool(const fuchsia::mediaplayer::PlayerStatus&)>;
CommandQueue();
~CommandQueue();
void Init(fuchsia::mediaplayer::Player* player) { player_ = player; }
void SetVerbose(bool verbose) { verbose_ = verbose; }
// Executes the commands in the queue.
void Execute() { ExecuteNextCommand(); }
// Clears the command queue.
void Clear() {
wait_for_position_ = fuchsia::media::NO_TIMESTAMP;
wait_for_status_condition_ = nullptr;
status_ = nullptr;
while (!command_queue_.empty()) {
command_queue_.pop();
}
}
// Notifies the command queue that player status has changed.
void NotifyStatusChanged(const fuchsia::mediaplayer::PlayerStatus& status);
// Notifies the command queue that the view is ready.
void NotifyViewReady();
// Queues a |SetFileSource| or |SetHttpSource| command.
void SetUrl(const std::string& url) { AddCommand(new SetUrlCommand(url)); }
// Queues a |SetFileSource| command.
void SetFile(const std::string& path) {
AddCommand(new SetFileCommand(path));
}
// Queues a play command.
void Play() { AddCommand(new PlayCommand()); }
// Queues a pause command.
void Pause() { AddCommand(new PauseCommand()); }
// Queues a seek command.
void Seek(zx::duration position) { AddCommand(new SeekCommand(position)); }
void Seek(int64_t position) { Seek(zx::duration(position)); }
// Queues a command that invokes |action|.
void Invoke(fit::closure action) {
AddCommand(new InvokeCommand(std::move(action)));
}
void WaitForStatusCondition(StatusCondition condition) {
AddCommand(new WaitForStatusConditionCommand(std::move(condition)));
}
// Queues a command that waits until content is loaded.
void WaitForContentLoaded() {
WaitForStatusCondition(
[](const fuchsia::mediaplayer::PlayerStatus& status) {
return status.duration_ns != 0;
});
}
// Queues a command that waits until audio is connected.
void WaitForAudioConnected() {
WaitForStatusCondition(
[](const fuchsia::mediaplayer::PlayerStatus& status) {
return status.audio_connected;
});
}
// Queues a command that waits until video is connected.
void WaitForVideoConnected() {
WaitForStatusCondition(
[](const fuchsia::mediaplayer::PlayerStatus& status) {
return status.video_connected;
});
}
// Queues a command that waits until a problem is indicated.
void WaitForProblem() {
WaitForStatusCondition(
[](const fuchsia::mediaplayer::PlayerStatus& status) {
return status.problem != nullptr;
});
}
// Queues a command that waits util the view is ready.
void WaitForViewReady() { AddCommand(new WaitForViewReadyCommand()); }
// Queues a command that waits until the specified position is reached.
void WaitForPosition(zx::duration position) {
AddCommand(new WaitForPositionCommand(position));
}
void WaitForPosition(int64_t position) {
WaitForPosition(zx::duration(position));
}
// Queues a command that waits a previous seek completes.
void WaitForSeekCompletion() {
AddCommand(new WaitForSeekCompletionCommand());
}
// Queues a command that waits until end of stream is reached.
void WaitForEndOfStream() {
WaitForStatusCondition(
[](const fuchsia::mediaplayer::PlayerStatus& status) {
return status.end_of_stream;
});
}
// Queues a command that sleeps for the specified duration.
void Sleep(zx::duration duration) { AddCommand(new SleepCommand(duration)); }
void Sleep(int64_t position) { Sleep(zx::duration(position)); }
private:
struct Command {
Command() = default;
virtual ~Command() = default;
virtual void Execute(CommandQueue* command_queue) = 0;
};
struct SetUrlCommand : public Command {
SetUrlCommand(const std::string& url) : url_(url) {}
void Execute(CommandQueue* command_queue) override;
std::string url_;
};
struct SetFileCommand : public Command {
SetFileCommand(const std::string& path) : path_(path) {}
void Execute(CommandQueue* command_queue) override;
std::string path_;
};
struct PlayCommand : public Command {
void Execute(CommandQueue* command_queue) override;
};
struct PauseCommand : public Command {
void Execute(CommandQueue* command_queue) override;
};
struct SeekCommand : public Command {
SeekCommand(zx::duration position) : position_(position) {}
void Execute(CommandQueue* command_queue) override;
zx::duration position_;
};
struct InvokeCommand : public Command {
InvokeCommand(fit::closure action) : action_(std::move(action)) {
FXL_DCHECK(action_);
}
void Execute(CommandQueue* command_queue) override;
fit::closure action_;
};
struct WaitForStatusConditionCommand : public Command {
WaitForStatusConditionCommand(StatusCondition condition)
: condition_(std::move(condition)) {
FXL_DCHECK(condition_);
}
void Execute(CommandQueue* command_queue) override;
StatusCondition condition_;
};
struct WaitForViewReadyCommand : public Command {
void Execute(CommandQueue* command_queue) override;
};
struct WaitForPositionCommand : public Command {
WaitForPositionCommand(zx::duration position) : position_(position) {}
void Execute(CommandQueue* command_queue) override;
zx::duration position_;
};
struct WaitForSeekCompletionCommand : public Command {
void Execute(CommandQueue* command_queue) override;
};
struct SleepCommand : public Command {
SleepCommand(zx::duration duration) : duration_(duration) {}
void Execute(CommandQueue* command_queue) override;
zx::duration duration_;
};
// Finishes waiting for |wait_for_status_condition_| to succeed.
void MaybeFinishWaitingForStatusCondition();
// Finishes waiting for view ready if we're waiting for view ready and
// if the view is ready.
void MaybeFinishWaitingForViewReady();
// Schedules a task to handle wait-for-position, as appropriate.
void MaybeScheduleWaitForPositionTask();
// Finishes waiting for seek completion if we're waiting for seek completion
// and if the previous seek has completed.
void MaybeFinishWaitingForSeekCompletion();
// Finishes waiting for end of stream if we're waiting for end of stream and
// if we're at end of stream.
void MaybeFinishWaitingForEndOfStream();
// Adds a command to the command queue.
void AddCommand(Command* command) { command_queue_.emplace(command); }
// Executes the next command in the queue, if any.
void ExecuteNextCommand();
async_dispatcher_t* dispatcher_;
fuchsia::mediaplayer::Player* player_;
std::queue<std::unique_ptr<Command>> command_queue_;
media::TimelineFunction timeline_function_;
std::unique_ptr<fuchsia::mediaplayer::PlayerStatus> status_;
// This condition is polled in |NotifyStatusChanged| to determine if command
// execution should be continued.
StatusCondition wait_for_status_condition_;
bool view_ready_ = false;
bool wait_for_view_ready_ = false;
int64_t prev_seek_position_ = fuchsia::media::NO_TIMESTAMP;
int64_t wait_for_seek_completion_position_ = fuchsia::media::NO_TIMESTAMP;
int64_t wait_for_position_ = fuchsia::media::NO_TIMESTAMP;
async::TaskClosure wait_for_position_task_;
bool verbose_ = false;
};
} // namespace test
} // namespace media_player
#endif // GARNET_BIN_MEDIAPLAYER_TEST_COMMAND_QUEUE_H_