blob: 7914425177faf67682e62b1653a15fbdff9ff79c [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.
#include "garnet/bin/mediaplayer/test/command_queue.h"
#include <fcntl.h>
#include <lib/async/cpp/task.h>
#include <iostream>
#include "garnet/bin/mediaplayer/graph/formatting.h"
#include "lib/fidl/cpp/optional.h"
#include "lib/fsl/io/fd.h"
#include "lib/fxl/logging.h"
#include "lib/media/timeline/type_converters.h"
#include "lib/url/gurl.h"
namespace media_player {
namespace test {
CommandQueue::CommandQueue() : dispatcher_(async_get_default_dispatcher()) {
wait_for_position_task_.set_handler([this]() {
if (wait_for_position_ != fuchsia::media::NO_TIMESTAMP) {
wait_for_position_ = fuchsia::media::NO_TIMESTAMP;
if (verbose_) {
std::cerr << "WaitForPosition done\n";
}
ExecuteNextCommand();
}
});
}
CommandQueue::~CommandQueue() {}
void CommandQueue::NotifyStatusChanged(
const fuchsia::mediaplayer::PlayerStatus& status) {
status_ = fidl::MakeOptional(fidl::Clone(status));
MaybeFinishWaitingForStatusCondition();
if (status.timeline_function) {
timeline_function_ =
fxl::To<media::TimelineFunction>(*status.timeline_function);
MaybeScheduleWaitForPositionTask();
MaybeFinishWaitingForSeekCompletion();
}
}
void CommandQueue::NotifyViewReady() {
view_ready_ = true;
MaybeFinishWaitingForViewReady();
}
void CommandQueue::MaybeFinishWaitingForViewReady() {
if (view_ready_ && wait_for_view_ready_) {
wait_for_view_ready_ = false;
if (verbose_) {
std::cerr << "WaitForViewReady done\n";
}
ExecuteNextCommand();
}
}
void CommandQueue::MaybeFinishWaitingForStatusCondition() {
if (status_ && wait_for_status_condition_ &&
wait_for_status_condition_(*status_)) {
// We have status from the player, are waiting for a condition relating to
// status to become true and have detected that, indeed, that condition has
// become true. Clear the condition and continue command execution.
wait_for_status_condition_ = nullptr;
ExecuteNextCommand();
}
}
void CommandQueue::MaybeScheduleWaitForPositionTask() {
if (wait_for_position_ != fuchsia::media::NO_TIMESTAMP) {
wait_for_position_task_.Cancel();
if (timeline_function_.invertable()) {
// Apply the timeline function in reverse to find the CLOCK_MONOTONIC
// time at which we should resume executing commands.
int64_t wait_for_time =
timeline_function_.ApplyInverse(wait_for_position_);
wait_for_position_task_.PostForTime(dispatcher_, zx::time(wait_for_time));
}
}
}
void CommandQueue::MaybeFinishWaitingForSeekCompletion() {
if (wait_for_seek_completion_position_ != fuchsia::media::NO_TIMESTAMP &&
timeline_function_.subject_time() == wait_for_seek_completion_position_) {
wait_for_seek_completion_position_ = fuchsia::media::NO_TIMESTAMP;
if (verbose_) {
std::cerr << "WaitForSeekCompletion done\n";
}
ExecuteNextCommand();
}
}
void CommandQueue::ExecuteNextCommand() {
if (command_queue_.empty()) {
return;
}
async::PostTask(dispatcher_, [this]() {
if (command_queue_.empty()) {
return;
}
auto command = std::move(command_queue_.front());
command_queue_.pop();
command->Execute(this);
});
}
void CommandQueue::SetUrlCommand::Execute(CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "SetUrl " << url_ << "\n";
}
url::GURL url = url::GURL(url_);
if (url.SchemeIsFile()) {
auto fd = fxl::UniqueFD(open(url.path().c_str(), O_RDONLY));
FXL_CHECK(fd.is_valid());
command_queue->player_->SetFileSource(
fsl::CloneChannelFromFileDescriptor(fd.get()));
} else {
command_queue->player_->SetHttpSource(url_, nullptr);
}
command_queue->prev_seek_position_ = 0;
command_queue->status_ = nullptr;
command_queue->ExecuteNextCommand();
}
void CommandQueue::SetFileCommand::Execute(CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "SetFile\n";
}
auto fd = fxl::UniqueFD(open(path_.c_str(), O_RDONLY));
FXL_CHECK(fd.is_valid());
command_queue->player_->SetFileSource(
fsl::CloneChannelFromFileDescriptor(fd.get()));
command_queue->prev_seek_position_ = 0;
command_queue->status_ = nullptr;
command_queue->ExecuteNextCommand();
}
void CommandQueue::PlayCommand::Execute(CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "Play\n";
}
command_queue->player_->Play();
command_queue->ExecuteNextCommand();
}
void CommandQueue::PauseCommand::Execute(CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "Pause\n";
}
command_queue->player_->Pause();
command_queue->ExecuteNextCommand();
}
void CommandQueue::SeekCommand::Execute(CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "Seek " << AsNs(position_.get()) << "\n";
}
command_queue->player_->Seek(position_.get());
command_queue->prev_seek_position_ = position_.get();
command_queue->status_ = nullptr;
command_queue->ExecuteNextCommand();
}
void CommandQueue::InvokeCommand::Execute(CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "Invoke\n";
}
FXL_DCHECK(action_);
action_();
command_queue->ExecuteNextCommand();
}
void CommandQueue::WaitForStatusConditionCommand::Execute(
CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "WaitForStatusConditionCommand\n";
}
command_queue->wait_for_status_condition_ = std::move(condition_);
// |ExecuteNextCommand| will be called when |wait_for_status_condition_|
// returns true.
command_queue->MaybeFinishWaitingForStatusCondition();
}
void CommandQueue::WaitForViewReadyCommand::Execute(
CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "WaitForViewReady\n";
}
command_queue->wait_for_view_ready_ = true;
// |ExecuteNextCommand| will be called when the view is ready.
command_queue->MaybeFinishWaitingForViewReady();
}
void CommandQueue::WaitForPositionCommand::Execute(
CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "WaitForPosition " << AsNs(position_.get()) << "\n";
}
command_queue->wait_for_position_ = position_.get();
// |ExecuteNextCommand| will be called when the position has been reached.
command_queue->MaybeScheduleWaitForPositionTask();
}
void CommandQueue::WaitForSeekCompletionCommand::Execute(
CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "WaitForSeekCompletion\n";
}
command_queue->wait_for_seek_completion_position_ =
command_queue->prev_seek_position_;
// |ExecuteNextCommand| will be called when the seek has completed.
command_queue->MaybeFinishWaitingForSeekCompletion();
}
void CommandQueue::SleepCommand::Execute(CommandQueue* command_queue) {
if (command_queue->verbose_) {
std::cerr << "Sleep " << AsNs(duration_.get()) << "\n";
}
async::PostDelayedTask(
command_queue->dispatcher_,
[command_queue]() { command_queue->ExecuteNextCommand(); },
zx::duration(duration_));
}
} // namespace test
} // namespace media_player