| // 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 |